1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
3
4 #include "mlx5hws_internal.h"
5 #include "mlx5hws_buddy.h"
6
hws_buddy_init(struct mlx5hws_buddy_mem * buddy,u32 max_order)7 static int hws_buddy_init(struct mlx5hws_buddy_mem *buddy, u32 max_order)
8 {
9 int i, s, ret = 0;
10
11 buddy->max_order = max_order;
12
13 buddy->bitmap = kcalloc(buddy->max_order + 1,
14 sizeof(*buddy->bitmap),
15 GFP_KERNEL);
16 if (!buddy->bitmap)
17 return -ENOMEM;
18
19 buddy->num_free = kcalloc(buddy->max_order + 1,
20 sizeof(*buddy->num_free),
21 GFP_KERNEL);
22 if (!buddy->num_free) {
23 ret = -ENOMEM;
24 goto err_out_free_bits;
25 }
26
27 for (i = 0; i <= (int)buddy->max_order; ++i) {
28 s = 1 << (buddy->max_order - i);
29
30 buddy->bitmap[i] = bitmap_zalloc(s, GFP_KERNEL);
31 if (!buddy->bitmap[i]) {
32 ret = -ENOMEM;
33 goto err_out_free_num_free;
34 }
35 }
36
37 bitmap_set(buddy->bitmap[buddy->max_order], 0, 1);
38 buddy->num_free[buddy->max_order] = 1;
39
40 return 0;
41
42 err_out_free_num_free:
43 for (i = 0; i <= (int)buddy->max_order; ++i)
44 bitmap_free(buddy->bitmap[i]);
45
46 kfree(buddy->num_free);
47
48 err_out_free_bits:
49 kfree(buddy->bitmap);
50 return ret;
51 }
52
mlx5hws_buddy_create(u32 max_order)53 struct mlx5hws_buddy_mem *mlx5hws_buddy_create(u32 max_order)
54 {
55 struct mlx5hws_buddy_mem *buddy;
56
57 buddy = kzalloc(sizeof(*buddy), GFP_KERNEL);
58 if (!buddy)
59 return NULL;
60
61 if (hws_buddy_init(buddy, max_order))
62 goto free_buddy;
63
64 return buddy;
65
66 free_buddy:
67 kfree(buddy);
68 return NULL;
69 }
70
mlx5hws_buddy_cleanup(struct mlx5hws_buddy_mem * buddy)71 void mlx5hws_buddy_cleanup(struct mlx5hws_buddy_mem *buddy)
72 {
73 int i;
74
75 for (i = 0; i <= (int)buddy->max_order; ++i)
76 bitmap_free(buddy->bitmap[i]);
77
78 kfree(buddy->num_free);
79 kfree(buddy->bitmap);
80 }
81
hws_buddy_find_free_seg(struct mlx5hws_buddy_mem * buddy,u32 start_order,u32 * segment,u32 * order)82 static int hws_buddy_find_free_seg(struct mlx5hws_buddy_mem *buddy,
83 u32 start_order,
84 u32 *segment,
85 u32 *order)
86 {
87 unsigned int seg, order_iter, m;
88
89 for (order_iter = start_order;
90 order_iter <= buddy->max_order; ++order_iter) {
91 if (!buddy->num_free[order_iter])
92 continue;
93
94 m = 1 << (buddy->max_order - order_iter);
95 seg = find_first_bit(buddy->bitmap[order_iter], m);
96
97 if (WARN(seg >= m,
98 "ICM Buddy: failed finding free mem for order %d\n",
99 order_iter))
100 return -ENOMEM;
101
102 break;
103 }
104
105 if (order_iter > buddy->max_order)
106 return -ENOMEM;
107
108 *segment = seg;
109 *order = order_iter;
110 return 0;
111 }
112
mlx5hws_buddy_alloc_mem(struct mlx5hws_buddy_mem * buddy,u32 order)113 int mlx5hws_buddy_alloc_mem(struct mlx5hws_buddy_mem *buddy, u32 order)
114 {
115 u32 seg, order_iter, err;
116
117 err = hws_buddy_find_free_seg(buddy, order, &seg, &order_iter);
118 if (err)
119 return err;
120
121 bitmap_clear(buddy->bitmap[order_iter], seg, 1);
122 --buddy->num_free[order_iter];
123
124 while (order_iter > order) {
125 --order_iter;
126 seg <<= 1;
127 bitmap_set(buddy->bitmap[order_iter], seg ^ 1, 1);
128 ++buddy->num_free[order_iter];
129 }
130
131 seg <<= order;
132
133 return seg;
134 }
135
mlx5hws_buddy_free_mem(struct mlx5hws_buddy_mem * buddy,u32 seg,u32 order)136 void mlx5hws_buddy_free_mem(struct mlx5hws_buddy_mem *buddy, u32 seg, u32 order)
137 {
138 seg >>= order;
139
140 while (test_bit(seg ^ 1, buddy->bitmap[order])) {
141 bitmap_clear(buddy->bitmap[order], seg ^ 1, 1);
142 --buddy->num_free[order];
143 seg >>= 1;
144 ++order;
145 }
146
147 bitmap_set(buddy->bitmap[order], seg, 1);
148 ++buddy->num_free[order];
149 }
150