diff options
Diffstat (limited to 'lib/objagg.c')
-rw-r--r-- | lib/objagg.c | 573 |
1 files changed, 560 insertions, 13 deletions
diff --git a/lib/objagg.c b/lib/objagg.c index dae390bcef1a..befe8a47d080 100644 --- a/lib/objagg.c +++ b/lib/objagg.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/module.h> | 4 | #include <linux/module.h> |
5 | #include <linux/slab.h> | 5 | #include <linux/slab.h> |
6 | #include <linux/rhashtable.h> | 6 | #include <linux/rhashtable.h> |
7 | #include <linux/idr.h> | ||
7 | #include <linux/list.h> | 8 | #include <linux/list.h> |
8 | #include <linux/sort.h> | 9 | #include <linux/sort.h> |
9 | #include <linux/objagg.h> | 10 | #include <linux/objagg.h> |
@@ -11,6 +12,34 @@ | |||
11 | #define CREATE_TRACE_POINTS | 12 | #define CREATE_TRACE_POINTS |
12 | #include <trace/events/objagg.h> | 13 | #include <trace/events/objagg.h> |
13 | 14 | ||
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 | |||
14 | struct objagg { | 43 | struct objagg { |
15 | const struct objagg_ops *ops; | 44 | const struct objagg_ops *ops; |
16 | void *priv; | 45 | void *priv; |
@@ -18,6 +47,8 @@ struct objagg { | |||
18 | struct rhashtable_params ht_params; | 47 | struct rhashtable_params ht_params; |
19 | struct list_head obj_list; | 48 | struct list_head obj_list; |
20 | unsigned int obj_count; | 49 | unsigned int obj_count; |
50 | struct ida root_ida; | ||
51 | struct objagg_hints *hints; | ||
21 | }; | 52 | }; |
22 | 53 | ||
23 | struct objagg_obj { | 54 | struct objagg_obj { |
@@ -30,6 +61,7 @@ struct objagg_obj { | |||
30 | void *delta_priv; /* user delta private */ | 61 | void *delta_priv; /* user delta private */ |
31 | void *root_priv; /* user root private */ | 62 | void *root_priv; /* user root private */ |
32 | }; | 63 | }; |
64 | unsigned int root_id; | ||
33 | unsigned int refcount; /* counts number of users of this object | 65 | unsigned int refcount; /* counts number of users of this object |
34 | * including nested objects | 66 | * including nested objects |
35 | */ | 67 | */ |
@@ -130,7 +162,8 @@ static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj) | |||
130 | 162 | ||
131 | static int objagg_obj_parent_assign(struct objagg *objagg, | 163 | static int objagg_obj_parent_assign(struct objagg *objagg, |
132 | struct objagg_obj *objagg_obj, | 164 | struct objagg_obj *objagg_obj, |
133 | struct objagg_obj *parent) | 165 | struct objagg_obj *parent, |
166 | bool take_parent_ref) | ||
134 | { | 167 | { |
135 | void *delta_priv; | 168 | void *delta_priv; |
136 | 169 | ||
@@ -144,7 +177,8 @@ static int objagg_obj_parent_assign(struct objagg *objagg, | |||
144 | */ | 177 | */ |
145 | objagg_obj->parent = parent; | 178 | objagg_obj->parent = parent; |
146 | objagg_obj->delta_priv = delta_priv; | 179 | objagg_obj->delta_priv = delta_priv; |
147 | objagg_obj_ref_inc(objagg_obj->parent); | 180 | if (take_parent_ref) |
181 | objagg_obj_ref_inc(objagg_obj->parent); | ||
148 | trace_objagg_obj_parent_assign(objagg, objagg_obj, | 182 | trace_objagg_obj_parent_assign(objagg, objagg_obj, |
149 | parent, | 183 | parent, |
150 | parent->refcount); | 184 | parent->refcount); |
@@ -164,7 +198,7 @@ static int objagg_obj_parent_lookup_assign(struct objagg *objagg, | |||
164 | if (!objagg_obj_is_root(objagg_obj_cur)) | 198 | if (!objagg_obj_is_root(objagg_obj_cur)) |
165 | continue; | 199 | continue; |
166 | err = objagg_obj_parent_assign(objagg, objagg_obj, | 200 | err = objagg_obj_parent_assign(objagg, objagg_obj, |
167 | objagg_obj_cur); | 201 | objagg_obj_cur, true); |
168 | if (!err) | 202 | if (!err) |
169 | return 0; | 203 | return 0; |
170 | } | 204 | } |
@@ -184,16 +218,68 @@ static void objagg_obj_parent_unassign(struct objagg *objagg, | |||
184 | __objagg_obj_put(objagg, objagg_obj->parent); | 218 | __objagg_obj_put(objagg, objagg_obj->parent); |
185 | } | 219 | } |
186 | 220 | ||
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 | |||
187 | static int objagg_obj_root_create(struct objagg *objagg, | 261 | static int objagg_obj_root_create(struct objagg *objagg, |
188 | struct objagg_obj *objagg_obj) | 262 | struct objagg_obj *objagg_obj, |
263 | struct objagg_hints_node *hnode) | ||
189 | { | 264 | { |
190 | objagg_obj->root_priv = objagg->ops->root_create(objagg->priv, | 265 | int err; |
191 | objagg_obj->obj); | ||
192 | if (IS_ERR(objagg_obj->root_priv)) | ||
193 | return PTR_ERR(objagg_obj->root_priv); | ||
194 | 266 | ||
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 | } | ||
195 | trace_objagg_obj_root_create(objagg, objagg_obj); | 277 | trace_objagg_obj_root_create(objagg, objagg_obj); |
196 | return 0; | 278 | return 0; |
279 | |||
280 | err_root_create: | ||
281 | objagg_obj_root_id_free(objagg, objagg_obj); | ||
282 | return err; | ||
197 | } | 283 | } |
198 | 284 | ||
199 | static void objagg_obj_root_destroy(struct objagg *objagg, | 285 | static void objagg_obj_root_destroy(struct objagg *objagg, |
@@ -201,19 +287,69 @@ static void objagg_obj_root_destroy(struct objagg *objagg, | |||
201 | { | 287 | { |
202 | trace_objagg_obj_root_destroy(objagg, objagg_obj); | 288 | trace_objagg_obj_root_destroy(objagg, objagg_obj); |
203 | objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv); | 289 | objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv); |
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; | ||
204 | } | 329 | } |
205 | 330 | ||
206 | static int objagg_obj_init(struct objagg *objagg, | 331 | static int objagg_obj_init(struct objagg *objagg, |
207 | struct objagg_obj *objagg_obj) | 332 | struct objagg_obj *objagg_obj) |
208 | { | 333 | { |
334 | bool hint_found; | ||
209 | int err; | 335 | int err; |
210 | 336 | ||
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 | |||
211 | /* Try to find if the object can be aggregated under an existing one. */ | 347 | /* Try to find if the object can be aggregated under an existing one. */ |
212 | err = objagg_obj_parent_lookup_assign(objagg, objagg_obj); | 348 | err = objagg_obj_parent_lookup_assign(objagg, objagg_obj); |
213 | if (!err) | 349 | if (!err) |
214 | return 0; | 350 | return 0; |
215 | /* If aggregation is not possible, make the object a root. */ | 351 | /* If aggregation is not possible, make the object a root. */ |
216 | return objagg_obj_root_create(objagg, objagg_obj); | 352 | return objagg_obj_root_create(objagg, objagg_obj, NULL); |
217 | } | 353 | } |
218 | 354 | ||
219 | static void objagg_obj_fini(struct objagg *objagg, | 355 | static void objagg_obj_fini(struct objagg *objagg, |
@@ -349,8 +485,9 @@ EXPORT_SYMBOL(objagg_obj_put); | |||
349 | 485 | ||
350 | /** | 486 | /** |
351 | * objagg_create - creates a new objagg instance | 487 | * objagg_create - creates a new objagg instance |
352 | * @ops: user-specific callbacks | 488 | * @ops: user-specific callbacks |
353 | * @priv: pointer to a private data passed to the ops | 489 | * @objagg_hints: hints, can be NULL |
490 | * @priv: pointer to a private data passed to the ops | ||
354 | * | 491 | * |
355 | * Note: all locking must be provided by the caller. | 492 | * Note: all locking must be provided by the caller. |
356 | * | 493 | * |
@@ -374,18 +511,25 @@ EXPORT_SYMBOL(objagg_obj_put); | |||
374 | * Returns a pointer to newly created objagg instance in case of success, | 511 | * Returns a pointer to newly created objagg instance in case of success, |
375 | * otherwise it returns pointer error using ERR_PTR macro. | 512 | * otherwise it returns pointer error using ERR_PTR macro. |
376 | */ | 513 | */ |
377 | struct objagg *objagg_create(const struct objagg_ops *ops, void *priv) | 514 | struct objagg *objagg_create(const struct objagg_ops *ops, |
515 | struct objagg_hints *objagg_hints, void *priv) | ||
378 | { | 516 | { |
379 | struct objagg *objagg; | 517 | struct objagg *objagg; |
380 | int err; | 518 | int err; |
381 | 519 | ||
382 | if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy || | 520 | if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy || |
383 | !ops->delta_create || !ops->delta_destroy)) | 521 | !ops->delta_check || !ops->delta_create || |
522 | !ops->delta_destroy)) | ||
384 | return ERR_PTR(-EINVAL); | 523 | return ERR_PTR(-EINVAL); |
524 | |||
385 | objagg = kzalloc(sizeof(*objagg), GFP_KERNEL); | 525 | objagg = kzalloc(sizeof(*objagg), GFP_KERNEL); |
386 | if (!objagg) | 526 | if (!objagg) |
387 | return ERR_PTR(-ENOMEM); | 527 | return ERR_PTR(-ENOMEM); |
388 | objagg->ops = ops; | 528 | objagg->ops = ops; |
529 | if (objagg_hints) { | ||
530 | objagg->hints = objagg_hints; | ||
531 | objagg_hints->refcount++; | ||
532 | } | ||
389 | objagg->priv = priv; | 533 | objagg->priv = priv; |
390 | INIT_LIST_HEAD(&objagg->obj_list); | 534 | INIT_LIST_HEAD(&objagg->obj_list); |
391 | 535 | ||
@@ -397,6 +541,8 @@ struct objagg *objagg_create(const struct objagg_ops *ops, void *priv) | |||
397 | if (err) | 541 | if (err) |
398 | goto err_rhashtable_init; | 542 | goto err_rhashtable_init; |
399 | 543 | ||
544 | ida_init(&objagg->root_ida); | ||
545 | |||
400 | trace_objagg_create(objagg); | 546 | trace_objagg_create(objagg); |
401 | return objagg; | 547 | return objagg; |
402 | 548 | ||
@@ -415,8 +561,11 @@ EXPORT_SYMBOL(objagg_create); | |||
415 | void objagg_destroy(struct objagg *objagg) | 561 | void objagg_destroy(struct objagg *objagg) |
416 | { | 562 | { |
417 | trace_objagg_destroy(objagg); | 563 | trace_objagg_destroy(objagg); |
564 | ida_destroy(&objagg->root_ida); | ||
418 | WARN_ON(!list_empty(&objagg->obj_list)); | 565 | WARN_ON(!list_empty(&objagg->obj_list)); |
419 | rhashtable_destroy(&objagg->obj_ht); | 566 | rhashtable_destroy(&objagg->obj_ht); |
567 | if (objagg->hints) | ||
568 | objagg_hints_put(objagg->hints); | ||
420 | kfree(objagg); | 569 | kfree(objagg); |
421 | } | 570 | } |
422 | EXPORT_SYMBOL(objagg_destroy); | 571 | EXPORT_SYMBOL(objagg_destroy); |
@@ -496,6 +645,404 @@ void objagg_stats_put(const struct objagg_stats *objagg_stats) | |||
496 | } | 645 | } |
497 | EXPORT_SYMBOL(objagg_stats_put); | 646 | EXPORT_SYMBOL(objagg_stats_put); |
498 | 647 | ||
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 | |||
745 | if (node->crossed_out) | ||
746 | return 0; | ||
747 | for (j = 0; j < graph->nodes_count; j++) { | ||
748 | if (!objagg_tmp_graph_is_edge(graph, index, j)) | ||
749 | continue; | ||
750 | node = &graph->nodes[j]; | ||
751 | if (node->crossed_out) | ||
752 | continue; | ||
753 | weight += node->objagg_obj->stats.user_count; | ||
754 | } | ||
755 | return weight; | ||
756 | } | ||
757 | |||
758 | static int objagg_tmp_graph_node_max_weight(struct objagg_tmp_graph *graph) | ||
759 | { | ||
760 | unsigned int max_weight = 0; | ||
761 | unsigned int weight; | ||
762 | int max_index = -1; | ||
763 | int i; | ||
764 | |||
765 | for (i = 0; i < graph->nodes_count; i++) { | ||
766 | weight = objagg_tmp_graph_node_weight(graph, i); | ||
767 | if (weight > max_weight) { | ||
768 | max_weight = weight; | ||
769 | max_index = i; | ||
770 | } | ||
771 | } | ||
772 | return max_index; | ||
773 | } | ||
774 | |||
775 | static struct objagg_tmp_graph *objagg_tmp_graph_create(struct objagg *objagg) | ||
776 | { | ||
777 | unsigned int nodes_count = objagg->obj_count; | ||
778 | struct objagg_tmp_graph *graph; | ||
779 | struct objagg_tmp_node *node; | ||
780 | struct objagg_tmp_node *pnode; | ||
781 | struct objagg_obj *objagg_obj; | ||
782 | size_t alloc_size; | ||
783 | int i, j; | ||
784 | |||
785 | graph = kzalloc(sizeof(*graph), GFP_KERNEL); | ||
786 | if (!graph) | ||
787 | return NULL; | ||
788 | |||
789 | graph->nodes = kcalloc(nodes_count, sizeof(*graph->nodes), GFP_KERNEL); | ||
790 | if (!graph->nodes) | ||
791 | goto err_nodes_alloc; | ||
792 | graph->nodes_count = nodes_count; | ||
793 | |||
794 | alloc_size = BITS_TO_LONGS(nodes_count * nodes_count) * | ||
795 | sizeof(unsigned long); | ||
796 | graph->edges = kzalloc(alloc_size, GFP_KERNEL); | ||
797 | if (!graph->edges) | ||
798 | goto err_edges_alloc; | ||
799 | |||
800 | i = 0; | ||
801 | list_for_each_entry(objagg_obj, &objagg->obj_list, list) { | ||
802 | node = &graph->nodes[i++]; | ||
803 | node->objagg_obj = objagg_obj; | ||
804 | } | ||
805 | |||
806 | /* Assemble a temporary graph. Insert edge X->Y in case Y can be | ||
807 | * in delta of X. | ||
808 | */ | ||
809 | for (i = 0; i < nodes_count; i++) { | ||
810 | for (j = 0; j < nodes_count; j++) { | ||
811 | if (i == j) | ||
812 | continue; | ||
813 | pnode = &graph->nodes[i]; | ||
814 | node = &graph->nodes[j]; | ||
815 | if (objagg->ops->delta_check(objagg->priv, | ||
816 | pnode->objagg_obj->obj, | ||
817 | node->objagg_obj->obj)) { | ||
818 | objagg_tmp_graph_edge_set(graph, i, j); | ||
819 | |||
820 | } | ||
821 | } | ||
822 | } | ||
823 | return graph; | ||
824 | |||
825 | err_edges_alloc: | ||
826 | kfree(graph->nodes); | ||
827 | err_nodes_alloc: | ||
828 | kfree(graph); | ||
829 | return NULL; | ||
830 | } | ||
831 | |||
832 | static void objagg_tmp_graph_destroy(struct objagg_tmp_graph *graph) | ||
833 | { | ||
834 | kfree(graph->edges); | ||
835 | kfree(graph->nodes); | ||
836 | kfree(graph); | ||
837 | } | ||
838 | |||
839 | static int | ||
840 | objagg_opt_simple_greedy_fillup_hints(struct objagg_hints *objagg_hints, | ||
841 | struct objagg *objagg) | ||
842 | { | ||
843 | struct objagg_hints_node *hnode, *parent_hnode; | ||
844 | struct objagg_tmp_graph *graph; | ||
845 | struct objagg_tmp_node *node; | ||
846 | int index; | ||
847 | int j; | ||
848 | int err; | ||
849 | |||
850 | graph = objagg_tmp_graph_create(objagg); | ||
851 | if (!graph) | ||
852 | return -ENOMEM; | ||
853 | |||
854 | /* Find the nodes from the ones that can accommodate most users | ||
855 | * and cross them out of the graph. Save them to the hint list. | ||
856 | */ | ||
857 | while ((index = objagg_tmp_graph_node_max_weight(graph)) != -1) { | ||
858 | node = &graph->nodes[index]; | ||
859 | node->crossed_out = true; | ||
860 | hnode = objagg_hints_node_create(objagg_hints, | ||
861 | node->objagg_obj, | ||
862 | objagg->ops->obj_size, | ||
863 | NULL); | ||
864 | if (IS_ERR(hnode)) { | ||
865 | err = PTR_ERR(hnode); | ||
866 | goto out; | ||
867 | } | ||
868 | parent_hnode = hnode; | ||
869 | for (j = 0; j < graph->nodes_count; j++) { | ||
870 | if (!objagg_tmp_graph_is_edge(graph, index, j)) | ||
871 | continue; | ||
872 | node = &graph->nodes[j]; | ||
873 | if (node->crossed_out) | ||
874 | continue; | ||
875 | node->crossed_out = true; | ||
876 | hnode = objagg_hints_node_create(objagg_hints, | ||
877 | node->objagg_obj, | ||
878 | objagg->ops->obj_size, | ||
879 | parent_hnode); | ||
880 | if (IS_ERR(hnode)) { | ||
881 | err = PTR_ERR(hnode); | ||
882 | goto out; | ||
883 | } | ||
884 | } | ||
885 | } | ||
886 | |||
887 | err = 0; | ||
888 | out: | ||
889 | objagg_tmp_graph_destroy(graph); | ||
890 | return err; | ||
891 | } | ||
892 | |||
893 | struct objagg_opt_algo { | ||
894 | int (*fillup_hints)(struct objagg_hints *objagg_hints, | ||
895 | struct objagg *objagg); | ||
896 | }; | ||
897 | |||
898 | static const struct objagg_opt_algo objagg_opt_simple_greedy = { | ||
899 | .fillup_hints = objagg_opt_simple_greedy_fillup_hints, | ||
900 | }; | ||
901 | |||
902 | |||
903 | static const struct objagg_opt_algo *objagg_opt_algos[] = { | ||
904 | [OBJAGG_OPT_ALGO_SIMPLE_GREEDY] = &objagg_opt_simple_greedy, | ||
905 | }; | ||
906 | |||
907 | static int objagg_hints_obj_cmp(struct rhashtable_compare_arg *arg, | ||
908 | const void *obj) | ||
909 | { | ||
910 | struct rhashtable *ht = arg->ht; | ||
911 | struct objagg_hints *objagg_hints = | ||
912 | container_of(ht, struct objagg_hints, node_ht); | ||
913 | const struct objagg_ops *ops = objagg_hints->ops; | ||
914 | const char *ptr = obj; | ||
915 | |||
916 | ptr += ht->p.key_offset; | ||
917 | return ops->hints_obj_cmp ? ops->hints_obj_cmp(ptr, arg->key) : | ||
918 | memcmp(ptr, arg->key, ht->p.key_len); | ||
919 | } | ||
920 | |||
921 | /** | ||
922 | * objagg_hints_get - obtains hints instance | ||
923 | * @objagg: objagg instance | ||
924 | * @opt_algo_type: type of hints finding algorithm | ||
925 | * | ||
926 | * Note: all locking must be provided by the caller. | ||
927 | * | ||
928 | * According to the algo type, the existing objects of objagg instance | ||
929 | * are going to be went-through to assemble an optimal tree. We call this | ||
930 | * tree hints. These hints can be later on used for creation of | ||
931 | * a new objagg instance. There, the future object creations are going | ||
932 | * to be consulted with these hints in order to find out, where exactly | ||
933 | * the new object should be put as a root or delta. | ||
934 | * | ||
935 | * Returns a pointer to hints instance in case of success, | ||
936 | * otherwise it returns pointer error using ERR_PTR macro. | ||
937 | */ | ||
938 | struct objagg_hints *objagg_hints_get(struct objagg *objagg, | ||
939 | enum objagg_opt_algo_type opt_algo_type) | ||
940 | { | ||
941 | const struct objagg_opt_algo *algo = objagg_opt_algos[opt_algo_type]; | ||
942 | struct objagg_hints *objagg_hints; | ||
943 | int err; | ||
944 | |||
945 | objagg_hints = kzalloc(sizeof(*objagg_hints), GFP_KERNEL); | ||
946 | if (!objagg_hints) | ||
947 | return ERR_PTR(-ENOMEM); | ||
948 | |||
949 | objagg_hints->ops = objagg->ops; | ||
950 | objagg_hints->refcount = 1; | ||
951 | |||
952 | INIT_LIST_HEAD(&objagg_hints->node_list); | ||
953 | |||
954 | objagg_hints->ht_params.key_len = objagg->ops->obj_size; | ||
955 | objagg_hints->ht_params.key_offset = | ||
956 | offsetof(struct objagg_hints_node, obj); | ||
957 | objagg_hints->ht_params.head_offset = | ||
958 | offsetof(struct objagg_hints_node, ht_node); | ||
959 | objagg_hints->ht_params.obj_cmpfn = objagg_hints_obj_cmp; | ||
960 | |||
961 | err = rhashtable_init(&objagg_hints->node_ht, &objagg_hints->ht_params); | ||
962 | if (err) | ||
963 | goto err_rhashtable_init; | ||
964 | |||
965 | err = algo->fillup_hints(objagg_hints, objagg); | ||
966 | if (err) | ||
967 | goto err_fillup_hints; | ||
968 | |||
969 | if (WARN_ON(objagg_hints->node_count != objagg->obj_count)) | ||
970 | goto err_node_count_check; | ||
971 | |||
972 | return objagg_hints; | ||
973 | |||
974 | err_node_count_check: | ||
975 | err_fillup_hints: | ||
976 | objagg_hints_flush(objagg_hints); | ||
977 | rhashtable_destroy(&objagg_hints->node_ht); | ||
978 | err_rhashtable_init: | ||
979 | kfree(objagg_hints); | ||
980 | return ERR_PTR(err); | ||
981 | } | ||
982 | EXPORT_SYMBOL(objagg_hints_get); | ||
983 | |||
984 | /** | ||
985 | * objagg_hints_put - puts hints instance | ||
986 | * @objagg_hints: objagg hints instance | ||
987 | * | ||
988 | * Note: all locking must be provided by the caller. | ||
989 | */ | ||
990 | void objagg_hints_put(struct objagg_hints *objagg_hints) | ||
991 | { | ||
992 | if (--objagg_hints->refcount) | ||
993 | return; | ||
994 | objagg_hints_flush(objagg_hints); | ||
995 | rhashtable_destroy(&objagg_hints->node_ht); | ||
996 | kfree(objagg_hints); | ||
997 | } | ||
998 | EXPORT_SYMBOL(objagg_hints_put); | ||
999 | |||
1000 | /** | ||
1001 | * objagg_hints_stats_get - obtains stats of the hints instance | ||
1002 | * @objagg_hints: hints instance | ||
1003 | * | ||
1004 | * Note: all locking must be provided by the caller. | ||
1005 | * | ||
1006 | * The returned structure contains statistics of all objects | ||
1007 | * currently in use, ordered by following rules: | ||
1008 | * 1) Root objects are always on lower indexes than the rest. | ||
1009 | * 2) Objects with higher delta user count are always on lower | ||
1010 | * indexes. | ||
1011 | * 3) In case multiple objects have the same delta user count, | ||
1012 | * the objects are ordered by user count. | ||
1013 | * | ||
1014 | * Returns a pointer to stats instance in case of success, | ||
1015 | * otherwise it returns pointer error using ERR_PTR macro. | ||
1016 | */ | ||
1017 | const struct objagg_stats * | ||
1018 | objagg_hints_stats_get(struct objagg_hints *objagg_hints) | ||
1019 | { | ||
1020 | struct objagg_stats *objagg_stats; | ||
1021 | struct objagg_hints_node *hnode; | ||
1022 | int i; | ||
1023 | |||
1024 | objagg_stats = kzalloc(struct_size(objagg_stats, stats_info, | ||
1025 | objagg_hints->node_count), | ||
1026 | GFP_KERNEL); | ||
1027 | if (!objagg_stats) | ||
1028 | return ERR_PTR(-ENOMEM); | ||
1029 | |||
1030 | i = 0; | ||
1031 | list_for_each_entry(hnode, &objagg_hints->node_list, list) { | ||
1032 | memcpy(&objagg_stats->stats_info[i], &hnode->stats_info, | ||
1033 | sizeof(objagg_stats->stats_info[0])); | ||
1034 | i++; | ||
1035 | } | ||
1036 | objagg_stats->stats_info_count = i; | ||
1037 | |||
1038 | sort(objagg_stats->stats_info, objagg_stats->stats_info_count, | ||
1039 | sizeof(struct objagg_obj_stats_info), | ||
1040 | objagg_stats_info_sort_cmp_func, NULL); | ||
1041 | |||
1042 | return objagg_stats; | ||
1043 | } | ||
1044 | EXPORT_SYMBOL(objagg_hints_stats_get); | ||
1045 | |||
499 | MODULE_LICENSE("Dual BSD/GPL"); | 1046 | MODULE_LICENSE("Dual BSD/GPL"); |
500 | MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); | 1047 | MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); |
501 | MODULE_DESCRIPTION("Object aggregation manager"); | 1048 | MODULE_DESCRIPTION("Object aggregation manager"); |