1*3b72422dSYevgeny Kliteynik // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2*3b72422dSYevgeny Kliteynik /* Copyright (c) 2004 Topspin Communications. All rights reserved.
3*3b72422dSYevgeny Kliteynik  * Copyright (c) 2005 - 2008 Mellanox Technologies. All rights reserved.
4*3b72422dSYevgeny Kliteynik  * Copyright (c) 2006 - 2007 Cisco Systems, Inc. All rights reserved.
5*3b72422dSYevgeny Kliteynik  * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
6*3b72422dSYevgeny Kliteynik  */
7*3b72422dSYevgeny Kliteynik 
8*3b72422dSYevgeny Kliteynik #include "dr_types.h"
9*3b72422dSYevgeny Kliteynik 
mlx5dr_buddy_init(struct mlx5dr_icm_buddy_mem * buddy,unsigned int max_order)10*3b72422dSYevgeny Kliteynik int mlx5dr_buddy_init(struct mlx5dr_icm_buddy_mem *buddy,
11*3b72422dSYevgeny Kliteynik 		      unsigned int max_order)
12*3b72422dSYevgeny Kliteynik {
13*3b72422dSYevgeny Kliteynik 	int i;
14*3b72422dSYevgeny Kliteynik 
15*3b72422dSYevgeny Kliteynik 	buddy->max_order = max_order;
16*3b72422dSYevgeny Kliteynik 
17*3b72422dSYevgeny Kliteynik 	INIT_LIST_HEAD(&buddy->list_node);
18*3b72422dSYevgeny Kliteynik 
19*3b72422dSYevgeny Kliteynik 	buddy->bitmap = kcalloc(buddy->max_order + 1,
20*3b72422dSYevgeny Kliteynik 				sizeof(*buddy->bitmap),
21*3b72422dSYevgeny Kliteynik 				GFP_KERNEL);
22*3b72422dSYevgeny Kliteynik 	buddy->num_free = kcalloc(buddy->max_order + 1,
23*3b72422dSYevgeny Kliteynik 				  sizeof(*buddy->num_free),
24*3b72422dSYevgeny Kliteynik 				  GFP_KERNEL);
25*3b72422dSYevgeny Kliteynik 
26*3b72422dSYevgeny Kliteynik 	if (!buddy->bitmap || !buddy->num_free)
27*3b72422dSYevgeny Kliteynik 		goto err_free_all;
28*3b72422dSYevgeny Kliteynik 
29*3b72422dSYevgeny Kliteynik 	/* Allocating max_order bitmaps, one for each order */
30*3b72422dSYevgeny Kliteynik 
31*3b72422dSYevgeny Kliteynik 	for (i = 0; i <= buddy->max_order; ++i) {
32*3b72422dSYevgeny Kliteynik 		unsigned int size = 1 << (buddy->max_order - i);
33*3b72422dSYevgeny Kliteynik 
34*3b72422dSYevgeny Kliteynik 		buddy->bitmap[i] = bitmap_zalloc(size, GFP_KERNEL);
35*3b72422dSYevgeny Kliteynik 		if (!buddy->bitmap[i])
36*3b72422dSYevgeny Kliteynik 			goto err_out_free_each_bit_per_order;
37*3b72422dSYevgeny Kliteynik 	}
38*3b72422dSYevgeny Kliteynik 
39*3b72422dSYevgeny Kliteynik 	/* In the beginning, we have only one order that is available for
40*3b72422dSYevgeny Kliteynik 	 * use (the biggest one), so mark the first bit in both bitmaps.
41*3b72422dSYevgeny Kliteynik 	 */
42*3b72422dSYevgeny Kliteynik 
43*3b72422dSYevgeny Kliteynik 	bitmap_set(buddy->bitmap[buddy->max_order], 0, 1);
44*3b72422dSYevgeny Kliteynik 
45*3b72422dSYevgeny Kliteynik 	buddy->num_free[buddy->max_order] = 1;
46*3b72422dSYevgeny Kliteynik 
47*3b72422dSYevgeny Kliteynik 	return 0;
48*3b72422dSYevgeny Kliteynik 
49*3b72422dSYevgeny Kliteynik err_out_free_each_bit_per_order:
50*3b72422dSYevgeny Kliteynik 	for (i = 0; i <= buddy->max_order; ++i)
51*3b72422dSYevgeny Kliteynik 		bitmap_free(buddy->bitmap[i]);
52*3b72422dSYevgeny Kliteynik 
53*3b72422dSYevgeny Kliteynik err_free_all:
54*3b72422dSYevgeny Kliteynik 	kfree(buddy->num_free);
55*3b72422dSYevgeny Kliteynik 	kfree(buddy->bitmap);
56*3b72422dSYevgeny Kliteynik 	return -ENOMEM;
57*3b72422dSYevgeny Kliteynik }
58*3b72422dSYevgeny Kliteynik 
mlx5dr_buddy_cleanup(struct mlx5dr_icm_buddy_mem * buddy)59*3b72422dSYevgeny Kliteynik void mlx5dr_buddy_cleanup(struct mlx5dr_icm_buddy_mem *buddy)
60*3b72422dSYevgeny Kliteynik {
61*3b72422dSYevgeny Kliteynik 	int i;
62*3b72422dSYevgeny Kliteynik 
63*3b72422dSYevgeny Kliteynik 	list_del(&buddy->list_node);
64*3b72422dSYevgeny Kliteynik 
65*3b72422dSYevgeny Kliteynik 	for (i = 0; i <= buddy->max_order; ++i)
66*3b72422dSYevgeny Kliteynik 		bitmap_free(buddy->bitmap[i]);
67*3b72422dSYevgeny Kliteynik 
68*3b72422dSYevgeny Kliteynik 	kfree(buddy->num_free);
69*3b72422dSYevgeny Kliteynik 	kfree(buddy->bitmap);
70*3b72422dSYevgeny Kliteynik }
71*3b72422dSYevgeny Kliteynik 
dr_buddy_find_free_seg(struct mlx5dr_icm_buddy_mem * buddy,unsigned int start_order,unsigned int * segment,unsigned int * order)72*3b72422dSYevgeny Kliteynik static int dr_buddy_find_free_seg(struct mlx5dr_icm_buddy_mem *buddy,
73*3b72422dSYevgeny Kliteynik 				  unsigned int start_order,
74*3b72422dSYevgeny Kliteynik 				  unsigned int *segment,
75*3b72422dSYevgeny Kliteynik 				  unsigned int *order)
76*3b72422dSYevgeny Kliteynik {
77*3b72422dSYevgeny Kliteynik 	unsigned int seg, order_iter, m;
78*3b72422dSYevgeny Kliteynik 
79*3b72422dSYevgeny Kliteynik 	for (order_iter = start_order;
80*3b72422dSYevgeny Kliteynik 	     order_iter <= buddy->max_order; ++order_iter) {
81*3b72422dSYevgeny Kliteynik 		if (!buddy->num_free[order_iter])
82*3b72422dSYevgeny Kliteynik 			continue;
83*3b72422dSYevgeny Kliteynik 
84*3b72422dSYevgeny Kliteynik 		m = 1 << (buddy->max_order - order_iter);
85*3b72422dSYevgeny Kliteynik 		seg = find_first_bit(buddy->bitmap[order_iter], m);
86*3b72422dSYevgeny Kliteynik 
87*3b72422dSYevgeny Kliteynik 		if (WARN(seg >= m,
88*3b72422dSYevgeny Kliteynik 			 "ICM Buddy: failed finding free mem for order %d\n",
89*3b72422dSYevgeny Kliteynik 			 order_iter))
90*3b72422dSYevgeny Kliteynik 			return -ENOMEM;
91*3b72422dSYevgeny Kliteynik 
92*3b72422dSYevgeny Kliteynik 		break;
93*3b72422dSYevgeny Kliteynik 	}
94*3b72422dSYevgeny Kliteynik 
95*3b72422dSYevgeny Kliteynik 	if (order_iter > buddy->max_order)
96*3b72422dSYevgeny Kliteynik 		return -ENOMEM;
97*3b72422dSYevgeny Kliteynik 
98*3b72422dSYevgeny Kliteynik 	*segment = seg;
99*3b72422dSYevgeny Kliteynik 	*order = order_iter;
100*3b72422dSYevgeny Kliteynik 	return 0;
101*3b72422dSYevgeny Kliteynik }
102*3b72422dSYevgeny Kliteynik 
103*3b72422dSYevgeny Kliteynik /**
104*3b72422dSYevgeny Kliteynik  * mlx5dr_buddy_alloc_mem() - Update second level bitmap.
105*3b72422dSYevgeny Kliteynik  * @buddy: Buddy to update.
106*3b72422dSYevgeny Kliteynik  * @order: Order of the buddy to update.
107*3b72422dSYevgeny Kliteynik  * @segment: Segment number.
108*3b72422dSYevgeny Kliteynik  *
109*3b72422dSYevgeny Kliteynik  * This function finds the first area of the ICM memory managed by this buddy.
110*3b72422dSYevgeny Kliteynik  * It uses the data structures of the buddy system in order to find the first
111*3b72422dSYevgeny Kliteynik  * area of free place, starting from the current order till the maximum order
112*3b72422dSYevgeny Kliteynik  * in the system.
113*3b72422dSYevgeny Kliteynik  *
114*3b72422dSYevgeny Kliteynik  * Return: 0 when segment is set, non-zero error status otherwise.
115*3b72422dSYevgeny Kliteynik  *
116*3b72422dSYevgeny Kliteynik  * The function returns the location (segment) in the whole buddy ICM memory
117*3b72422dSYevgeny Kliteynik  * area - the index of the memory segment that is available for use.
118*3b72422dSYevgeny Kliteynik  */
mlx5dr_buddy_alloc_mem(struct mlx5dr_icm_buddy_mem * buddy,unsigned int order,unsigned int * segment)119*3b72422dSYevgeny Kliteynik int mlx5dr_buddy_alloc_mem(struct mlx5dr_icm_buddy_mem *buddy,
120*3b72422dSYevgeny Kliteynik 			   unsigned int order,
121*3b72422dSYevgeny Kliteynik 			   unsigned int *segment)
122*3b72422dSYevgeny Kliteynik {
123*3b72422dSYevgeny Kliteynik 	unsigned int seg, order_iter;
124*3b72422dSYevgeny Kliteynik 	int err;
125*3b72422dSYevgeny Kliteynik 
126*3b72422dSYevgeny Kliteynik 	err = dr_buddy_find_free_seg(buddy, order, &seg, &order_iter);
127*3b72422dSYevgeny Kliteynik 	if (err)
128*3b72422dSYevgeny Kliteynik 		return err;
129*3b72422dSYevgeny Kliteynik 
130*3b72422dSYevgeny Kliteynik 	bitmap_clear(buddy->bitmap[order_iter], seg, 1);
131*3b72422dSYevgeny Kliteynik 	--buddy->num_free[order_iter];
132*3b72422dSYevgeny Kliteynik 
133*3b72422dSYevgeny Kliteynik 	/* If we found free memory in some order that is bigger than the
134*3b72422dSYevgeny Kliteynik 	 * required order, we need to split every order between the required
135*3b72422dSYevgeny Kliteynik 	 * order and the order that we found into two parts, and mark accordingly.
136*3b72422dSYevgeny Kliteynik 	 */
137*3b72422dSYevgeny Kliteynik 	while (order_iter > order) {
138*3b72422dSYevgeny Kliteynik 		--order_iter;
139*3b72422dSYevgeny Kliteynik 		seg <<= 1;
140*3b72422dSYevgeny Kliteynik 		bitmap_set(buddy->bitmap[order_iter], seg ^ 1, 1);
141*3b72422dSYevgeny Kliteynik 		++buddy->num_free[order_iter];
142*3b72422dSYevgeny Kliteynik 	}
143*3b72422dSYevgeny Kliteynik 
144*3b72422dSYevgeny Kliteynik 	seg <<= order;
145*3b72422dSYevgeny Kliteynik 	*segment = seg;
146*3b72422dSYevgeny Kliteynik 
147*3b72422dSYevgeny Kliteynik 	return 0;
148*3b72422dSYevgeny Kliteynik }
149*3b72422dSYevgeny Kliteynik 
mlx5dr_buddy_free_mem(struct mlx5dr_icm_buddy_mem * buddy,unsigned int seg,unsigned int order)150*3b72422dSYevgeny Kliteynik void mlx5dr_buddy_free_mem(struct mlx5dr_icm_buddy_mem *buddy,
151*3b72422dSYevgeny Kliteynik 			   unsigned int seg, unsigned int order)
152*3b72422dSYevgeny Kliteynik {
153*3b72422dSYevgeny Kliteynik 	seg >>= order;
154*3b72422dSYevgeny Kliteynik 
155*3b72422dSYevgeny Kliteynik 	/* Whenever a segment is free,
156*3b72422dSYevgeny Kliteynik 	 * the mem is added to the buddy that gave it.
157*3b72422dSYevgeny Kliteynik 	 */
158*3b72422dSYevgeny Kliteynik 	while (test_bit(seg ^ 1, buddy->bitmap[order])) {
159*3b72422dSYevgeny Kliteynik 		bitmap_clear(buddy->bitmap[order], seg ^ 1, 1);
160*3b72422dSYevgeny Kliteynik 		--buddy->num_free[order];
161*3b72422dSYevgeny Kliteynik 		seg >>= 1;
162*3b72422dSYevgeny Kliteynik 		++order;
163*3b72422dSYevgeny Kliteynik 	}
164*3b72422dSYevgeny Kliteynik 	bitmap_set(buddy->bitmap[order], seg, 1);
165*3b72422dSYevgeny Kliteynik 
166*3b72422dSYevgeny Kliteynik 	++buddy->num_free[order];
167*3b72422dSYevgeny Kliteynik }
168*3b72422dSYevgeny Kliteynik 
169