forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									bb72e68bd1
								
							
						
					
					
						commit
						9069a3817d
					
				
					 4 changed files with 803 additions and 23 deletions
				
			
		|  | @ -1200,6 +1200,32 @@ mlxsw_sp_acl_erp_delta_fill(const struct mlxsw_sp_acl_erp_key *parent_key, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static bool mlxsw_sp_acl_erp_delta_check(void *priv, const void *parent_obj, | ||||
| 					 const void *obj) | ||||
| { | ||||
| 	const struct mlxsw_sp_acl_erp_key *parent_key = parent_obj; | ||||
| 	const struct mlxsw_sp_acl_erp_key *key = obj; | ||||
| 	u16 delta_start; | ||||
| 	u8 delta_mask; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = mlxsw_sp_acl_erp_delta_fill(parent_key, key, | ||||
| 					  &delta_start, &delta_mask); | ||||
| 	return err ? false : true; | ||||
| } | ||||
| 
 | ||||
| static int mlxsw_sp_acl_erp_hints_obj_cmp(const void *obj1, const void *obj2) | ||||
| { | ||||
| 	const struct mlxsw_sp_acl_erp_key *key1 = obj1; | ||||
| 	const struct mlxsw_sp_acl_erp_key *key2 = obj2; | ||||
| 
 | ||||
| 	/* For hints purposes, two objects are considered equal
 | ||||
| 	 * in case the masks are the same. Does not matter what | ||||
| 	 * the "ctcam" value is. | ||||
| 	 */ | ||||
| 	return memcmp(key1->mask, key2->mask, sizeof(key1->mask)); | ||||
| } | ||||
| 
 | ||||
| static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj, | ||||
| 					   void *obj) | ||||
| { | ||||
|  | @ -1254,12 +1280,17 @@ static void mlxsw_sp_acl_erp_delta_destroy(void *priv, void *delta_priv) | |||
| 	kfree(delta); | ||||
| } | ||||
| 
 | ||||
| static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj) | ||||
| static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj, | ||||
| 					  unsigned int root_id) | ||||
| { | ||||
| 	struct mlxsw_sp_acl_atcam_region *aregion = priv; | ||||
| 	struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; | ||||
| 	struct mlxsw_sp_acl_erp_key *key = obj; | ||||
| 
 | ||||
| 	if (!key->ctcam && | ||||
| 	    root_id != OBJAGG_OBJ_ROOT_ID_INVALID && | ||||
| 	    root_id >= MLXSW_SP_ACL_ERP_MAX_PER_REGION) | ||||
| 		return ERR_PTR(-ENOBUFS); | ||||
| 	return erp_table->ops->erp_create(erp_table, key); | ||||
| } | ||||
| 
 | ||||
|  | @ -1273,6 +1304,8 @@ static void mlxsw_sp_acl_erp_root_destroy(void *priv, void *root_priv) | |||
| 
 | ||||
| static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = { | ||||
| 	.obj_size = sizeof(struct mlxsw_sp_acl_erp_key), | ||||
| 	.delta_check = mlxsw_sp_acl_erp_delta_check, | ||||
| 	.hints_obj_cmp = mlxsw_sp_acl_erp_hints_obj_cmp, | ||||
| 	.delta_create = mlxsw_sp_acl_erp_delta_create, | ||||
| 	.delta_destroy = mlxsw_sp_acl_erp_delta_destroy, | ||||
| 	.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) | |||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	erp_table->objagg = objagg_create(&mlxsw_sp_acl_erp_objagg_ops, | ||||
| 					  aregion); | ||||
| 					  NULL, aregion); | ||||
| 	if (IS_ERR(erp_table->objagg)) { | ||||
| 		err = PTR_ERR(erp_table->objagg); | ||||
| 		goto err_objagg_create; | ||||
|  |  | |||
|  | @ -6,14 +6,19 @@ | |||
| 
 | ||||
| struct objagg_ops { | ||||
| 	size_t obj_size; | ||||
| 	bool (*delta_check)(void *priv, const void *parent_obj, | ||||
| 			    const void *obj); | ||||
| 	int (*hints_obj_cmp)(const void *obj1, const void *obj2); | ||||
| 	void * (*delta_create)(void *priv, void *parent_obj, void *obj); | ||||
| 	void (*delta_destroy)(void *priv, void *delta_priv); | ||||
| 	void * (*root_create)(void *priv, void *obj); | ||||
| 	void * (*root_create)(void *priv, void *obj, unsigned int root_id); | ||||
| #define OBJAGG_OBJ_ROOT_ID_INVALID UINT_MAX | ||||
| 	void (*root_destroy)(void *priv, void *root_priv); | ||||
| }; | ||||
| 
 | ||||
| struct objagg; | ||||
| struct objagg_obj; | ||||
| struct objagg_hints; | ||||
| 
 | ||||
| const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj); | ||||
| 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); | |||
| 
 | ||||
| struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj); | ||||
| void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj); | ||||
| struct objagg *objagg_create(const struct objagg_ops *ops, void *priv); | ||||
| struct objagg *objagg_create(const struct objagg_ops *ops, | ||||
| 			     struct objagg_hints *hints, void *priv); | ||||
| void objagg_destroy(struct objagg *objagg); | ||||
| 
 | ||||
