]>
Commit | Line | Data |
---|---|---|
0a020d41 JP |
1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ | |
3 | ||
4 | #include <linux/module.h> | |
5 | #include <linux/slab.h> | |
6 | #include <linux/rhashtable.h> | |
9069a381 | 7 | #include <linux/idr.h> |
0a020d41 JP |
8 | #include <linux/list.h> |
9 | #include <linux/sort.h> | |
10 | #include <linux/objagg.h> | |
11 | ||
12 | #define CREATE_TRACE_POINTS | |
13 | #include <trace/events/objagg.h> | |
14 | ||
9069a381 JP |
15 | struct objagg_hints { |
16 | struct rhashtable node_ht; | |
17 | struct rhashtable_params ht_params; | |
18 | struct list_head node_list; | |
19 | unsigned int node_count; | |
20 | unsigned int root_count; | |
21 | unsigned int refcount; | |
22 | const struct objagg_ops *ops; | |
23 | }; | |
24 | ||
25 | struct objagg_hints_node { | |
26 | struct rhash_head ht_node; /* member of objagg_hints->node_ht */ | |
27 | struct list_head list; /* member of objagg_hints->node_list */ | |
28 | struct objagg_hints_node *parent; | |
29 | unsigned int root_id; | |
30 | struct objagg_obj_stats_info stats_info; | |
31 | unsigned long obj[0]; | |
32 | }; | |
33 | ||
34 | static struct objagg_hints_node * | |
35 | objagg_hints_lookup(struct objagg_hints *objagg_hints, void *obj) | |
36 | { | |
37 | if (!objagg_hints) | |
38 | return NULL; | |
39 | return rhashtable_lookup_fast(&objagg_hints->node_ht, obj, | |
40 | objagg_hints->ht_params); | |
41 | } | |
42 | ||
0a020d41 JP |
43 | struct objagg { |
44 | const struct objagg_ops *ops; | |
45 | void *priv; | |
46 | struct rhashtable obj_ht; | |
47 | struct rhashtable_params ht_params; | |
48 | struct list_head obj_list; | |
49 | unsigned int obj_count; | |
9069a381 JP |
50 | struct ida root_ida; |
51 | struct objagg_hints *hints; | |
0a020d41 JP |
52 | }; |
53 | ||
54 | struct objagg_obj { | |
55 | struct rhash_head ht_node; /* member of objagg->obj_ht */ | |
56 | struct list_head list; /* member of objagg->obj_list */ | |
57 | struct objagg_obj *parent; /* if the object is nested, this | |
58 | * holds pointer to parent, otherwise NULL | |
59 | */ | |
60 | union { | |
61 | void *delta_priv; /* user delta private */ | |
62 | void *root_priv; /* user root private */ | |
63 | }; | |
9069a381 | 64 | unsigned int root_id; |
0a020d41 JP |
65 | unsigned int refcount; /* counts number of users of this object |
66 | * including nested objects | |
67 | */ | |
68 | struct objagg_obj_stats stats; | |
69 | unsigned long obj[0]; | |
70 | }; | |
71 | ||
72 | static unsigned int objagg_obj_ref_inc(struct objagg_obj *objagg_obj) | |
73 | { | |
74 | return ++objagg_obj->refcount; | |
75 | } | |
76 | ||
77 | static unsigned int objagg_obj_ref_dec(struct objagg_obj *objagg_obj) | |
78 | { | |
79 | return --objagg_obj->refcount; | |
80 | } | |
81 | ||
82 | static void objagg_obj_stats_inc(struct objagg_obj *objagg_obj) | |
83 | { | |
84 | objagg_obj->stats.user_count++; | |
85 | objagg_obj->stats.delta_user_count++; | |
86 | if (objagg_obj->parent) | |
87 | objagg_obj->parent->stats.delta_user_count++; | |
88 | } | |
89 | ||
90 | static void objagg_obj_stats_dec(struct objagg_obj *objagg_obj) | |
91 | { | |
92 | objagg_obj->stats.user_count--; | |
93 | objagg_obj->stats.delta_user_count--; | |
94 | if (objagg_obj->parent) | |
95 | objagg_obj->parent->stats.delta_user_count--; | |
96 | } | |
97 | ||
98 | static bool objagg_obj_is_root(const struct objagg_obj *objagg_obj) | |
99 | { | |
100 | /* Nesting is not supported, so we can use ->parent | |
101 | * to figure out if the object is root. | |
102 | */ | |
103 | return !objagg_obj->parent; | |
104 | } | |
105 | ||
106 | /** | |
107 | * objagg_obj_root_priv - obtains root private for an object | |
108 | * @objagg_obj: objagg object instance | |
109 | * | |
110 | * Note: all locking must be provided by the caller. | |
111 | * | |
112 | * Either the object is root itself when the private is returned | |
113 | * directly, or the parent is root and its private is returned | |
114 | * instead. | |
115 | * | |
116 | * Returns a user private root pointer. | |
117 | */ | |
118 | const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj) | |
119 | { | |
120 | if (objagg_obj_is_root(objagg_obj)) | |
121 | return objagg_obj->root_priv; | |
122 | WARN_ON(!objagg_obj_is_root(objagg_obj->parent)); | |
123 | return objagg_obj->parent->root_priv; | |
124 | } | |
125 | EXPORT_SYMBOL(objagg_obj_root_priv); | |
126 | ||
127 | /** | |
128 | * objagg_obj_delta_priv - obtains delta private for an object | |
129 | * @objagg_obj: objagg object instance | |
130 | * | |
131 | * Note: all locking must be provided by the caller. | |
132 | * | |
133 | * Returns user private delta pointer or NULL in case the passed | |
134 | * object is root. | |
135 | */ | |
136 | const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj) | |
137 | { | |
138 | if (objagg_obj_is_root(objagg_obj)) | |
139 | return NULL; | |
140 | return objagg_obj->delta_priv; | |
141 | } | |
142 | EXPORT_SYMBOL(objagg_obj_delta_priv); | |
143 | ||
144 | /** | |
145 | * objagg_obj_raw - obtains object user private pointer | |
146 | * @objagg_obj: objagg object instance | |
147 | * | |
148 | * Note: all locking must be provided by the caller. | |
149 | * | |
150 | * Returns user private pointer as was passed to objagg_obj_get() by "obj" arg. | |
151 | */ | |
152 | const void *objagg_obj_raw(const struct objagg_obj *objagg_obj) | |
153 | { | |
154 | return objagg_obj->obj; | |
155 | } | |
156 | EXPORT_SYMBOL(objagg_obj_raw); | |
157 | ||
158 | static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj) | |
159 | { | |
160 | return rhashtable_lookup_fast(&objagg->obj_ht, obj, objagg->ht_params); | |
161 | } | |
162 | ||
163 | static int objagg_obj_parent_assign(struct objagg *objagg, | |
164 | struct objagg_obj *objagg_obj, | |
9069a381 JP |
165 | struct objagg_obj *parent, |
166 | bool take_parent_ref) | |
0a020d41 JP |
167 | { |
168 | void *delta_priv; | |
169 | ||
170 | delta_priv = objagg->ops->delta_create(objagg->priv, parent->obj, | |
171 | objagg_obj->obj); | |
172 | if (IS_ERR(delta_priv)) | |
173 | return PTR_ERR(delta_priv); | |
174 | ||
175 | /* User returned a delta private, that means that | |
176 | * our object can be aggregated into the parent. | |
177 | */ | |
178 | objagg_obj->parent = parent; | |
179 | objagg_obj->delta_priv = delta_priv; | |
9069a381 JP |
180 | if (take_parent_ref) |
181 | objagg_obj_ref_inc(objagg_obj->parent); | |
0a020d41 JP |
182 | trace_objagg_obj_parent_assign(objagg, objagg_obj, |
183 | parent, | |
184 | parent->refcount); | |
185 | return 0; | |
186 | } | |
187 | ||
188 | static int objagg_obj_parent_lookup_assign(struct objagg *objagg, | |
189 | struct objagg_obj *objagg_obj) | |
190 | { | |
191 | struct objagg_obj *objagg_obj_cur; | |
192 | int err; | |
193 | ||
194 | list_for_each_entry(objagg_obj_cur, &objagg->obj_list, list) { | |
195 | /* Nesting is not supported. In case the object | |
196 | * is not root, it cannot be assigned as parent. | |
197 | */ | |
198 | if (!objagg_obj_is_root(objagg_obj_cur)) | |
199 | continue; | |
200 | err = objagg_obj_parent_assign(objagg, objagg_obj, | |
9069a381 | 201 | objagg_obj_cur, true); |
0a020d41 JP |
202 | if (!err) |
203 | return 0; | |
204 | } | |
205 | return -ENOENT; | |
206 | } | |
207 | ||
208 | static void __objagg_obj_put(struct objagg *objagg, | |
209 | struct objagg_obj *objagg_obj); | |
210 | ||
211 | static void objagg_obj_parent_unassign(struct objagg *objagg, | |
212 | struct objagg_obj *objagg_obj) | |
213 | { | |
214 | trace_objagg_obj_parent_unassign(objagg, objagg_obj, | |
215 | objagg_obj->parent, | |
216 | objagg_obj->parent->refcount); | |
217 | objagg->ops->delta_destroy(objagg->priv, objagg_obj->delta_priv); | |
218 | __objagg_obj_put(objagg, objagg_obj->parent); | |
219 | } | |
220 | ||
9069a381 JP |
221 | static int objagg_obj_root_id_alloc(struct objagg *objagg, |
222 | struct objagg_obj *objagg_obj, | |
223 | struct objagg_hints_node *hnode) | |
224 | { | |
225 | unsigned int min, max; | |
226 | int root_id; | |
227 | ||
228 | /* In case there are no hints available, the root id is invalid. */ | |
229 | if (!objagg->hints) { | |
230 | objagg_obj->root_id = OBJAGG_OBJ_ROOT_ID_INVALID; | |
231 | return 0; | |
232 | } | |
233 | ||
234 | if (hnode) { | |
235 | min = hnode->root_id; | |
236 | max = hnode->root_id; | |
237 | } else { | |
238 | /* For objects with no hint, start after the last | |
239 | * hinted root_id. | |
240 | */ | |
241 | min = objagg->hints->root_count; | |
242 | max = ~0; | |
243 | } | |
244 | ||
245 | root_id = ida_alloc_range(&objagg->root_ida, min, max, GFP_KERNEL); | |
246 | ||
247 | if (root_id < 0) | |
248 | return root_id; | |
249 | objagg_obj->root_id = root_id; | |
250 | return 0; | |
251 | } | |
252 | ||
253 | static void objagg_obj_root_id_free(struct objagg *objagg, | |
254 | struct objagg_obj *objagg_obj) | |
255 | { | |
256 | if (!objagg->hints) | |
257 | return; | |
258 | ida_free(&objagg->root_ida, objagg_obj->root_id); | |
259 | } | |
260 | ||
0a020d41 | 261 | static int objagg_obj_root_create(struct objagg *objagg, |
9069a381 JP |
262 | struct objagg_obj *objagg_obj, |
263 | struct objagg_hints_node *hnode) | |
0a020d41 | 264 | { |
9069a381 | 265 | int err; |
0a020d41 | 266 | |
9069a381 JP |
267 | err = objagg_obj_root_id_alloc(objagg, objagg_obj, hnode); |
268 | if (err) | |
269 | return err; | |
270 | objagg_obj->root_priv = objagg->ops->root_create(objagg->priv, | |
271 | objagg_obj->obj, | |
272 | objagg_obj->root_id); | |
273 | if (IS_ERR(objagg_obj->root_priv)) { | |
274 | err = PTR_ERR(objagg_obj->root_priv); | |
275 | goto err_root_create; | |
276 | } | |
0a020d41 JP |
277 | trace_objagg_obj_root_create(objagg, objagg_obj); |
278 | return 0; | |
9069a381 JP |
279 | |
280 | err_root_create: | |
281 | objagg_obj_root_id_free(objagg, objagg_obj); | |
282 | return err; | |
0a020d41 JP |
283 | } |
284 | ||
285 | static void objagg_obj_root_destroy(struct objagg *objagg, | |
286 | struct objagg_obj *objagg_obj) | |
287 | { | |
288 | trace_objagg_obj_root_destroy(objagg, objagg_obj); | |
289 | objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv); | |
9069a381 JP |
290 | objagg_obj_root_id_free(objagg, objagg_obj); |
291 | } | |
292 | ||
293 | static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj); | |
294 | ||
295 | static int objagg_obj_init_with_hints(struct objagg *objagg, | |
296 | struct objagg_obj *objagg_obj, | |
297 | bool *hint_found) | |
298 | { | |
299 | struct objagg_hints_node *hnode; | |
300 | struct objagg_obj *parent; | |
301 | int err; | |
302 | ||
303 | hnode = objagg_hints_lookup(objagg->hints, objagg_obj->obj); | |
304 | if (!hnode) { | |
305 | *hint_found = false; | |
306 | return 0; | |
307 | } | |
308 | *hint_found = true; | |
309 | ||
310 | if (!hnode->parent) | |
311 | return objagg_obj_root_create(objagg, objagg_obj, hnode); | |
312 | ||
313 | parent = __objagg_obj_get(objagg, hnode->parent->obj); | |
314 | if (IS_ERR(parent)) | |
315 | return PTR_ERR(parent); | |
316 | ||
317 | err = objagg_obj_parent_assign(objagg, objagg_obj, parent, false); | |
318 | if (err) { | |
319 | *hint_found = false; | |
320 | err = 0; | |
321 | goto err_parent_assign; | |
322 | } | |
323 | ||
324 | return 0; | |
325 | ||
326 | err_parent_assign: | |
327 | objagg_obj_put(objagg, parent); | |
328 | return err; | |
0a020d41 JP |
329 | } |
330 | ||
331 | static int objagg_obj_init(struct objagg *objagg, | |
332 | struct objagg_obj *objagg_obj) | |
333 | { | |
9069a381 | 334 | bool hint_found; |
0a020d41 JP |
335 | int err; |
336 | ||
9069a381 JP |
337 | /* First, try to use hints if they are available and |
338 | * if they provide result. | |
339 | */ | |
340 | err = objagg_obj_init_with_hints(objagg, objagg_obj, &hint_found); | |
341 | if (err) | |
342 | return err; | |
343 | ||
344 | if (hint_found) | |
345 | return 0; | |
346 | ||
0a020d41 JP |
347 | /* Try to find if the object can be aggregated under an existing one. */ |
348 | err = objagg_obj_parent_lookup_assign(objagg, objagg_obj); | |
349 | if (!err) | |
350 | return 0; | |
351 | /* If aggregation is not possible, make the object a root. */ | |
9069a381 | 352 | return objagg_obj_root_create(objagg, objagg_obj, NULL); |
0a020d41 JP |
353 | } |
354 | ||
355 | static void objagg_obj_fini(struct objagg *objagg, | |
356 | struct objagg_obj *objagg_obj) | |
357 | { | |
358 | if (!objagg_obj_is_root(objagg_obj)) | |
359 | objagg_obj_parent_unassign(objagg, objagg_obj); | |
360 | else | |
361 | objagg_obj_root_destroy(objagg, objagg_obj); | |
362 | } | |
363 | ||
364 | static struct objagg_obj *objagg_obj_create(struct objagg *objagg, void *obj) | |
365 | { | |
366 | struct objagg_obj *objagg_obj; | |
367 | int err; | |
368 | ||
369 | objagg_obj = kzalloc(sizeof(*objagg_obj) + objagg->ops->obj_size, | |
370 | GFP_KERNEL); | |
371 | if (!objagg_obj) | |
372 | return ERR_PTR(-ENOMEM); | |
373 | objagg_obj_ref_inc(objagg_obj); | |
374 | memcpy(objagg_obj->obj, obj, objagg->ops->obj_size); | |
375 | ||
376 | err = objagg_obj_init(objagg, objagg_obj); | |
377 | if (err) | |
378 | goto err_obj_init; | |
379 | ||
380 | err = rhashtable_insert_fast(&objagg->obj_ht, &objagg_obj->ht_node, | |
381 | objagg->ht_params); | |
382 | if (err) | |
383 | goto err_ht_insert; | |
384 | list_add(&objagg_obj->list, &objagg->obj_list); | |
385 | objagg->obj_count++; | |
386 | trace_objagg_obj_create(objagg, objagg_obj); | |
387 | ||
388 | return objagg_obj; | |
389 | ||
390 | err_ht_insert: | |
391 | objagg_obj_fini(objagg, objagg_obj); | |
392 | err_obj_init: | |
393 | kfree(objagg_obj); | |
394 | return ERR_PTR(err); | |
395 | } | |
396 | ||
397 | static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj) | |
398 | { | |
399 | struct objagg_obj *objagg_obj; | |
400 | ||
401 | /* First, try to find the object exactly as user passed it, | |
402 | * perhaps it is already in use. | |
403 | */ | |
404 | objagg_obj = objagg_obj_lookup(objagg, obj); | |
405 | if (objagg_obj) { | |
406 | objagg_obj_ref_inc(objagg_obj); | |
407 | return objagg_obj; | |
408 | } | |
409 | ||
410 | return objagg_obj_create(objagg, obj); | |
411 | } | |
412 | ||
413 | /** | |
414 | * objagg_obj_get - gets an object within objagg instance | |
415 | * @objagg: objagg instance | |
416 | * @obj: user-specific private object pointer | |
417 | * | |
418 | * Note: all locking must be provided by the caller. | |
419 | * | |
420 | * Size of the "obj" memory is specified in "objagg->ops". | |
421 | * | |
422 | * There are 3 main options this function wraps: | |
423 | * 1) The object according to "obj" already exist. In that case | |
424 | * the reference counter is incrementes and the object is returned. | |
425 | * 2) The object does not exist, but it can be aggregated within | |
426 | * another object. In that case, user ops->delta_create() is called | |
427 | * to obtain delta data and a new object is created with returned | |
428 | * user-delta private pointer. | |
429 | * 3) The object does not exist and cannot be aggregated into | |
430 | * any of the existing objects. In that case, user ops->root_create() | |
431 | * is called to create the root and a new object is created with | |
432 | * returned user-root private pointer. | |
433 | * | |
434 | * Returns a pointer to objagg object instance in case of success, | |
435 | * otherwise it returns pointer error using ERR_PTR macro. | |
436 | */ | |
437 | struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj) | |
438 | { | |
439 | struct objagg_obj *objagg_obj; | |
440 | ||
441 | objagg_obj = __objagg_obj_get(objagg, obj); | |
442 | if (IS_ERR(objagg_obj)) | |
443 | return objagg_obj; | |
444 | objagg_obj_stats_inc(objagg_obj); | |
445 | trace_objagg_obj_get(objagg, objagg_obj, objagg_obj->refcount); | |
446 | return objagg_obj; | |
447 | } | |
448 | EXPORT_SYMBOL(objagg_obj_get); | |
449 | ||
450 | static void objagg_obj_destroy(struct objagg *objagg, | |
451 | struct objagg_obj *objagg_obj) | |
452 | { | |
453 | trace_objagg_obj_destroy(objagg, objagg_obj); | |
454 | --objagg->obj_count; | |
455 | list_del(&objagg_obj->list); | |
456 | rhashtable_remove_fast(&objagg->obj_ht, &objagg_obj->ht_node, | |
457 | objagg->ht_params); | |
458 | objagg_obj_fini(objagg, objagg_obj); | |
459 | kfree(objagg_obj); | |
460 | } | |
461 | ||
462 | static void __objagg_obj_put(struct objagg *objagg, | |
463 | struct objagg_obj *objagg_obj) | |
464 | { | |
465 | if (!objagg_obj_ref_dec(objagg_obj)) | |
466 | objagg_obj_destroy(objagg, objagg_obj); | |
467 | } | |
468 | ||
469 | /** | |
470 | * objagg_obj_put - puts an object within objagg instance | |
471 | * @objagg: objagg instance | |
472 | * @objagg_obj: objagg object instance | |
473 | * | |
474 | * Note: all locking must be provided by the caller. | |
475 | * | |
476 | * Symmetric to objagg_obj_get(). | |
477 | */ | |
478 | void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj) | |
479 | { | |
480 | trace_objagg_obj_put(objagg, objagg_obj, objagg_obj->refcount); | |
481 | objagg_obj_stats_dec(objagg_obj); | |
482 | __objagg_obj_put(objagg, objagg_obj); | |
483 | } | |
484 | EXPORT_SYMBOL(objagg_obj_put); | |
485 | ||
486 | /** | |
487 | * objagg_create - creates a new objagg instance | |
9069a381 JP |
488 | * @ops: user-specific callbacks |
489 | * @objagg_hints: hints, can be NULL | |
490 | * @priv: pointer to a private data passed to the ops | |
0a020d41 JP |
491 | * |
492 | * Note: all locking must be provided by the caller. | |
493 | * | |
494 | * The purpose of the library is to provide an infrastructure to | |
495 | * aggregate user-specified objects. Library does not care about the type | |
496 | * of the object. User fills-up ops which take care of the specific | |
497 | * user object manipulation. | |
498 | * | |
499 | * As a very stupid example, consider integer numbers. For example | |
500 | * number 8 as a root object. That can aggregate number 9 with delta 1, | |
501 | * number 10 with delta 2, etc. This example is implemented as | |
502 | * a part of a testing module in test_objagg.c file. | |
503 | * | |
504 | * Each objagg instance contains multiple trees. Each tree node is | |
505 | * represented by "an object". In the current implementation there can be | |
506 | * only roots and leafs nodes. Leaf nodes are called deltas. | |
507 | * But in general, this can be easily extended for intermediate nodes. | |
508 | * In that extension, a delta would be associated with all non-root | |
509 | * nodes. | |
510 | * | |
511 | * Returns a pointer to newly created objagg instance in case of success, | |
512 | * otherwise it returns pointer error using ERR_PTR macro. | |
513 | */ | |
9069a381 JP |
514 | struct objagg *objagg_create(const struct objagg_ops *ops, |
515 | struct objagg_hints *objagg_hints, void *priv) | |
0a020d41 JP |
516 | { |
517 | struct objagg *objagg; | |
518 | int err; | |
519 | ||
520 | if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy || | |
9069a381 JP |
521 | !ops->delta_check || !ops->delta_create || |
522 | !ops->delta_destroy)) | |
0a020d41 | 523 | return ERR_PTR(-EINVAL); |
9069a381 | 524 | |
0a020d41 JP |
525 | objagg = kzalloc(sizeof(*objagg), GFP_KERNEL); |
526 | if (!objagg) | |
527 | return ERR_PTR(-ENOMEM); | |
528 | objagg->ops = ops; | |
9069a381 JP |
529 | if (objagg_hints) { |
530 | objagg->hints = objagg_hints; | |
531 | objagg_hints->refcount++; | |
532 | } | |
0a020d41 JP |
533 | objagg->priv = priv; |
534 | INIT_LIST_HEAD(&objagg->obj_list); | |
535 | ||
536 | objagg->ht_params.key_len = ops->obj_size; | |
537 | objagg->ht_params.key_offset = offsetof(struct objagg_obj, obj); | |
538 | objagg->ht_params.head_offset = offsetof(struct objagg_obj, ht_node); | |
539 | ||
540 | err = rhashtable_init(&objagg->obj_ht, &objagg->ht_params); | |
541 | if (err) | |
542 | goto err_rhashtable_init; | |
543 | ||
9069a381 JP |
544 | ida_init(&objagg->root_ida); |
545 | ||
0a020d41 JP |
546 | trace_objagg_create(objagg); |
547 | return objagg; | |
548 | ||
549 | err_rhashtable_init: | |
550 | kfree(objagg); | |
551 | return ERR_PTR(err); | |
552 | } | |
553 | EXPORT_SYMBOL(objagg_create); | |
554 | ||
555 | /** | |
556 | * objagg_destroy - destroys a new objagg instance | |
557 | * @objagg: objagg instance | |
558 | * | |
559 | * Note: all locking must be provided by the caller. | |
560 | */ | |
561 | void objagg_destroy(struct objagg *objagg) | |
562 | { | |
563 | trace_objagg_destroy(objagg); | |
9069a381 | 564 | ida_destroy(&objagg->root_ida); |
0a020d41 JP |
565 | WARN_ON(!list_empty(&objagg->obj_list)); |
566 | rhashtable_destroy(&objagg->obj_ht); | |
9069a381 JP |
567 | if (objagg->hints) |
568 | objagg_hints_put(objagg->hints); | |
0a020d41 JP |
569 | kfree(objagg); |
570 | } | |
571 | EXPORT_SYMBOL(objagg_destroy); | |
572 | ||
573 | static int objagg_stats_info_sort_cmp_func(const void *a, const void *b) | |
574 | { | |
575 | const struct objagg_obj_stats_info *stats_info1 = a; | |
576 | const struct objagg_obj_stats_info *stats_info2 = b; | |
577 | ||
578 | if (stats_info1->is_root != stats_info2->is_root) | |
579 | return stats_info2->is_root - stats_info1->is_root; | |
580 | if (stats_info1->stats.delta_user_count != | |
581 | stats_info2->stats.delta_user_count) | |
582 | return stats_info2->stats.delta_user_count - | |
583 | stats_info1->stats.delta_user_count; | |
584 | return stats_info2->stats.user_count - stats_info1->stats.user_count; | |
585 | } | |
586 | ||
587 | /** | |
588 | * objagg_stats_get - obtains stats of the objagg instance | |
589 | * @objagg: objagg instance | |
590 | * | |
591 | * Note: all locking must be provided by the caller. | |
592 | * | |
593 | * The returned structure contains statistics of all object | |
594 | * currently in use, ordered by following rules: | |
595 | * 1) Root objects are always on lower indexes than the rest. | |
596 | * 2) Objects with higher delta user count are always on lower | |
597 | * indexes. | |
598 | * 3) In case more objects have the same delta user count, | |
599 | * the objects are ordered by user count. | |
600 | * | |
601 | * Returns a pointer to stats instance in case of success, | |
602 | * otherwise it returns pointer error using ERR_PTR macro. | |
603 | */ | |
604 | const struct objagg_stats *objagg_stats_get(struct objagg *objagg) | |
605 | { | |
606 | struct objagg_stats *objagg_stats; | |
607 | struct objagg_obj *objagg_obj; | |
0a020d41 JP |
608 | int i; |
609 | ||
e736bf72 GS |
610 | objagg_stats = kzalloc(struct_size(objagg_stats, stats_info, |
611 | objagg->obj_count), GFP_KERNEL); | |
0a020d41 JP |
612 | if (!objagg_stats) |
613 | return ERR_PTR(-ENOMEM); | |
614 | ||
615 | i = 0; | |
616 | list_for_each_entry(objagg_obj, &objagg->obj_list, list) { | |
617 | memcpy(&objagg_stats->stats_info[i].stats, &objagg_obj->stats, | |
618 | sizeof(objagg_stats->stats_info[0].stats)); | |
619 | objagg_stats->stats_info[i].objagg_obj = objagg_obj; | |
620 | objagg_stats->stats_info[i].is_root = | |
621 | objagg_obj_is_root(objagg_obj); | |
204f6a8c JP |
622 | if (objagg_stats->stats_info[i].is_root) |
623 | objagg_stats->root_count++; | |
0a020d41 JP |
624 | i++; |
625 | } | |
626 | objagg_stats->stats_info_count = i; | |
627 | ||
628 | sort(objagg_stats->stats_info, objagg_stats->stats_info_count, | |
629 | sizeof(struct objagg_obj_stats_info), | |
630 | objagg_stats_info_sort_cmp_func, NULL); | |
631 | ||
632 | return objagg_stats; | |
633 | } | |
634 | EXPORT_SYMBOL(objagg_stats_get); | |
635 | ||
636 | /** | |
bb72e68b | 637 | * objagg_stats_put - puts stats of the objagg instance |
0a020d41 JP |
638 | * @objagg_stats: objagg instance stats |
639 | * | |
640 | * Note: all locking must be provided by the caller. | |
641 | */ | |
642 | void objagg_stats_put(const struct objagg_stats *objagg_stats) | |
643 | { | |
644 | kfree(objagg_stats); | |
645 | } | |
646 | EXPORT_SYMBOL(objagg_stats_put); | |
647 | ||
9069a381 JP |
648 | static struct objagg_hints_node * |
649 | objagg_hints_node_create(struct objagg_hints *objagg_hints, | |
650 | struct objagg_obj *objagg_obj, size_t obj_size, | |
651 | struct objagg_hints_node *parent_hnode) | |
652 | { | |
653 | unsigned int user_count = objagg_obj->stats.user_count; | |
654 | struct objagg_hints_node *hnode; | |
655 | int err; | |
656 | ||
657 | hnode = kzalloc(sizeof(*hnode) + obj_size, GFP_KERNEL); | |
658 | if (!hnode) | |
659 | return ERR_PTR(-ENOMEM); | |
660 | memcpy(hnode->obj, &objagg_obj->obj, obj_size); | |
661 | hnode->stats_info.stats.user_count = user_count; | |
662 | hnode->stats_info.stats.delta_user_count = user_count; | |
663 | if (parent_hnode) { | |
664 | parent_hnode->stats_info.stats.delta_user_count += user_count; | |
665 | } else { | |
666 | hnode->root_id = objagg_hints->root_count++; | |
667 | hnode->stats_info.is_root = true; | |
668 | } | |
669 | hnode->stats_info.objagg_obj = objagg_obj; | |
670 | ||
671 | err = rhashtable_insert_fast(&objagg_hints->node_ht, &hnode->ht_node, | |
672 | objagg_hints->ht_params); | |
673 | if (err) | |
674 | goto err_ht_insert; | |
675 | ||
676 | list_add(&hnode->list, &objagg_hints->node_list); | |
677 | hnode->parent = parent_hnode; | |
678 | objagg_hints->node_count++; | |
679 | ||
680 | return hnode; | |
681 | ||
682 | err_ht_insert: | |
683 | kfree(hnode); | |
684 | return ERR_PTR(err); | |
685 | } | |
686 | ||
687 | static void objagg_hints_flush(struct objagg_hints *objagg_hints) | |
688 | { | |
689 | struct objagg_hints_node *hnode, *tmp; | |
690 | ||
691 | list_for_each_entry_safe(hnode, tmp, &objagg_hints->node_list, list) { | |
692 | list_del(&hnode->list); | |
693 | rhashtable_remove_fast(&objagg_hints->node_ht, &hnode->ht_node, | |
694 | objagg_hints->ht_params); | |
695 | kfree(hnode); | |
696 | } | |
697 | } | |
698 | ||
699 | struct objagg_tmp_node { | |
700 | struct objagg_obj *objagg_obj; | |
701 | bool crossed_out; | |
702 | }; | |
703 | ||
704 | struct objagg_tmp_graph { | |
705 | struct objagg_tmp_node *nodes; | |
706 | unsigned long nodes_count; | |
707 | unsigned long *edges; | |
708 | }; | |
709 | ||
710 | static int objagg_tmp_graph_edge_index(struct objagg_tmp_graph *graph, | |
711 | int parent_index, int index) | |
712 | { | |
713 | return index * graph->nodes_count + parent_index; | |
714 | } | |
715 | ||
716 | static void objagg_tmp_graph_edge_set(struct objagg_tmp_graph *graph, | |
717 | int parent_index, int index) | |
718 | { | |
719 | int edge_index = objagg_tmp_graph_edge_index(graph, index, | |
720 | parent_index); | |
721 | ||
722 | __set_bit(edge_index, graph->edges); | |
723 | } | |
724 | ||
725 | static bool objagg_tmp_graph_is_edge(struct objagg_tmp_graph *graph, | |
726 | int parent_index, int index) | |
727 | { | |
728 | int edge_index = objagg_tmp_graph_edge_index(graph, index, | |
729 | parent_index); | |
730 | ||
731 | return test_bit(edge_index, graph->edges); | |
732 | } | |
733 | ||
734 | static unsigned int objagg_tmp_graph_node_weight(struct objagg_tmp_graph *graph, | |
735 | unsigned int index) | |
736 | { | |
737 | struct objagg_tmp_node *node = &graph->nodes[index]; | |
738 | unsigned int weight = node->objagg_obj->stats.user_count; | |
739 | int j; | |
740 | ||
741 | /* Node weight is sum of node users and all other nodes users | |
742 | * that this node can represent with delta. | |
743 | */ | |
744 | ||
9069a381 JP |
745 | for (j = 0; j < graph->nodes_count; j++) { |
746 | if (!objagg_tmp_graph_is_edge(graph, index, j)) | |
747 | continue; | |
748 | node = &graph->nodes[j]; | |
749 | if (node->crossed_out) | |
750 | continue; | |
751 | weight += node->objagg_obj->stats.user_count; | |
752 | } | |
753 | return weight; | |
754 | } | |
755 | ||
756 | static int objagg_tmp_graph_node_max_weight(struct objagg_tmp_graph *graph) | |
757 | { | |
fa8ba2cb | 758 | struct objagg_tmp_node *node; |
9069a381 JP |
759 | unsigned int max_weight = 0; |
760 | unsigned int weight; | |
761 | int max_index = -1; | |
762 | int i; | |
763 | ||
764 | for (i = 0; i < graph->nodes_count; i++) { | |
fa8ba2cb JP |
765 | node = &graph->nodes[i]; |
766 | if (node->crossed_out) | |
767 | continue; | |
9069a381 | 768 | weight = objagg_tmp_graph_node_weight(graph, i); |
fa8ba2cb | 769 | if (weight >= max_weight) { |
9069a381 JP |
770 | max_weight = weight; |
771 | max_index = i; | |
772 | } | |
773 | } | |
774 | return max_index; | |
775 | } | |
776 | ||
777 | static struct objagg_tmp_graph *objagg_tmp_graph_create(struct objagg *objagg) | |
778 | { | |
779 | unsigned int nodes_count = objagg->obj_count; | |
780 | struct objagg_tmp_graph *graph; | |
781 | struct objagg_tmp_node *node; | |
782 | struct objagg_tmp_node *pnode; | |
783 | struct objagg_obj *objagg_obj; | |
784 | size_t alloc_size; | |
785 | int i, j; | |
786 | ||
787 | graph = kzalloc(sizeof(*graph), GFP_KERNEL); | |
788 | if (!graph) | |
789 | return NULL; | |
790 | ||
791 | graph->nodes = kcalloc(nodes_count, sizeof(*graph->nodes), GFP_KERNEL); | |
792 | if (!graph->nodes) | |
793 | goto err_nodes_alloc; | |
794 | graph->nodes_count = nodes_count; | |
795 | ||
796 | alloc_size = BITS_TO_LONGS(nodes_count * nodes_count) * | |
797 | sizeof(unsigned long); | |
798 | graph->edges = kzalloc(alloc_size, GFP_KERNEL); | |
799 | if (!graph->edges) | |
800 | goto err_edges_alloc; | |
801 | ||
802 | i = 0; | |
803 | list_for_each_entry(objagg_obj, &objagg->obj_list, list) { | |
804 | node = &graph->nodes[i++]; | |
805 | node->objagg_obj = objagg_obj; | |
806 | } | |
807 | ||
808 | /* Assemble a temporary graph. Insert edge X->Y in case Y can be | |
809 | * in delta of X. | |
810 | */ | |
811 | for (i = 0; i < nodes_count; i++) { | |
812 | for (j = 0; j < nodes_count; j++) { | |
813 | if (i == j) | |
814 | continue; | |
815 | pnode = &graph->nodes[i]; | |
816 | node = &graph->nodes[j]; | |
817 | if (objagg->ops->delta_check(objagg->priv, | |
818 | pnode->objagg_obj->obj, | |
819 | node->objagg_obj->obj)) { | |
820 | objagg_tmp_graph_edge_set(graph, i, j); | |
821 | ||
822 | } | |
823 | } | |
824 | } | |
825 | return graph; | |
826 | ||
827 | err_edges_alloc: | |
828 | kfree(graph->nodes); | |
829 | err_nodes_alloc: | |
830 | kfree(graph); | |
831 | return NULL; | |
832 | } | |
833 | ||
834 | static void objagg_tmp_graph_destroy(struct objagg_tmp_graph *graph) | |
835 | { | |
836 | kfree(graph->edges); | |
837 | kfree(graph->nodes); | |
838 | kfree(graph); | |
839 | } | |
840 | ||
841 | static int | |
842 | objagg_opt_simple_greedy_fillup_hints(struct objagg_hints *objagg_hints, | |
843 | struct objagg *objagg) | |
844 | { | |
845 | struct objagg_hints_node *hnode, *parent_hnode; | |
846 | struct objagg_tmp_graph *graph; | |
847 | struct objagg_tmp_node *node; | |
848 | int index; | |
849 | int j; | |
850 | int err; | |
851 | ||
852 | graph = objagg_tmp_graph_create(objagg); | |
853 | if (!graph) | |
854 | return -ENOMEM; | |
855 | ||
856 | /* Find the nodes from the ones that can accommodate most users | |
857 | * and cross them out of the graph. Save them to the hint list. | |
858 | */ | |
859 | while ((index = objagg_tmp_graph_node_max_weight(graph)) != -1) { | |
860 | node = &graph->nodes[index]; | |
861 | node->crossed_out = true; | |
862 | hnode = objagg_hints_node_create(objagg_hints, | |
863 | node->objagg_obj, | |
864 | objagg->ops->obj_size, | |
865 | NULL); | |
866 | if (IS_ERR(hnode)) { | |
867 | err = PTR_ERR(hnode); | |
868 | goto out; | |
869 | } | |
870 | parent_hnode = hnode; | |
871 | for (j = 0; j < graph->nodes_count; j++) { | |
872 | if (!objagg_tmp_graph_is_edge(graph, index, j)) | |
873 | continue; | |
874 | node = &graph->nodes[j]; | |
875 | if (node->crossed_out) | |
876 | continue; | |
877 | node->crossed_out = true; | |
878 | hnode = objagg_hints_node_create(objagg_hints, | |
879 | node->objagg_obj, | |
880 | objagg->ops->obj_size, | |
881 | parent_hnode); | |
882 | if (IS_ERR(hnode)) { | |
883 | err = PTR_ERR(hnode); | |
884 | goto out; | |
885 | } | |
886 | } | |
887 | } | |
888 | ||
889 | err = 0; | |
890 | out: | |
891 | objagg_tmp_graph_destroy(graph); | |
892 | return err; | |
893 | } | |
894 | ||
895 | struct objagg_opt_algo { | |
896 | int (*fillup_hints)(struct objagg_hints *objagg_hints, | |
897 | struct objagg *objagg); | |
898 | }; | |
899 | ||
900 | static const struct objagg_opt_algo objagg_opt_simple_greedy = { | |
901 | .fillup_hints = objagg_opt_simple_greedy_fillup_hints, | |
902 | }; | |
903 | ||
904 | ||
905 | static const struct objagg_opt_algo *objagg_opt_algos[] = { | |
906 | [OBJAGG_OPT_ALGO_SIMPLE_GREEDY] = &objagg_opt_simple_greedy, | |
907 | }; | |
908 | ||
909 | static int objagg_hints_obj_cmp(struct rhashtable_compare_arg *arg, | |
910 | const void *obj) | |
911 | { | |
912 | struct rhashtable *ht = arg->ht; | |
913 | struct objagg_hints *objagg_hints = | |
914 | container_of(ht, struct objagg_hints, node_ht); | |
915 | const struct objagg_ops *ops = objagg_hints->ops; | |
916 | const char *ptr = obj; | |
917 | ||
918 | ptr += ht->p.key_offset; | |
919 | return ops->hints_obj_cmp ? ops->hints_obj_cmp(ptr, arg->key) : | |
920 | memcmp(ptr, arg->key, ht->p.key_len); | |
921 | } | |
922 | ||
923 | /** | |
924 | * objagg_hints_get - obtains hints instance | |
925 | * @objagg: objagg instance | |
926 | * @opt_algo_type: type of hints finding algorithm | |
927 | * | |
928 | * Note: all locking must be provided by the caller. | |
929 | * | |
930 | * According to the algo type, the existing objects of objagg instance | |
931 | * are going to be went-through to assemble an optimal tree. We call this | |
932 | * tree hints. These hints can be later on used for creation of | |
933 | * a new objagg instance. There, the future object creations are going | |
934 | * to be consulted with these hints in order to find out, where exactly | |
935 | * the new object should be put as a root or delta. | |
936 | * | |
937 | * Returns a pointer to hints instance in case of success, | |
938 | * otherwise it returns pointer error using ERR_PTR macro. | |
939 | */ | |
940 | struct objagg_hints *objagg_hints_get(struct objagg *objagg, | |
941 | enum objagg_opt_algo_type opt_algo_type) | |
942 | { | |
943 | const struct objagg_opt_algo *algo = objagg_opt_algos[opt_algo_type]; | |
944 | struct objagg_hints *objagg_hints; | |
945 | int err; | |
946 | ||
947 | objagg_hints = kzalloc(sizeof(*objagg_hints), GFP_KERNEL); | |
948 | if (!objagg_hints) | |
949 | return ERR_PTR(-ENOMEM); | |
950 | ||
951 | objagg_hints->ops = objagg->ops; | |
952 | objagg_hints->refcount = 1; | |
953 | ||
954 | INIT_LIST_HEAD(&objagg_hints->node_list); | |
955 | ||
956 | objagg_hints->ht_params.key_len = objagg->ops->obj_size; | |
957 | objagg_hints->ht_params.key_offset = | |
958 | offsetof(struct objagg_hints_node, obj); | |
959 | objagg_hints->ht_params.head_offset = | |
960 | offsetof(struct objagg_hints_node, ht_node); | |
961 | objagg_hints->ht_params.obj_cmpfn = objagg_hints_obj_cmp; | |
962 | ||
963 | err = rhashtable_init(&objagg_hints->node_ht, &objagg_hints->ht_params); | |
964 | if (err) | |
965 | goto err_rhashtable_init; | |
966 | ||
967 | err = algo->fillup_hints(objagg_hints, objagg); | |
968 | if (err) | |
969 | goto err_fillup_hints; | |
970 | ||
4446eb8d DC |
971 | if (WARN_ON(objagg_hints->node_count != objagg->obj_count)) { |
972 | err = -EINVAL; | |
9069a381 | 973 | goto err_node_count_check; |
4446eb8d | 974 | } |
9069a381 JP |
975 | |
976 | return objagg_hints; | |
977 | ||
978 | err_node_count_check: | |
979 | err_fillup_hints: | |
980 | objagg_hints_flush(objagg_hints); | |
981 | rhashtable_destroy(&objagg_hints->node_ht); | |
982 | err_rhashtable_init: | |
983 | kfree(objagg_hints); | |
984 | return ERR_PTR(err); | |
985 | } | |
986 | EXPORT_SYMBOL(objagg_hints_get); | |
987 | ||
988 | /** | |
989 | * objagg_hints_put - puts hints instance | |
990 | * @objagg_hints: objagg hints instance | |
991 | * | |
992 | * Note: all locking must be provided by the caller. | |
993 | */ | |
994 | void objagg_hints_put(struct objagg_hints *objagg_hints) | |
995 | { | |
996 | if (--objagg_hints->refcount) | |
997 | return; | |
998 | objagg_hints_flush(objagg_hints); | |
999 | rhashtable_destroy(&objagg_hints->node_ht); | |
1000 | kfree(objagg_hints); | |
1001 | } | |
1002 | EXPORT_SYMBOL(objagg_hints_put); | |
1003 | ||
1004 | /** | |
1005 | * objagg_hints_stats_get - obtains stats of the hints instance | |
1006 | * @objagg_hints: hints instance | |
1007 | * | |
1008 | * Note: all locking must be provided by the caller. | |
1009 | * | |
1010 | * The returned structure contains statistics of all objects | |
1011 | * currently in use, ordered by following rules: | |
1012 | * 1) Root objects are always on lower indexes than the rest. | |
1013 | * 2) Objects with higher delta user count are always on lower | |
1014 | * indexes. | |
1015 | * 3) In case multiple objects have the same delta user count, | |
1016 | * the objects are ordered by user count. | |
1017 | * | |
1018 | * Returns a pointer to stats instance in case of success, | |
1019 | * otherwise it returns pointer error using ERR_PTR macro. | |
1020 | */ | |
1021 | const struct objagg_stats * | |
1022 | objagg_hints_stats_get(struct objagg_hints *objagg_hints) | |
1023 | { | |
1024 | struct objagg_stats *objagg_stats; | |
1025 | struct objagg_hints_node *hnode; | |
1026 | int i; | |
1027 | ||
1028 | objagg_stats = kzalloc(struct_size(objagg_stats, stats_info, | |
1029 | objagg_hints->node_count), | |
1030 | GFP_KERNEL); | |
1031 | if (!objagg_stats) | |
1032 | return ERR_PTR(-ENOMEM); | |
1033 | ||
1034 | i = 0; | |
1035 | list_for_each_entry(hnode, &objagg_hints->node_list, list) { | |
1036 | memcpy(&objagg_stats->stats_info[i], &hnode->stats_info, | |
1037 | sizeof(objagg_stats->stats_info[0])); | |
204f6a8c JP |
1038 | if (objagg_stats->stats_info[i].is_root) |
1039 | objagg_stats->root_count++; | |
9069a381 JP |
1040 | i++; |
1041 | } | |
1042 | objagg_stats->stats_info_count = i; | |
1043 | ||
1044 | sort(objagg_stats->stats_info, objagg_stats->stats_info_count, | |
1045 | sizeof(struct objagg_obj_stats_info), | |
1046 | objagg_stats_info_sort_cmp_func, NULL); | |
1047 | ||
1048 | return objagg_stats; | |
1049 | } | |
1050 | EXPORT_SYMBOL(objagg_hints_stats_get); | |
1051 | ||
0a020d41 JP |
1052 | MODULE_LICENSE("Dual BSD/GPL"); |
1053 | MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); | |
1054 | MODULE_DESCRIPTION("Object aggregation manager"); |