diff options
author | Jiri Pirko <jiri@mellanox.com> | 2019-02-07 06:22:46 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-02-08 18:02:49 -0500 |
commit | 9069a3817d82b01b3a55da382c774e3575946130 (patch) | |
tree | 5b8aca6dbaf69345aec08b325761cdf9162ebc9b | |
parent | bb72e68bd1f2a86c37a990e9d905f34b712ae2b6 (diff) |
lib: objagg: implement optimization hints assembly and use hints for object creation
Implement simple greedy algo to find more optimized root-delta tree for
a given objagg instance. This "hints" can be used by a driver to:
1) check if the hints are better (driver's choice) than the original
objagg tree. Driver does comparison of objagg stats and hints stats.
2) use the hints to create a new objagg instance which will construct
the root-delta tree according to the passed hints. Currently, only a
simple greedy algorithm is implemented. Basically it finds the roots
according to the maximal possible user count including deltas.
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c | 37 | ||||
-rw-r--r-- | include/linux/objagg.h | 20 | ||||
-rw-r--r-- | lib/objagg.c | 573 | ||||
-rw-r--r-- | lib/test_objagg.c | 194 |
4 files changed, 802 insertions, 22 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c index 2941967e1cc5..302070a74f2e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c | |||
@@ -1200,6 +1200,32 @@ mlxsw_sp_acl_erp_delta_fill(const struct mlxsw_sp_acl_erp_key *parent_key, | |||
1200 | return 0; | 1200 | return 0; |
1201 | } | 1201 | } |
1202 | 1202 | ||
1203 | static bool mlxsw_sp_acl_erp_delta_check(void *priv, const void *parent_obj, | ||
1204 | const void *obj) | ||
1205 | { | ||
1206 | const struct mlxsw_sp_acl_erp_key *parent_key = parent_obj; | ||
1207 | const struct mlxsw_sp_acl_erp_key *key = obj; | ||
1208 | u16 delta_start; | ||
1209 | u8 delta_mask; | ||
1210 | int err; | ||
1211 | |||
1212 | err = mlxsw_sp_acl_erp_delta_fill(parent_key, key, | ||
1213 | &delta_start, &delta_mask); | ||
1214 | return err ? false : true; | ||
1215 | } | ||
1216 | |||
1217 | static int mlxsw_sp_acl_erp_hints_obj_cmp(const void *obj1, const void *obj2) | ||
1218 | { | ||
1219 | const struct mlxsw_sp_acl_erp_key *key1 = obj1; | ||
1220 | const struct mlxsw_sp_acl_erp_key *key2 = obj2; | ||
1221 | |||
1222 | /* For hints purposes, two objects are considered equal | ||
1223 | * in case the masks are the same. Does not matter what | ||
1224 | * the "ctcam" value is. | ||
1225 | */ | ||
1226 | return memcmp(key1->mask, key2->mask, sizeof(key1->mask)); | ||
1227 | } | ||
1228 | |||
1203 | static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj, | 1229 | static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj, |
1204 | void *obj) | 1230 | void *obj) |
1205 | { | 1231 | { |
@@ -1254,12 +1280,17 @@ static void mlxsw_sp_acl_erp_delta_destroy(void *priv, void *delta_priv) | |||
1254 | kfree(delta); | 1280 | kfree(delta); |
1255 | } | 1281 | } |
1256 | 1282 | ||
1257 | static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj) | 1283 | static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj, |
1284 | unsigned int root_id) | ||
1258 | { | 1285 | { |
1259 | struct mlxsw_sp_acl_atcam_region *aregion = priv; | 1286 | struct mlxsw_sp_acl_atcam_region *aregion = priv; |
1260 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; | 1287 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1261 | struct mlxsw_sp_acl_erp_key *key = obj; | 1288 | struct mlxsw_sp_acl_erp_key *key = obj; |
1262 | 1289 | ||
1290 | if (!key->ctcam && | ||
1291 | root_id != OBJAGG_OBJ_ROOT_ID_INVALID && | ||
1292 | root_id >= MLXSW_SP_ACL_ERP_MAX_PER_REGION) | ||
1293 | return ERR_PTR(-ENOBUFS); | ||
1263 | return erp_table->ops->erp_create(erp_table, key); | 1294 | return erp_table->ops->erp_create(erp_table, key); |
1264 | } | 1295 | } |
1265 | 1296 | ||
@@ -1273,6 +1304,8 @@ static void mlxsw_sp_acl_erp_root_destroy(void *priv, void *root_priv) | |||
1273 | 1304 | ||
1274 | static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = { | 1305 | static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = { |
1275 | .obj_size = sizeof(struct mlxsw_sp_acl_erp_key), | 1306 | .obj_size = sizeof(struct mlxsw_sp_acl_erp_key), |
1307 | .delta_check = mlxsw_sp_acl_erp_delta_check, | ||
1308 | .hints_obj_cmp = mlxsw_sp_acl_erp_hints_obj_cmp, | ||
1276 | .delta_create = mlxsw_sp_acl_erp_delta_create, | 1309 | .delta_create = mlxsw_sp_acl_erp_delta_create, |
1277 | .delta_destroy = mlxsw_sp_acl_erp_delta_destroy, | 1310 | .delta_destroy = mlxsw_sp_acl_erp_delta_destroy, |
1278 | .root_create = mlxsw_sp_acl_erp_root_create, | 1311 | .root_create = mlxsw_sp_acl_erp_root_create, |
@@ -1290,7 +1323,7 @@ mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion) | |||
1290 | return ERR_PTR(-ENOMEM); | 1323 | return ERR_PTR(-ENOMEM); |
1291 | 1324 | ||
1292 | erp_table->objagg = objagg_create(&mlxsw_sp_acl_erp_objagg_ops, | 1325 | erp_table->objagg = objagg_create(&mlxsw_sp_acl_erp_objagg_ops, |
1293 | aregion); | 1326 | NULL, aregion); |
1294 | if (IS_ERR(erp_table->objagg)) { | 1327 | if (IS_ERR(erp_table->objagg)) { |
1295 | err = PTR_ERR(erp_table->objagg); | 1328 | err = PTR_ERR(erp_table->objagg); |
1296 | goto err_objagg_create; | 1329 | goto err_objagg_create; |
diff --git a/include/linux/objagg.h b/include/linux/objagg.h index 34f38c186ea0..a675286df1af 100644 --- a/include/linux/objagg.h +++ b/include/linux/objagg.h | |||
@@ -6,14 +6,19 @@ | |||
6 | 6 | ||
7 | struct objagg_ops { | 7 | struct objagg_ops { |
8 | size_t obj_size; | 8 | size_t obj_size; |
9 | bool (*delta_check)(void *priv, const void *parent_obj, | ||
10 | const void *obj); | ||
11 | int (*hints_obj_cmp)(const void *obj1, const void *obj2); | ||
9 | void * (*delta_create)(void *priv, void *parent_obj, void *obj); | 12 | void * (*delta_create)(void *priv, void *parent_obj, void *obj); |
10 | void (*delta_destroy)(void *priv, void *delta_priv); | 13 | void (*delta_destroy)(void *priv, void *delta_priv); |
11 | void * (*root_create)(void *priv, void *obj); | 14 | void * (*root_create)(void *priv, void *obj, unsigned int root_id); |
15 | #define OBJAGG_OBJ_ROOT_ID_INVALID UINT_MAX | ||
12 | void (*root_destroy)(void *priv, void *root_priv); | 16 | void (*root_destroy)(void *priv, void *root_priv); |
13 | }; | 17 | }; |
14 | 18 | ||
15 | struct objagg; | 19 | struct objagg; |
16 | struct objagg_obj; | 20 | struct objagg_obj; |
21 | struct objagg_hints; | ||
17 | 22 | ||
18 | const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj); | 23 | const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj); |
19 | const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj); | 24 | const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj); |
@@ -21,7 +26,8 @@ const void *objagg_obj_raw(const struct objagg_obj *objagg_obj); | |||
21 | 26 | ||
22 | struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj); | 27 | struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj); |
23 | void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj); | 28 | void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj); |
24 | struct objagg *objagg_create(const struct objagg_ops *ops, void *priv); | 29 | struct objagg *objagg_create(const struct objagg_ops *ops, |
30 | struct objagg_hints *hints, void *priv); | ||
25 | void objagg_destroy(struct objagg *objagg); | 31 | void objagg_destroy(struct objagg *objagg); |
26 | 32 | ||
27 | struct objagg_obj_stats { | 33 | struct objagg_obj_stats { |
@@ -43,4 +49,14 @@ struct objagg_stats { | |||
43 | const struct objagg_stats *objagg_stats_get(struct objagg *objagg); | 49 | const struct objagg_stats *objagg_stats_get(struct objagg *objagg); |
44 | void objagg_stats_put(const struct objagg_stats *objagg_stats); | 50 | void objagg_stats_put(const struct objagg_stats *objagg_stats); |
45 | 51 | ||
52 | enum objagg_opt_algo_type { | ||
53 | OBJAGG_OPT_ALGO_SIMPLE_GREEDY, | ||
54 | }; | ||
55 | |||
56 | struct objagg_hints *objagg_hints_get(struct objagg *objagg, | ||
57 | enum objagg_opt_algo_type opt_algo_type); | ||
58 | void objagg_hints_put(struct objagg_hints *objagg_hints); | ||
59 | const struct objagg_stats * | ||
60 | objagg_hints_stats_get(struct objagg_hints *objagg_hints); | ||
61 | |||
46 | #endif | 62 | #endif |
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"); |
diff --git a/lib/test_objagg.c b/lib/test_objagg.c index ab57144bb0cd..3744573b6365 100644 --- a/lib/test_objagg.c +++ b/lib/test_objagg.c | |||
@@ -87,6 +87,15 @@ static void world_obj_put(struct world *world, struct objagg *objagg, | |||
87 | 87 | ||
88 | #define MAX_KEY_ID_DIFF 5 | 88 | #define MAX_KEY_ID_DIFF 5 |
89 | 89 | ||
90 | static bool delta_check(void *priv, const void *parent_obj, const void *obj) | ||
91 | { | ||
92 | const struct tokey *parent_key = parent_obj; | ||
93 | const struct tokey *key = obj; | ||
94 | int diff = key->id - parent_key->id; | ||
95 | |||
96 | return diff >= 0 && diff <= MAX_KEY_ID_DIFF; | ||
97 | } | ||
98 | |||
90 | static void *delta_create(void *priv, void *parent_obj, void *obj) | 99 | static void *delta_create(void *priv, void *parent_obj, void *obj) |
91 | { | 100 | { |
92 | struct tokey *parent_key = parent_obj; | 101 | struct tokey *parent_key = parent_obj; |
@@ -95,7 +104,7 @@ static void *delta_create(void *priv, void *parent_obj, void *obj) | |||
95 | int diff = key->id - parent_key->id; | 104 | int diff = key->id - parent_key->id; |
96 | struct delta *delta; | 105 | struct delta *delta; |
97 | 106 | ||
98 | if (diff < 0 || diff > MAX_KEY_ID_DIFF) | 107 | if (!delta_check(priv, parent_obj, obj)) |
99 | return ERR_PTR(-EINVAL); | 108 | return ERR_PTR(-EINVAL); |
100 | 109 | ||
101 | delta = kzalloc(sizeof(*delta), GFP_KERNEL); | 110 | delta = kzalloc(sizeof(*delta), GFP_KERNEL); |
@@ -115,7 +124,7 @@ static void delta_destroy(void *priv, void *delta_priv) | |||
115 | kfree(delta); | 124 | kfree(delta); |
116 | } | 125 | } |
117 | 126 | ||
118 | static void *root_create(void *priv, void *obj) | 127 | static void *root_create(void *priv, void *obj, unsigned int id) |
119 | { | 128 | { |
120 | struct world *world = priv; | 129 | struct world *world = priv; |
121 | struct tokey *key = obj; | 130 | struct tokey *key = obj; |
@@ -268,6 +277,12 @@ stats_put: | |||
268 | return err; | 277 | return err; |
269 | } | 278 | } |
270 | 279 | ||
280 | static bool delta_check_dummy(void *priv, const void *parent_obj, | ||
281 | const void *obj) | ||
282 | { | ||
283 | return false; | ||
284 | } | ||
285 | |||
271 | static void *delta_create_dummy(void *priv, void *parent_obj, void *obj) | 286 | static void *delta_create_dummy(void *priv, void *parent_obj, void *obj) |
272 | { | 287 | { |
273 | return ERR_PTR(-EOPNOTSUPP); | 288 | return ERR_PTR(-EOPNOTSUPP); |
@@ -279,6 +294,7 @@ static void delta_destroy_dummy(void *priv, void *delta_priv) | |||
279 | 294 | ||
280 | static const struct objagg_ops nodelta_ops = { | 295 | static const struct objagg_ops nodelta_ops = { |
281 | .obj_size = sizeof(struct tokey), | 296 | .obj_size = sizeof(struct tokey), |
297 | .delta_check = delta_check_dummy, | ||
282 | .delta_create = delta_create_dummy, | 298 | .delta_create = delta_create_dummy, |
283 | .delta_destroy = delta_destroy_dummy, | 299 | .delta_destroy = delta_destroy_dummy, |
284 | .root_create = root_create, | 300 | .root_create = root_create, |
@@ -292,7 +308,7 @@ static int test_nodelta(void) | |||
292 | int i; | 308 | int i; |
293 | int err; | 309 | int err; |
294 | 310 | ||
295 | objagg = objagg_create(&nodelta_ops, &world); | 311 | objagg = objagg_create(&nodelta_ops, NULL, &world); |
296 | if (IS_ERR(objagg)) | 312 | if (IS_ERR(objagg)) |
297 | return PTR_ERR(objagg); | 313 | return PTR_ERR(objagg); |
298 | 314 | ||
@@ -357,6 +373,7 @@ err_stats_second_zero: | |||
357 | 373 | ||
358 | static const struct objagg_ops delta_ops = { | 374 | static const struct objagg_ops delta_ops = { |
359 | .obj_size = sizeof(struct tokey), | 375 | .obj_size = sizeof(struct tokey), |
376 | .delta_check = delta_check, | ||
360 | .delta_create = delta_create, | 377 | .delta_create = delta_create, |
361 | .delta_destroy = delta_destroy, | 378 | .delta_destroy = delta_destroy, |
362 | .root_create = root_create, | 379 | .root_create = root_create, |
@@ -793,7 +810,7 @@ static int test_delta(void) | |||
793 | int i; | 810 | int i; |
794 | int err; | 811 | int err; |
795 | 812 | ||
796 | objagg = objagg_create(&delta_ops, &world); | 813 | objagg = objagg_create(&delta_ops, NULL, &world); |
797 | if (IS_ERR(objagg)) | 814 | if (IS_ERR(objagg)) |
798 | return PTR_ERR(objagg); | 815 | return PTR_ERR(objagg); |
799 | 816 | ||
@@ -815,6 +832,170 @@ err_do_action_item: | |||
815 | return err; | 832 | return err; |
816 | } | 833 | } |
817 | 834 | ||
835 | struct hints_case { | ||
836 | const unsigned int *key_ids; | ||
837 | size_t key_ids_count; | ||
838 | struct expect_stats expect_stats; | ||
839 | struct expect_stats expect_stats_hints; | ||
840 | }; | ||
841 | |||
842 | static const unsigned int hints_case_key_ids[] = { | ||
843 | 1, 7, 3, 5, 3, 1, 30, 8, 8, 5, 6, 8, | ||
844 | }; | ||
845 | |||
846 | static const struct hints_case hints_case = { | ||
847 | .key_ids = hints_case_key_ids, | ||
848 | .key_ids_count = ARRAY_SIZE(hints_case_key_ids), | ||
849 | .expect_stats = | ||
850 | EXPECT_STATS(7, ROOT(1, 2, 7), ROOT(7, 1, 4), ROOT(30, 1, 1), | ||
851 | DELTA(8, 3), DELTA(3, 2), | ||
852 | DELTA(5, 2), DELTA(6, 1)), | ||
853 | .expect_stats_hints = | ||
854 | EXPECT_STATS(7, ROOT(3, 2, 9), ROOT(1, 2, 2), ROOT(30, 1, 1), | ||
855 | DELTA(8, 3), DELTA(5, 2), | ||
856 | DELTA(6, 1), DELTA(7, 1)), | ||
857 | }; | ||
858 | |||
859 | static void __pr_debug_stats(const struct objagg_stats *stats) | ||
860 | { | ||
861 | int i; | ||
862 | |||
863 | for (i = 0; i < stats->stats_info_count; i++) | ||
864 | pr_debug("Stat index %d key %u: u %d, d %d, %s\n", i, | ||
865 | obj_to_key_id(stats->stats_info[i].objagg_obj), | ||
866 | stats->stats_info[i].stats.user_count, | ||
867 | stats->stats_info[i].stats.delta_user_count, | ||
868 | stats->stats_info[i].is_root ? "root" : "noroot"); | ||
869 | } | ||
870 | |||
871 | static void pr_debug_stats(struct objagg *objagg) | ||
872 | { | ||
873 | const struct objagg_stats *stats; | ||
874 | |||
875 | stats = objagg_stats_get(objagg); | ||
876 | if (IS_ERR(stats)) | ||
877 | return; | ||
878 | __pr_debug_stats(stats); | ||
879 | objagg_stats_put(stats); | ||
880 | } | ||
881 | |||
882 | static void pr_debug_hints_stats(struct objagg_hints *objagg_hints) | ||
883 | { | ||
884 | const struct objagg_stats *stats; | ||
885 | |||
886 | stats = objagg_hints_stats_get(objagg_hints); | ||
887 | if (IS_ERR(stats)) | ||
888 | return; | ||
889 | __pr_debug_stats(stats); | ||
890 | objagg_stats_put(stats); | ||
891 | } | ||
892 | |||
893 | static int check_expect_hints_stats(struct objagg_hints *objagg_hints, | ||
894 | const struct expect_stats *expect_stats, | ||
895 | const char **errmsg) | ||
896 | { | ||
897 | const struct objagg_stats *stats; | ||
898 | int err; | ||
899 | |||
900 | stats = objagg_hints_stats_get(objagg_hints); | ||
901 | if (IS_ERR(stats)) | ||
902 | return PTR_ERR(stats); | ||
903 | err = __check_expect_stats(stats, expect_stats, errmsg); | ||
904 | objagg_stats_put(stats); | ||
905 | return err; | ||
906 | } | ||
907 | |||
908 | static int test_hints_case(const struct hints_case *hints_case) | ||
909 | { | ||
910 | struct objagg_obj *objagg_obj; | ||
911 | struct objagg_hints *hints; | ||
912 | struct world world2 = {}; | ||
913 | struct world world = {}; | ||
914 | struct objagg *objagg2; | ||
915 | struct objagg *objagg; | ||
916 | const char *errmsg; | ||
917 | int i; | ||
918 | int err; | ||
919 | |||
920 | objagg = objagg_create(&delta_ops, NULL, &world); | ||
921 | if (IS_ERR(objagg)) | ||
922 | return PTR_ERR(objagg); | ||
923 | |||
924 | for (i = 0; i < hints_case->key_ids_count; i++) { | ||
925 | objagg_obj = world_obj_get(&world, objagg, | ||
926 | hints_case->key_ids[i]); | ||
927 | if (IS_ERR(objagg_obj)) { | ||
928 | err = PTR_ERR(objagg_obj); | ||
929 | goto err_world_obj_get; | ||
930 | } | ||
931 | } | ||
932 | |||
933 | pr_debug_stats(objagg); | ||
934 | err = check_expect_stats(objagg, &hints_case->expect_stats, &errmsg); | ||
935 | if (err) { | ||
936 | pr_err("Stats: %s\n", errmsg); | ||
937 | goto err_check_expect_stats; | ||
938 | } | ||
939 | |||
940 | hints = objagg_hints_get(objagg, OBJAGG_OPT_ALGO_SIMPLE_GREEDY); | ||
941 | if (IS_ERR(hints)) { | ||
942 | err = PTR_ERR(hints); | ||
943 | goto err_hints_get; | ||
944 | } | ||
945 | |||
946 | pr_debug_hints_stats(hints); | ||
947 | err = check_expect_hints_stats(hints, &hints_case->expect_stats_hints, | ||
948 | &errmsg); | ||
949 | if (err) { | ||
950 | pr_err("Hints stats: %s\n", errmsg); | ||
951 | goto err_check_expect_hints_stats; | ||
952 | } | ||
953 | |||
954 | objagg2 = objagg_create(&delta_ops, hints, &world2); | ||
955 | if (IS_ERR(objagg)) | ||
956 | return PTR_ERR(objagg); | ||
957 | |||
958 | for (i = 0; i < hints_case->key_ids_count; i++) { | ||
959 | objagg_obj = world_obj_get(&world2, objagg2, | ||
960 | hints_case->key_ids[i]); | ||
961 | if (IS_ERR(objagg_obj)) { | ||
962 | err = PTR_ERR(objagg_obj); | ||
963 | goto err_world2_obj_get; | ||
964 | } | ||
965 | } | ||
966 | |||
967 | pr_debug_stats(objagg2); | ||
968 | err = check_expect_stats(objagg2, &hints_case->expect_stats_hints, | ||
969 | &errmsg); | ||
970 | if (err) { | ||
971 | pr_err("Stats2: %s\n", errmsg); | ||
972 | goto err_check_expect_stats2; | ||
973 | } | ||
974 | |||
975 | err = 0; | ||
976 | |||
977 | err_check_expect_stats2: | ||
978 | err_world2_obj_get: | ||
979 | for (i--; i >= 0; i--) | ||
980 | world_obj_put(&world2, objagg, hints_case->key_ids[i]); | ||
981 | objagg_hints_put(hints); | ||
982 | objagg_destroy(objagg2); | ||
983 | i = hints_case->key_ids_count; | ||
984 | err_check_expect_hints_stats: | ||
985 | err_hints_get: | ||
986 | err_check_expect_stats: | ||
987 | err_world_obj_get: | ||
988 | for (i--; i >= 0; i--) | ||
989 | world_obj_put(&world, objagg, hints_case->key_ids[i]); | ||
990 | |||
991 | objagg_destroy(objagg); | ||
992 | return err; | ||
993 | } | ||
994 | static int test_hints(void) | ||
995 | { | ||
996 | return test_hints_case(&hints_case); | ||
997 | } | ||
998 | |||
818 | static int __init test_objagg_init(void) | 999 | static int __init test_objagg_init(void) |
819 | { | 1000 | { |
820 | int err; | 1001 | int err; |
@@ -822,7 +1003,10 @@ static int __init test_objagg_init(void) | |||
822 | err = test_nodelta(); | 1003 | err = test_nodelta(); |
823 | if (err) | 1004 | if (err) |
824 | return err; | 1005 | return err; |
825 | return test_delta(); | 1006 | err = test_delta(); |
1007 | if (err) | ||
1008 | return err; | ||
1009 | return test_hints(); | ||
826 | } | 1010 | } |
827 | 1011 | ||
828 | static void __exit test_objagg_exit(void) | 1012 | static void __exit test_objagg_exit(void) |