| struct objagg_obj_stats { | ||||
|  | @ -43,4 +49,14 @@ struct objagg_stats { | |||
| const struct objagg_stats *objagg_stats_get(struct objagg *objagg); | ||||
| void objagg_stats_put(const struct objagg_stats *objagg_stats); | ||||
| 
 | ||||
| enum objagg_opt_algo_type { | ||||
| 	OBJAGG_OPT_ALGO_SIMPLE_GREEDY, | ||||
| }; | ||||
| 
 | ||||
| struct objagg_hints *objagg_hints_get(struct objagg *objagg, | ||||
| 				      enum objagg_opt_algo_type opt_algo_type); | ||||
| void objagg_hints_put(struct objagg_hints *objagg_hints); | ||||
| const struct objagg_stats * | ||||
| objagg_hints_stats_get(struct objagg_hints *objagg_hints); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										575
									
								
								lib/objagg.c
									
									
									
									
									
								
							
							
						
						
									
										575
									
								
								lib/objagg.c
									
									
									
									
									
								
							|  | @ -4,6 +4,7 @@ | |||
| #include <linux/module.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/rhashtable.h> | ||||
| #include <linux/idr.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/sort.h> | ||||
| #include <linux/objagg.h> | ||||
|  | @ -11,6 +12,34 @@ | |||
| #define CREATE_TRACE_POINTS | ||||
| #include <trace/events/objagg.h> | ||||
| 
 | ||||
| struct objagg_hints { | ||||
| 	struct rhashtable node_ht; | ||||
| 	struct rhashtable_params ht_params; | ||||
| 	struct list_head node_list; | ||||
| 	unsigned int node_count; | ||||
| 	unsigned int root_count; | ||||
| 	unsigned int refcount; | ||||
| 	const struct objagg_ops *ops; | ||||
| }; | ||||
| 
 | ||||
| struct objagg_hints_node { | ||||
| 	struct rhash_head ht_node; /* member of objagg_hints->node_ht */ | ||||
| 	struct list_head list; /* member of objagg_hints->node_list */ | ||||
| 	struct objagg_hints_node *parent; | ||||
| 	unsigned int root_id; | ||||
| 	struct objagg_obj_stats_info stats_info; | ||||
| 	unsigned long obj[0]; | ||||
| }; | ||||
| 
 | ||||
| static struct objagg_hints_node * | ||||
| objagg_hints_lookup(struct objagg_hints *objagg_hints, void *obj) | ||||
| { | ||||
| 	if (!objagg_hints) | ||||
| 		return NULL; | ||||
| 	return rhashtable_lookup_fast(&objagg_hints->node_ht, obj, | ||||
| 				      objagg_hints->ht_params); | ||||
| } | ||||
| 
 | ||||
| struct objagg { | ||||
| 	const struct objagg_ops *ops; | ||||
| 	void *priv; | ||||
|  | @ -18,6 +47,8 @@ struct objagg { | |||
| 	struct rhashtable_params ht_params; | ||||
| 	struct list_head obj_list; | ||||
| 	unsigned int obj_count; | ||||
| 	struct ida root_ida; | ||||
| 	struct objagg_hints *hints; | ||||
| }; | ||||
| 
 | ||||
| struct objagg_obj { | ||||
|  | @ -30,6 +61,7 @@ struct objagg_obj { | |||
| 		void *delta_priv; /* user delta private */ | ||||
| 		void *root_priv; /* user root private */ | ||||
| 	}; | ||||
| 	unsigned int root_id; | ||||
| 	unsigned int refcount; /* counts number of users of this object
 | ||||
| 				* including nested objects | ||||
| 				*/ | ||||
|  | @ -130,7 +162,8 @@ static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj) | |||
| 
 | ||||
| static int objagg_obj_parent_assign(struct objagg *objagg, | ||||
| 				    struct objagg_obj *objagg_obj, | ||||
| 				    struct objagg_obj *parent) | ||||
| 				    struct objagg_obj *parent, | ||||
| 				    bool take_parent_ref) | ||||
| { | ||||
| 	void *delta_priv; | ||||
| 
 | ||||
|  | @ -144,7 +177,8 @@ static int objagg_obj_parent_assign(struct objagg *objagg, | |||
| 	 */ | ||||
| 	objagg_obj->parent = parent; | ||||
| 	objagg_obj->delta_priv = delta_priv; | ||||
| 	objagg_obj_ref_inc(objagg_obj->parent); | ||||
| 	if (take_parent_ref) | ||||
| 		objagg_obj_ref_inc(objagg_obj->parent); | ||||
| 	trace_objagg_obj_parent_assign(objagg, objagg_obj, | ||||
| 				       parent, | ||||
| 				       parent->refcount); | ||||
|  | @ -164,7 +198,7 @@ static int objagg_obj_parent_lookup_assign(struct objagg *objagg, | |||
| 		if (!objagg_obj_is_root(objagg_obj_cur)) | ||||
| 			continue; | ||||
| 		err = objagg_obj_parent_assign(objagg, objagg_obj, | ||||
| 					       objagg_obj_cur); | ||||
| 					       objagg_obj_cur, true); | ||||
| 		if (!err) | ||||
| 			return 0; | ||||
| 	} | ||||
|  | @ -184,16 +218,68 @@ static void objagg_obj_parent_unassign(struct objagg *objagg, | |||
| 	__objagg_obj_put(objagg, objagg_obj->parent); | ||||
| } | ||||
| 
 | ||||
| static int objagg_obj_root_create(struct objagg *objagg, | ||||
| 				  struct objagg_obj *objagg_obj) | ||||
| static int objagg_obj_root_id_alloc(struct objagg *objagg, | ||||
| 				    struct objagg_obj *objagg_obj, | ||||
| 				    struct objagg_hints_node *hnode) | ||||
| { | ||||
| 	objagg_obj->root_priv = objagg->ops->root_create(objagg->priv, | ||||
| 							 objagg_obj->obj); | ||||
| 	if (IS_ERR(objagg_obj->root_priv)) | ||||
| 		return PTR_ERR(objagg_obj->root_priv); | ||||
| 	unsigned int min, max; | ||||
| 	int root_id; | ||||
| 
 | ||||
| 	/* In case there are no hints available, the root id is invalid. */ | ||||
| 	if (!objagg->hints) { | ||||
| 		objagg_obj->root_id = OBJAGG_OBJ_ROOT_ID_INVALID; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (hnode) { | ||||
| 		min = hnode->root_id; | ||||
| 		max = hnode->root_id; | ||||
| 	} else { | ||||
| 		/* For objects with no hint, start after the last
 | ||||
| 		 * hinted root_id. | ||||
| 		 */ | ||||
| 		min = objagg->hints->root_count; | ||||
| 		max = ~0; | ||||
| 	} | ||||
| 
 | ||||
| 	root_id = ida_alloc_range(&objagg->root_ida, min, max, GFP_KERNEL); | ||||
| 
 | ||||
| 	if (root_id < 0) | ||||
| 		return root_id; | ||||
| 	objagg_obj->root_id = root_id; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void objagg_obj_root_id_free(struct objagg *objagg, | ||||
| 				    struct objagg_obj *objagg_obj) | ||||
| { | ||||
| 	if (!objagg->hints) | ||||
| 		return; | ||||
| 	ida_free(&objagg->root_ida, objagg_obj->root_id); | ||||
| } | ||||
| 
 | ||||
| static int objagg_obj_root_create(struct objagg *objagg, | ||||
| 				  struct objagg_obj *objagg_obj, | ||||
| 				  struct objagg_hints_node *hnode) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = objagg_obj_root_id_alloc(objagg, objagg_obj, hnode); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	objagg_obj->root_priv = objagg->ops->root_create(objagg->priv, | ||||
| 							 objagg_obj->obj, | ||||
| 							 objagg_obj->root_id); | ||||
| 	if (IS_ERR(objagg_obj->root_priv)) { | ||||
| 		err = PTR_ERR(objagg_obj->root_priv); | ||||
| 		goto err_root_create; | ||||
| 	} | ||||
| 	trace_objagg_obj_root_create(objagg, objagg_obj); | ||||
| 	return 0; | ||||
| 
 | ||||
| err_root_create: | ||||
| 	objagg_obj_root_id_free(objagg, objagg_obj); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void objagg_obj_root_destroy(struct objagg *objagg, | ||||
|  | @ -201,19 +287,69 @@ static void objagg_obj_root_destroy(struct objagg *objagg, | |||
| { | ||||
| 	trace_objagg_obj_root_destroy(objagg, objagg_obj); | ||||
| 	objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv); | ||||
| 	objagg_obj_root_id_free(objagg, objagg_obj); | ||||
| } | ||||
| 
 | ||||
| static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj); | ||||
| 
 | ||||
| static int objagg_obj_init_with_hints(struct objagg *objagg, | ||||
| 				      struct objagg_obj *objagg_obj, | ||||
| 				      bool *hint_found) | ||||
| { | ||||
| 	struct objagg_hints_node *hnode; | ||||
| 	struct objagg_obj *parent; | ||||
| 	int err; | ||||
| 
 | ||||
| 	hnode = objagg_hints_lookup(objagg->hints, objagg_obj->obj); | ||||
| 	if (!hnode) { | ||||
| 		*hint_found = false; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	*hint_found = true; | ||||
| 
 | ||||
| 	if (!hnode->parent) | ||||
| 		return objagg_obj_root_create(objagg, objagg_obj, hnode); | ||||
| 
 | ||||
| 	parent = __objagg_obj_get(objagg, hnode->parent->obj); | ||||
| 	if (IS_ERR(parent)) | ||||
| 		return PTR_ERR(parent); | ||||
| 
 | ||||
| 	err = objagg_obj_parent_assign(objagg, objagg_obj, parent, false); | ||||
| 	if (err) { | ||||
| 		*hint_found = false; | ||||
| 		err = 0; | ||||
| 		goto err_parent_assign; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_parent_assign: | ||||
| 	objagg_obj_put(objagg, parent); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int objagg_obj_init(struct objagg *objagg, | ||||
| 			   struct objagg_obj *objagg_obj) | ||||
| { | ||||
| 	bool hint_found; | ||||
| 	int err; | ||||
| 
 | ||||
| 	/* First, try to use hints if they are available and
 | ||||
| 	 * if they provide result. | ||||
| 	 */ | ||||
| 	err = objagg_obj_init_with_hints(objagg, objagg_obj, &hint_found); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	if (hint_found) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Try to find if the object can be aggregated under an existing one. */ | ||||
| 	err = objagg_obj_parent_lookup_assign(objagg, objagg_obj); | ||||
| 	if (!err) | ||||
| 		return 0; | ||||
| 	/* If aggregation is not possible, make the object a root. */ | ||||
| 	return objagg_obj_root_create(objagg, objagg_obj); | ||||
| 	return objagg_obj_root_create(objagg, objagg_obj, NULL); | ||||
| } | ||||
| 
 | ||||
| static void objagg_obj_fini(struct objagg *objagg, | ||||
|  | @ -349,8 +485,9 @@ EXPORT_SYMBOL(objagg_obj_put); | |||
| 
 | ||||
| /**
 | ||||
|  * objagg_create - creates a new objagg instance | ||||
|  * @ops:	user-specific callbacks | ||||
|  * @priv:	pointer to a private data passed to the ops | ||||
|  * @ops:		user-specific callbacks | ||||
|  * @objagg_hints:	hints, can be NULL | ||||
|  * @priv:		pointer to a private data passed to the ops | ||||
|  * | ||||
|  * Note: all locking must be provided by the caller. | ||||
|  * | ||||
|  | @ -374,18 +511,25 @@ EXPORT_SYMBOL(objagg_obj_put); | |||
|  * Returns a pointer to newly created objagg instance in case of success, | ||||
|  * otherwise it returns pointer error using ERR_PTR macro. | ||||
|  */ | ||||
| struct objagg *objagg_create(const struct objagg_ops *ops, void *priv) | ||||
| struct objagg *objagg_create(const struct objagg_ops *ops, | ||||
| 			     struct objagg_hints *objagg_hints, void *priv) | ||||
| { | ||||
| 	struct objagg *objagg; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy || | ||||
| 		    !ops->delta_create || !ops->delta_destroy)) | ||||
| 		    !ops->delta_check || !ops->delta_create || | ||||
| 		    !ops->delta_destroy)) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	objagg = kzalloc(sizeof(*objagg), GFP_KERNEL); | ||||
| 	if (!objagg) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 	objagg->ops = ops; | ||||
| 	if (objagg_hints) { | ||||
| 		objagg->hints = objagg_hints; | ||||
| 		objagg_hints->refcount++; | ||||
| 	} | ||||
| 	objagg->priv = priv; | ||||
| 	INIT_LIST_HEAD(&objagg->obj_list); | ||||
| 
 | ||||
|  | @ -397,6 +541,8 @@ struct objagg *objagg_create(const struct objagg_ops *ops, void *priv) | |||
| 	if (err) | ||||
| 		goto err_rhashtable_init; | ||||
| 
 | ||||
| 	ida_init(&objagg->root_ida); | ||||
| 
 | ||||
| 	trace_objagg_create(objagg); | ||||
| 	return objagg; | ||||
| 
 | ||||
|  | @ -415,8 +561,11 @@ EXPORT_SYMBOL(objagg_create); | |||
| void objagg_destroy(struct objagg *objagg) | ||||
| { | ||||
| 	trace_objagg_destroy(objagg); | ||||
| 	ida_destroy(&objagg->root_ida); | ||||
| 	WARN_ON(!list_empty(&objagg->obj_list)); | ||||
| 	rhashtable_destroy(&objagg->obj_ht); | ||||
| 	if (objagg->hints) | ||||
| 		objagg_hints_put(objagg->hints); | ||||
| 	kfree(objagg); | ||||
| } | ||||
| EXPORT_SYMBOL(objagg_destroy); | ||||
|  | @ -496,6 +645,404 @@ void objagg_stats_put(const struct objagg_stats *objagg_stats) | |||
| } | ||||
| EXPORT_SYMBOL(objagg_stats_put); | ||||
| 
 | ||||
| static struct objagg_hints_node * | ||||
| objagg_hints_node_create(struct objagg_hints *objagg_hints, | ||||
| 			 struct objagg_obj *objagg_obj, size_t obj_size, | ||||
| 			 struct objagg_hints_node *parent_hnode) | ||||
| { | ||||
| 	unsigned int user_count = objagg_obj->stats.user_count; | ||||
| 	struct objagg_hints_node *hnode; | ||||
| 	int err; | ||||
| 
 | ||||
| 	hnode = kzalloc(sizeof(*hnode) + obj_size, GFP_KERNEL); | ||||
| 	if (!hnode) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 	memcpy(hnode->obj, &objagg_obj->obj, obj_size); | ||||
| 	hnode->stats_info.stats.user_count = user_count; | ||||
| 	hnode->stats_info.stats.delta_user_count = user_count; | ||||
| 	if (parent_hnode) { | ||||
| 		parent_hnode->stats_info.stats.delta_user_count += user_count; | ||||
| 	} else { | ||||
| 		hnode->root_id = objagg_hints->root_count++; | ||||
| 		hnode->stats_info.is_root = true; | ||||
| 	} | ||||
| 	hnode->stats_info.objagg_obj = objagg_obj; | ||||
| 
 | ||||
| 	err = rhashtable_insert_fast(&objagg_hints->node_ht, &hnode->ht_node, | ||||
| 				     objagg_hints->ht_params); | ||||
| 	if (err) | ||||
| 		goto err_ht_insert; | ||||
| 
 | ||||
| 	list_add(&hnode->list, &objagg_hints->node_list); | ||||
| 	hnode->parent = parent_hnode; | ||||
| 	objagg_hints->node_count++; | ||||
| 
 | ||||
| 	return hnode; | ||||
| 
 | ||||
| err_ht_insert: | ||||
| 	kfree(hnode); | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
| 
 | ||||
| static void objagg_hints_flush(struct objagg_hints *objagg_hints) | ||||
| { | ||||
| 	struct objagg_hints_node *hnode, *tmp; | ||||
| 
 | ||||
| 	list_for_each_entry_safe(hnode, tmp, &objagg_hints->node_list, list) { | ||||
| 		list_del(&hnode->list); | ||||
| 		rhashtable_remove_fast(&objagg_hints->node_ht, &hnode->ht_node, | ||||
| 				       objagg_hints->ht_params); | ||||
| 		kfree(hnode); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct objagg_tmp_node { | ||||
| 	struct objagg_obj *objagg_obj; | ||||
| 	bool crossed_out; | ||||
| }; | ||||
| 
 | ||||
| struct objagg_tmp_graph { | ||||
| 	struct objagg_tmp_node *nodes; | ||||
| 	unsigned long nodes_count; | ||||
| 	unsigned long *edges; | ||||
| }; | ||||
| 
 | ||||
| static int objagg_tmp_graph_edge_index(struct objagg_tmp_graph *graph, | ||||
| 				       int parent_index, int index) | ||||
| { | ||||
| 	return index * graph->nodes_count + parent_index; | ||||
| } | ||||
| 
 | ||||
| static void objagg_tmp_graph_edge_set(struct objagg_tmp_graph *graph, | ||||
| 				      int parent_index, int index) | ||||
| { | ||||
| 	int edge_index = objagg_tmp_graph_edge_index(graph, index, | ||||
| 						     parent_index); | ||||
| 
 | ||||
| 	__set_bit(edge_index, graph->edges); | ||||
| } | ||||
| 
 | ||||
| static bool objagg_tmp_graph_is_edge(struct objagg_tmp_graph *graph, | ||||
| 				     int parent_index, int index) | ||||
| { | ||||
| 	int edge_index = objagg_tmp_graph_edge_index(graph, index, | ||||
| 						     parent_index); | ||||
| 
 | ||||
| 	return test_bit(edge_index, graph->edges); | ||||
| } | ||||
| 
 | ||||
| static unsigned int objagg_tmp_graph_node_weight(struct objagg_tmp_graph *graph, | ||||
| 						 unsigned int index) | ||||
| { | ||||
| 	struct objagg_tmp_node *node = &graph->nodes[index]; | ||||
| 	unsigned int weight = node->objagg_obj->stats.user_count; | ||||
| 	int j; | ||||
| 
 | ||||
| 	/* Node weight is sum of node users and all other nodes users
 | ||||
| 	 * that this node can represent with delta. | ||||
| 	 */ | ||||
| 
 | ||||
| 	if (node->crossed_out) | ||||
| 		return 0; | ||||
| 	for (j = 0; j < graph->nodes_count; j++) { | ||||
| 		if (!objagg_tmp_graph_is_edge(graph, index, j)) | ||||
| 			continue; | ||||
| 		node = &graph->nodes[j]; | ||||
| 		if (node->crossed_out) | ||||
| 			continue; | ||||
| 		weight += node->objagg_obj->stats.user_count; | ||||
| 	} | ||||
| 	return weight; | ||||
| } | ||||
| 
 | ||||
| static int objagg_tmp_graph_node_max_weight(struct objagg_tmp_graph *graph) | ||||
| { | ||||
| 	unsigned int max_weight = 0; | ||||
| 	unsigned int weight; | ||||
| 	int max_index = -1; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < graph->nodes_count; i++) { | ||||
| 		weight = objagg_tmp_graph_node_weight(graph, i); | ||||
| 		if (weight > max_weight) { | ||||
| 			max_weight = weight; | ||||
| 			max_index = i; | ||||
| 		} | ||||
| 	} | ||||
| 	return max_index; | ||||
| } | ||||
| 
 | ||||
| static struct objagg_tmp_graph *objagg_tmp_graph_create(struct objagg *objagg) | ||||
| { | ||||
| 	unsigned int nodes_count = objagg->obj_count; | ||||
| 	struct objagg_tmp_graph *graph; | ||||
| 	struct objagg_tmp_node *node; | ||||
| 	struct objagg_tmp_node *pnode; | ||||
| 	struct objagg_obj *objagg_obj; | ||||
| 	size_t alloc_size; | ||||
| 	int i, j; | ||||
| 
 | ||||
| 	graph = kzalloc(sizeof(*graph), GFP_KERNEL); | ||||
| 	if (!graph) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	graph->nodes = kcalloc(nodes_count, sizeof(*graph->nodes), GFP_KERNEL); | ||||
| 	if (!graph->nodes) | ||||
| 		goto err_nodes_alloc; | ||||
| 	graph->nodes_count = nodes_count; | ||||
| 
 | ||||
| 	alloc_size = BITS_TO_LONGS(nodes_count * nodes_count) * | ||||
| 		     sizeof(unsigned long); | ||||
| 	graph->edges = kzalloc(alloc_size, GFP_KERNEL); | ||||
| 	if (!graph->edges) | ||||
| 		goto err_edges_alloc; | ||||
| 
 | ||||
| 	i = 0; | ||||
| 	list_for_each_entry(objagg_obj, &objagg->obj_list, list) { | ||||
| 		node = &graph->nodes[i++]; | ||||
| 		node->objagg_obj = objagg_obj; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Assemble a temporary graph. Insert edge X->Y in case Y can be
 | ||||
| 	 * in delta of X. | ||||
| 	 */ | ||||
| 	for (i = 0; i < nodes_count; i++) { | ||||
| 		for (j = 0; j < nodes_count; j++) { | ||||
| 			if (i == j) | ||||
| 				continue; | ||||
| 			pnode = &graph->nodes[i]; | ||||
| 			node = &graph->nodes[j]; | ||||
| 			if (objagg->ops->delta_check(objagg->priv, | ||||
| 						     pnode->objagg_obj->obj, | ||||
| 						     node->objagg_obj->obj)) { | ||||
| 				objagg_tmp_graph_edge_set(graph, i, j); | ||||
| 
 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return graph; | ||||
| 
 | ||||
| err_edges_alloc: | ||||
| 	kfree(graph->nodes); | ||||
| err_nodes_alloc: | ||||
| 	kfree(graph); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void objagg_tmp_graph_destroy(struct objagg_tmp_graph *graph) | ||||
| { | ||||
| 	kfree(graph->edges); | ||||
| 	kfree(graph->nodes); | ||||
| 	kfree(graph); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| objagg_opt_simple_greedy_fillup_hints(struct objagg_hints *objagg_hints, | ||||
| 				      struct objagg *objagg) | ||||
| { | ||||
| 	struct objagg_hints_node *hnode, *parent_hnode; | ||||
| 	struct objagg_tmp_graph *graph; | ||||
| 	struct objagg_tmp_node *node; | ||||
| 	int index; | ||||
| 	int j; | ||||
| 	int err; | ||||
| 
 | ||||
| 	graph = objagg_tmp_graph_create(objagg); | ||||
| 	if (!graph) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* Find the nodes from the ones that can accommodate most users
 | ||||
| 	 * and cross them out of the graph. Save them to the hint list. | ||||
| 	 */ | ||||
| 	while ((index = objagg_tmp_graph_node_max_weight(graph)) != -1) { | ||||
| 		node = &graph->nodes[index]; | ||||
| 		node->crossed_out = true; | ||||
| 		hnode = objagg_hints_node_create(objagg_hints, | ||||
| 						 node->objagg_obj, | ||||
| 						 objagg->ops->obj_size, | ||||
| 						 NULL); | ||||
| 		if (IS_ERR(hnode)) { | ||||
| 			err = PTR_ERR(hnode); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		parent_hnode = hnode; | ||||
| 		for (j = 0; j < graph->nodes_count; j++) { | ||||
| 			if (!objagg_tmp_graph_is_edge(graph, index, j)) | ||||
| 				continue; | ||||
| 			node = &graph->nodes[j]; | ||||
| 			if (node->crossed_out) | ||||
| 				continue; | ||||
| 			node->crossed_out = true; | ||||
| 			hnode = objagg_hints_node_create(objagg_hints, | ||||
| 							 node->objagg_obj, | ||||
| 							 objagg->ops->obj_size, | ||||
| 							 parent_hnode); | ||||
| 			if (IS_ERR(hnode)) { | ||||
| 				err = PTR_ERR(hnode); | ||||
| 				goto out; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	err = 0; | ||||
| out: | ||||
| 	objagg_tmp_graph_destroy(graph); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| struct objagg_opt_algo { | ||||
| 	int (*fillup_hints)(struct objagg_hints *objagg_hints, | ||||
| 			    struct objagg *objagg); | ||||
| }; | ||||
| 
 | ||||
| static const struct objagg_opt_algo objagg_opt_simple_greedy = { | ||||
| 	.fillup_hints = objagg_opt_simple_greedy_fillup_hints, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static const struct objagg_opt_algo *objagg_opt_algos[] = { | ||||
| 	[OBJAGG_OPT_ALGO_SIMPLE_GREEDY] = &objagg_opt_simple_greedy, | ||||
| }; | ||||
| 
 | ||||
| static int objagg_hints_obj_cmp(struct rhashtable_compare_arg *arg, | ||||
| 				const void *obj) | ||||
| { | ||||
| 	struct rhashtable *ht = arg->ht; | ||||
| 	struct objagg_hints *objagg_hints = | ||||
| 			container_of(ht, struct objagg_hints, node_ht); | ||||
| 	const struct objagg_ops *ops = objagg_hints->ops; | ||||
| 	const char *ptr = obj; | ||||
| 
 | ||||
| 	ptr += ht->p.key_offset; | ||||
| 	return ops->hints_obj_cmp ? ops->hints_obj_cmp(ptr, arg->key) : | ||||
| 				    memcmp(ptr, arg->key, ht->p.key_len); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * objagg_hints_get - obtains hints instance | ||||
|  * @objagg:		objagg instance | ||||
|  * @opt_algo_type:	type of hints finding algorithm | ||||
|  * | ||||
|  * Note: all locking must be provided by the caller. | ||||
|  * | ||||
|  * According to the algo type, the existing objects of objagg instance | ||||
|  * are going to be went-through to assemble an optimal tree. We call this | ||||
|  * tree hints. These hints can be later on used for creation of | ||||
|  * a new objagg instance. There, the future object creations are going | ||||
|  * to be consulted with these hints in order to find out, where exactly | ||||
|  * the new object should be put as a root or delta. | ||||
|  * | ||||
|  * Returns a pointer to hints instance in case of success, | ||||
|  * otherwise it returns pointer error using ERR_PTR macro. | ||||
|  */ | ||||
| struct objagg_hints *objagg_hints_get(struct objagg *objagg, | ||||
| 				      enum objagg_opt_algo_type opt_algo_type) | ||||
| { | ||||
| 	const struct objagg_opt_algo *algo = objagg_opt_algos[opt_algo_type]; | ||||
| 	struct objagg_hints *objagg_hints; | ||||
| 	int err; | ||||
| 
 | ||||
| 	objagg_hints = kzalloc(sizeof(*objagg_hints), GFP_KERNEL); | ||||
| 	if (!objagg_hints) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	objagg_hints->ops = objagg->ops; | ||||
| 	objagg_hints->refcount = 1; | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&objagg_hints->node_list); | ||||
| 
 | ||||
| 	objagg_hints->ht_params.key_len = objagg->ops->obj_size; | ||||
| 	objagg_hints->ht_params.key_offset = | ||||
| 				offsetof(struct objagg_hints_node, obj); | ||||
| 	objagg_hints->ht_params.head_offset = | ||||
| 				offsetof(struct objagg_hints_node, ht_node); | ||||
| 	objagg_hints->ht_params.obj_cmpfn = objagg_hints_obj_cmp; | ||||
| 
 | ||||
| 	err = rhashtable_init(&objagg_hints->node_ht, &objagg_hints->ht_params); | ||||
| 	if (err) | ||||
| 		goto err_rhashtable_init; | ||||
| 
 | ||||
| 	err = algo->fillup_hints(objagg_hints, objagg); | ||||
| 	if (err) | ||||
| 		goto err_fillup_hints; | ||||
| 
 | ||||
| 	if (WARN_ON(objagg_hints->node_count != objagg->obj_count)) | ||||
| 		goto err_node_count_check; | ||||
| 
 | ||||
| 	return objagg_hints; | ||||
| 
 | ||||
| err_node_count_check: | ||||
| err_fillup_hints: | ||||
| 	objagg_hints_flush(objagg_hints); | ||||
| 	rhashtable_destroy(&objagg_hints->node_ht); | ||||
| err_rhashtable_init: | ||||
| 	kfree(objagg_hints); | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
| EXPORT_SYMBOL(objagg_hints_get); | ||||
| 
 | ||||
| /**
 | ||||
|  * objagg_hints_put - puts hints instance | ||||
|  * @objagg_hints:	objagg hints instance | ||||
|  * | ||||
|  * Note: all locking must be provided by the caller. | ||||
|  */ | ||||
| void objagg_hints_put(struct objagg_hints *objagg_hints) | ||||
| { | ||||
| 	if (--objagg_hints->refcount) | ||||
| 		return; | ||||
| 	objagg_hints_flush(objagg_hints); | ||||
| 	rhashtable_destroy(&objagg_hints->node_ht); | ||||
| 	kfree(objagg_hints); | ||||
| } | ||||
| EXPORT_SYMBOL(objagg_hints_put); | ||||
| 
 | ||||
| /**
 | ||||
|  * objagg_hints_stats_get - obtains stats of the hints instance | ||||
|  * @objagg_hints:	hints instance | ||||
|  * | ||||
|  * Note: all locking must be provided by the caller. | ||||
|  * | ||||
|  * The returned structure contains statistics of all objects | ||||
|  * currently in use, ordered by following rules: | ||||
|  * 1) Root objects are always on lower indexes than the rest. | ||||
|  * 2) Objects with higher delta user count are always on lower | ||||
|  *    indexes. | ||||
|  * 3) In case multiple objects have the same delta user count, | ||||
|  *    the objects are ordered by user count. | ||||
|  * | ||||
|  * Returns a pointer to stats instance in case of success, | ||||
|  * otherwise it returns pointer error using ERR_PTR macro. | ||||
|  */ | ||||
| const struct objagg_stats * | ||||
| objagg_hints_stats_get(struct objagg_hints *objagg_hints) | ||||
| { | ||||
| 	struct objagg_stats *objagg_stats; | ||||
| 	struct objagg_hints_node *hnode; | ||||
| 	int i; | ||||
| 
 | ||||
| 	objagg_stats = kzalloc(struct_size(objagg_stats, stats_info, | ||||
| 					   objagg_hints->node_count), | ||||
| 			       GFP_KERNEL); | ||||
| 	if (!objagg_stats) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	i = 0; | ||||
| 	list_for_each_entry(hnode, &objagg_hints->node_list, list) { | ||||
| 		memcpy(&objagg_stats->stats_info[i], &hnode->stats_info, | ||||
| 		       sizeof(objagg_stats->stats_info[0])); | ||||
| 		i++; | ||||
| 	} | ||||
| 	objagg_stats->stats_info_count = i; | ||||
| 
 | ||||
| 	sort(objagg_stats->stats_info, objagg_stats->stats_info_count, | ||||
| 	     sizeof(struct objagg_obj_stats_info), | ||||
| 	     objagg_stats_info_sort_cmp_func, NULL); | ||||
| 
 | ||||
| 	return objagg_stats; | ||||
| } | ||||
| EXPORT_SYMBOL(objagg_hints_stats_get); | ||||
| 
 | ||||
| MODULE_LICENSE("Dual BSD/GPL"); | ||||
| MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); | ||||
| MODULE_DESCRIPTION("Object aggregation manager"); | ||||
|  |  | |||
|  | @ -87,6 +87,15 @@ static void world_obj_put(struct world *world, struct objagg *objagg, | |||
| 
 | ||||
| #define MAX_KEY_ID_DIFF 5 | ||||
| 
 | ||||
| static bool delta_check(void *priv, const void *parent_obj, const void *obj) | ||||
| { | ||||
| 	const struct tokey *parent_key = parent_obj; | ||||
| 	const struct tokey *key = obj; | ||||
| 	int diff = key->id - parent_key->id; | ||||
| 
 | ||||
| 	return diff >= 0 && diff <= MAX_KEY_ID_DIFF; | ||||
| } | ||||
| 
 | ||||
| static void *delta_create(void *priv, void *parent_obj, void *obj) | ||||
| { | ||||
| 	struct tokey *parent_key = parent_obj; | ||||
|  | @ -95,7 +104,7 @@ static void *delta_create(void *priv, void *parent_obj, void *obj) | |||
| 	int diff = key->id - parent_key->id; | ||||
| 	struct delta *delta; | ||||
| 
 | ||||
| 	if (diff < 0 || diff > MAX_KEY_ID_DIFF) | ||||
| 	if (!delta_check(priv, parent_obj, obj)) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	delta = kzalloc(sizeof(*delta), GFP_KERNEL); | ||||
|  | @ -115,7 +124,7 @@ static void delta_destroy(void *priv, void *delta_priv) | |||
| 	kfree(delta); | ||||
| } | ||||
| 
 | ||||
| static void *root_create(void *priv, void *obj) | ||||
| static void *root_create(void *priv, void *obj, unsigned int id) | ||||
| { | ||||
| 	struct world *world = priv; | ||||
| 	struct tokey *key = obj; | ||||
|  | @ -268,6 +277,12 @@ static int check_stats_nodelta(struct objagg *objagg) | |||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static bool delta_check_dummy(void *priv, const void *parent_obj, | ||||
| 			      const void *obj) | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static void *delta_create_dummy(void *priv, void *parent_obj, void *obj) | ||||
| { | ||||
| 	return ERR_PTR(-EOPNOTSUPP); | ||||
|  | @ -279,6 +294,7 @@ static void delta_destroy_dummy(void *priv, void *delta_priv) | |||
| 
 | ||||
| static const struct objagg_ops nodelta_ops = { | ||||
| 	.obj_size = sizeof(struct tokey), | ||||
| 	.delta_check = delta_check_dummy, | ||||
| 	.delta_create = delta_create_dummy, | ||||
| 	.delta_destroy = delta_destroy_dummy, | ||||
| 	.root_create = root_create, | ||||
|  | @ -292,7 +308,7 @@ static int test_nodelta(void) | |||
| 	int i; | ||||
| 	int err; | ||||
| 
 | ||||
| 	objagg = objagg_create(&nodelta_ops, &world); | ||||
| 	objagg = objagg_create(&nodelta_ops, NULL, &world); | ||||
| 	if (IS_ERR(objagg)) | ||||
| 		return PTR_ERR(objagg); | ||||
| 
 | ||||
|  | @ -357,6 +373,7 @@ static int test_nodelta(void) | |||
| 
 | ||||
| static const struct objagg_ops delta_ops = { | ||||
| 	.obj_size = sizeof(struct tokey), | ||||
| 	.delta_check = delta_check, | ||||
| 	.delta_create = delta_create, | ||||
| 	.delta_destroy = delta_destroy, | ||||
| 	.root_create = root_create, | ||||
|  | @ -793,7 +810,7 @@ static int test_delta(void) | |||
| 	int i; | ||||
| 	int err; | ||||
| 
 | ||||
| 	objagg = objagg_create(&delta_ops, &world); | ||||
| 	objagg = objagg_create(&delta_ops, NULL, &world); | ||||
| 	if (IS_ERR(objagg)) | ||||
| 		return PTR_ERR(objagg); | ||||
| 
 | ||||
|  | @ -815,6 +832,170 @@ static int test_delta(void) | |||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| struct hints_case { | ||||
| 	const unsigned int *key_ids; | ||||
| 	size_t key_ids_count; | ||||
| 	struct expect_stats expect_stats; | ||||
| 	struct expect_stats expect_stats_hints; | ||||
| }; | ||||
| 
 | ||||
| static const unsigned int hints_case_key_ids[] = { | ||||
| 	1, 7, 3, 5, 3, 1, 30, 8, 8, 5, 6, 8, | ||||
| }; | ||||
| 
 | ||||
| static const struct hints_case hints_case = { | ||||
| 	.key_ids = hints_case_key_ids, | ||||
| 	.key_ids_count = ARRAY_SIZE(hints_case_key_ids), | ||||
| 	.expect_stats = | ||||
| 		EXPECT_STATS(7, ROOT(1, 2, 7), ROOT(7, 1, 4), ROOT(30, 1, 1), | ||||
| 				DELTA(8, 3), DELTA(3, 2), | ||||
| 				DELTA(5, 2), DELTA(6, 1)), | ||||
| 	.expect_stats_hints = | ||||
| 		EXPECT_STATS(7, ROOT(3, 2, 9), ROOT(1, 2, 2), ROOT(30, 1, 1), | ||||
| 				DELTA(8, 3), DELTA(5, 2), | ||||
| 				DELTA(6, 1), DELTA(7, 1)), | ||||
| }; | ||||
| 
 | ||||
| static void __pr_debug_stats(const struct objagg_stats *stats) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < stats->stats_info_count; i++) | ||||
| 		pr_debug("Stat index %d key %u: u %d, d %d, %s\n", i, | ||||
| 			 obj_to_key_id(stats->stats_info[i].objagg_obj), | ||||
| 			 stats->stats_info[i].stats.user_count, | ||||
| 			 stats->stats_info[i].stats.delta_user_count, | ||||
| 			 stats->stats_info[i].is_root ? "root" : "noroot"); | ||||
| } | ||||
| 
 | ||||
| static void pr_debug_stats(struct objagg *objagg) | ||||
| { | ||||
| 	const struct objagg_stats *stats; | ||||
| 
 | ||||
| 	stats = objagg_stats_get(objagg); | ||||
| 	if (IS_ERR(stats)) | ||||
| 		return; | ||||
| 	__pr_debug_stats(stats); | ||||
| 	objagg_stats_put(stats); | ||||
| } | ||||
| 
 | ||||
| static void pr_debug_hints_stats(struct objagg_hints *objagg_hints) | ||||
| { | ||||
| 	const struct objagg_stats *stats; | ||||
| 
 | ||||
| 	stats = objagg_hints_stats_get(objagg_hints); | ||||
| 	if (IS_ERR(stats)) | ||||
| 		return; | ||||
| 	__pr_debug_stats(stats); | ||||
| 	objagg_stats_put(stats); | ||||
| } | ||||
| 
 | ||||
| static int check_expect_hints_stats(struct objagg_hints *objagg_hints, | ||||
| 				    const struct expect_stats *expect_stats, | ||||
| 				    const char **errmsg) | ||||
| { | ||||
| 	const struct objagg_stats *stats; | ||||
| 	int err; | ||||
| 
 | ||||
| 	stats = objagg_hints_stats_get(objagg_hints); | ||||
| 	if (IS_ERR(stats)) | ||||
| 		return PTR_ERR(stats); | ||||
| 	err = __check_expect_stats(stats, expect_stats, errmsg); | ||||
| 	objagg_stats_put(stats); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int test_hints_case(const struct hints_case *hints_case) | ||||
| { | ||||
| 	struct objagg_obj *objagg_obj; | ||||
| 	struct objagg_hints *hints; | ||||
| 	struct world world2 = {}; | ||||
| 	struct world world = {}; | ||||
| 	struct objagg *objagg2; | ||||
| 	struct objagg *objagg; | ||||
| 	const char *errmsg; | ||||
| 	int i; | ||||
| 	int err; | ||||
| 
 | ||||
| 	objagg = objagg_create(&delta_ops, NULL, &world); | ||||
| 	if (IS_ERR(objagg)) | ||||
| 		return PTR_ERR(objagg); | ||||
| 
 | ||||
| 	for (i = 0; i < hints_case->key_ids_count; i++) { | ||||
| 		objagg_obj = world_obj_get(&world, objagg, | ||||
| 					   hints_case->key_ids[i]); | ||||
| 		if (IS_ERR(objagg_obj)) { | ||||
| 			err = PTR_ERR(objagg_obj); | ||||
| 			goto err_world_obj_get; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pr_debug_stats(objagg); | ||||
| 	err = check_expect_stats(objagg, &hints_case->expect_stats, &errmsg); | ||||
| 	if (err) { | ||||
| 		pr_err("Stats: %s\n", errmsg); | ||||
| 		goto err_check_expect_stats; | ||||
| 	} | ||||
| 
 | ||||
| 	hints = objagg_hints_get(objagg, OBJAGG_OPT_ALGO_SIMPLE_GREEDY); | ||||
| 	if (IS_ERR(hints)) { | ||||
| 		err = PTR_ERR(hints); | ||||
| 		goto err_hints_get; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_debug_hints_stats(hints); | ||||
| 	err = check_expect_hints_stats(hints, &hints_case->expect_stats_hints, | ||||
| 				       &errmsg); | ||||
| 	if (err) { | ||||
| 		pr_err("Hints stats: %s\n", errmsg); | ||||
| 		goto err_check_expect_hints_stats; | ||||
| 	} | ||||
| 
 | ||||
| 	objagg2 = objagg_create(&delta_ops, hints, &world2); | ||||
| 	if (IS_ERR(objagg)) | ||||
| 		return PTR_ERR(objagg); | ||||
| 
 | ||||
| 	for (i = 0; i < hints_case->key_ids_count; i++) { | ||||
| 		objagg_obj = world_obj_get(&world2, objagg2, | ||||
| 					   hints_case->key_ids[i]); | ||||
| 		if (IS_ERR(objagg_obj)) { | ||||
| 			err = PTR_ERR(objagg_obj); | ||||
| 			goto err_world2_obj_get; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pr_debug_stats(objagg2); | ||||
| 	err = check_expect_stats(objagg2, &hints_case->expect_stats_hints, | ||||
| 				 &errmsg); | ||||
| 	if (err) { | ||||
| 		pr_err("Stats2: %s\n", errmsg); | ||||
| 		goto err_check_expect_stats2; | ||||
| 	} | ||||
| 
 | ||||
| 	err = 0; | ||||
| 
 | ||||
| err_check_expect_stats2: | ||||
| err_world2_obj_get: | ||||
| 	for (i--; i >= 0; i--) | ||||
| 		world_obj_put(&world2, objagg, hints_case->key_ids[i]); | ||||
| 	objagg_hints_put(hints); | ||||
| 	objagg_destroy(objagg2); | ||||
| 	i = hints_case->key_ids_count; | ||||
| err_check_expect_hints_stats: | ||||
| err_hints_get: | ||||
| err_check_expect_stats: | ||||
| err_world_obj_get: | ||||
| 	for (i--; i >= 0; i--) | ||||
| 		world_obj_put(&world, objagg, hints_case->key_ids[i]); | ||||
| 
 | ||||
| 	objagg_destroy(objagg); | ||||
| 	return err; | ||||
| } | ||||
| static int test_hints(void) | ||||
| { | ||||
| 	return test_hints_case(&hints_case); | ||||
| } | ||||
| 
 | ||||
| static int __init test_objagg_init(void) | ||||
| { | ||||
| 	int err; | ||||
|  | @ -822,7 +1003,10 @@ static int __init test_objagg_init(void) | |||
| 	err = test_nodelta(); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	return test_delta(); | ||||
| 	err = test_delta(); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	return test_hints(); | ||||
| } | ||||
| 
 | ||||
| static void __exit test_objagg_exit(void) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Jiri Pirko
						Jiri Pirko