mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	genetlink: register family ops as array
Instead of using a linked list, use an array. This reduces the data size needed by the users of genetlink, for example in wireless (net/wireless/nl80211.c) on 64-bit it frees up over 1K of data space. Remove the attempted sending of CTRL_CMD_NEWOPS ctrl event since genl_ctrl_event(CTRL_CMD_NEWOPS, ...) only returns -EINVAL anyway, therefore no such event could ever be sent. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3686ec5e84
								
							
						
					
					
						commit
						d91824c08f
					
				
					 2 changed files with 36 additions and 47 deletions
				
			
		| 
						 | 
					@ -39,9 +39,10 @@ struct genl_info;
 | 
				
			||||||
 * @post_doit: called after an operation's doit callback, it may
 | 
					 * @post_doit: called after an operation's doit callback, it may
 | 
				
			||||||
 *	undo operations done by pre_doit, for example release locks
 | 
					 *	undo operations done by pre_doit, for example release locks
 | 
				
			||||||
 * @attrbuf: buffer to store parsed attributes
 | 
					 * @attrbuf: buffer to store parsed attributes
 | 
				
			||||||
 * @ops_list: list of all assigned operations
 | 
					 | 
				
			||||||
 * @family_list: family list
 | 
					 * @family_list: family list
 | 
				
			||||||
 * @mcast_groups: multicast groups list
 | 
					 * @mcast_groups: multicast groups list
 | 
				
			||||||
 | 
					 * @ops: the operations supported by this family (private)
 | 
				
			||||||
 | 
					 * @n_ops: number of operations supported by this family (private)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct genl_family {
 | 
					struct genl_family {
 | 
				
			||||||
	unsigned int		id;
 | 
						unsigned int		id;
 | 
				
			||||||
| 
						 | 
					@ -58,7 +59,8 @@ struct genl_family {
 | 
				
			||||||
					     struct sk_buff *skb,
 | 
										     struct sk_buff *skb,
 | 
				
			||||||
					     struct genl_info *info);
 | 
										     struct genl_info *info);
 | 
				
			||||||
	struct nlattr **	attrbuf;	/* private */
 | 
						struct nlattr **	attrbuf;	/* private */
 | 
				
			||||||
	struct list_head	ops_list;	/* private */
 | 
						struct genl_ops *	ops;		/* private */
 | 
				
			||||||
 | 
						unsigned int		n_ops;		/* private */
 | 
				
			||||||
	struct list_head	family_list;	/* private */
 | 
						struct list_head	family_list;	/* private */
 | 
				
			||||||
	struct list_head	mcast_groups;	/* private */
 | 
						struct list_head	mcast_groups;	/* private */
 | 
				
			||||||
	struct module		*module;
 | 
						struct module		*module;
 | 
				
			||||||
| 
						 | 
					@ -119,7 +121,6 @@ struct genl_ops {
 | 
				
			||||||
	int		       (*dumpit)(struct sk_buff *skb,
 | 
						int		       (*dumpit)(struct sk_buff *skb,
 | 
				
			||||||
					 struct netlink_callback *cb);
 | 
										 struct netlink_callback *cb);
 | 
				
			||||||
	int		       (*done)(struct netlink_callback *cb);
 | 
						int		       (*done)(struct netlink_callback *cb);
 | 
				
			||||||
	struct list_head	ops_list;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int __genl_register_family(struct genl_family *family);
 | 
					int __genl_register_family(struct genl_family *family);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,11 +108,11 @@ static struct genl_family *genl_family_find_byname(char *name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
 | 
					static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct genl_ops *ops;
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(ops, &family->ops_list, ops_list)
 | 
						for (i = 0; i < family->n_ops; i++)
 | 
				
			||||||
		if (ops->cmd == cmd)
 | 
							if (family->ops[i].cmd == cmd)
 | 
				
			||||||
			return ops;
 | 
								return &family->ops[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -283,33 +283,30 @@ static void genl_unregister_mc_groups(struct genl_family *family)
 | 
				
			||||||
		__genl_unregister_mc_group(family, grp);
 | 
							__genl_unregister_mc_group(family, grp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int genl_register_ops(struct genl_family *family, struct genl_ops *ops)
 | 
					static int genl_validate_add_ops(struct genl_family *family, struct genl_ops *ops,
 | 
				
			||||||
 | 
									 unsigned int n_ops)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int err = -EINVAL;
 | 
						int i, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ops->dumpit == NULL && ops->doit == NULL)
 | 
						for (i = 0; i < n_ops; i++) {
 | 
				
			||||||
		goto errout;
 | 
							if (ops[i].dumpit == NULL && ops[i].doit == NULL)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
	if (genl_get_cmd(ops->cmd, family)) {
 | 
							for (j = i + 1; j < n_ops; j++)
 | 
				
			||||||
		err = -EEXIST;
 | 
								if (ops[i].cmd == ops[j].cmd)
 | 
				
			||||||
		goto errout;
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
							if (ops[i].dumpit)
 | 
				
			||||||
 | 
								ops[i].flags |= GENL_CMD_CAP_DUMP;
 | 
				
			||||||
 | 
							if (ops[i].doit)
 | 
				
			||||||
 | 
								ops[i].flags |= GENL_CMD_CAP_DO;
 | 
				
			||||||
 | 
							if (ops[i].policy)
 | 
				
			||||||
 | 
								ops[i].flags |= GENL_CMD_CAP_HASPOL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ops->dumpit)
 | 
						/* family is not registered yet, so no locking needed */
 | 
				
			||||||
		ops->flags |= GENL_CMD_CAP_DUMP;
 | 
						family->ops = ops;
 | 
				
			||||||
	if (ops->doit)
 | 
						family->n_ops = n_ops;
 | 
				
			||||||
		ops->flags |= GENL_CMD_CAP_DO;
 | 
					 | 
				
			||||||
	if (ops->policy)
 | 
					 | 
				
			||||||
		ops->flags |= GENL_CMD_CAP_HASPOL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	genl_lock_all();
 | 
						return 0;
 | 
				
			||||||
	list_add_tail(&ops->ops_list, &family->ops_list);
 | 
					 | 
				
			||||||
	genl_unlock_all();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	genl_ctrl_event(CTRL_CMD_NEWOPS, ops);
 | 
					 | 
				
			||||||
	err = 0;
 | 
					 | 
				
			||||||
errout:
 | 
					 | 
				
			||||||
	return err;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -333,7 +330,6 @@ int __genl_register_family(struct genl_family *family)
 | 
				
			||||||
	if (family->id > GENL_MAX_ID)
 | 
						if (family->id > GENL_MAX_ID)
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_LIST_HEAD(&family->ops_list);
 | 
					 | 
				
			||||||
	INIT_LIST_HEAD(&family->mcast_groups);
 | 
						INIT_LIST_HEAD(&family->mcast_groups);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	genl_lock_all();
 | 
						genl_lock_all();
 | 
				
			||||||
| 
						 | 
					@ -405,21 +401,13 @@ EXPORT_SYMBOL(__genl_register_family);
 | 
				
			||||||
int __genl_register_family_with_ops(struct genl_family *family,
 | 
					int __genl_register_family_with_ops(struct genl_family *family,
 | 
				
			||||||
	struct genl_ops *ops, size_t n_ops)
 | 
						struct genl_ops *ops, size_t n_ops)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int err, i;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = __genl_register_family(family);
 | 
						err = genl_validate_add_ops(family, ops, n_ops);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < n_ops; ++i, ++ops) {
 | 
						return __genl_register_family(family);
 | 
				
			||||||
		err = genl_register_ops(family, ops);
 | 
					 | 
				
			||||||
		if (err)
 | 
					 | 
				
			||||||
			goto err_out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
err_out:
 | 
					 | 
				
			||||||
	genl_unregister_family(family);
 | 
					 | 
				
			||||||
	return err;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(__genl_register_family_with_ops);
 | 
					EXPORT_SYMBOL(__genl_register_family_with_ops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -444,7 +432,7 @@ int genl_unregister_family(struct genl_family *family)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		list_del(&rc->family_list);
 | 
							list_del(&rc->family_list);
 | 
				
			||||||
		INIT_LIST_HEAD(&family->ops_list);
 | 
							family->n_ops = 0;
 | 
				
			||||||
		genl_unlock_all();
 | 
							genl_unlock_all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		kfree(family->attrbuf);
 | 
							kfree(family->attrbuf);
 | 
				
			||||||
| 
						 | 
					@ -671,19 +659,19 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq,
 | 
				
			||||||
	    nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr))
 | 
						    nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr))
 | 
				
			||||||
		goto nla_put_failure;
 | 
							goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!list_empty(&family->ops_list)) {
 | 
						if (family->n_ops) {
 | 
				
			||||||
		struct nlattr *nla_ops;
 | 
							struct nlattr *nla_ops;
 | 
				
			||||||
		struct genl_ops *ops;
 | 
							int i;
 | 
				
			||||||
		int idx = 1;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);
 | 
							nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);
 | 
				
			||||||
		if (nla_ops == NULL)
 | 
							if (nla_ops == NULL)
 | 
				
			||||||
			goto nla_put_failure;
 | 
								goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		list_for_each_entry(ops, &family->ops_list, ops_list) {
 | 
							for (i = 0; i < family->n_ops; i++) {
 | 
				
			||||||
			struct nlattr *nest;
 | 
								struct nlattr *nest;
 | 
				
			||||||
 | 
								struct genl_ops *ops = &family->ops[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			nest = nla_nest_start(skb, idx++);
 | 
								nest = nla_nest_start(skb, i + 1);
 | 
				
			||||||
			if (nest == NULL)
 | 
								if (nest == NULL)
 | 
				
			||||||
				goto nla_put_failure;
 | 
									goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue