net: Don't register pernet_operations if only one of id or size is specified.
authorKuniyuki Iwashima <kuniyu@amazon.com>
Wed, 31 Jul 2024 20:07:17 +0000 (13:07 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 3 Aug 2024 21:38:44 +0000 (22:38 +0100)
We can allocate per-netns memory for struct pernet_operations by specifying
id and size.

register_pernet_operations() assigns an id to pernet_operations and later
ops_init() allocates the specified size of memory as net->gen->ptr[id].

If id is missing, no memory is allocated.  If size is not specified,
pernet_operations just wastes an entry of net->gen->ptr[] for every netns.

net_generic is available only when both id and size are specified, so let's
ensure that.

While we are at it, we add const to both fields.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/net_namespace.h
net/core/net_namespace.c

index 20c34bd..e67b483 100644 (file)
@@ -451,8 +451,8 @@ struct pernet_operations {
        /* Following method is called with RTNL held. */
        void (*exit_batch_rtnl)(struct list_head *net_exit_list,
                                struct list_head *dev_kill_list);
-       unsigned int *id;
-       size_t size;
+       unsigned int * const id;
+       const size_t size;
 };
 
 /*
index 6a823ba..1cd87df 100644 (file)
@@ -125,7 +125,7 @@ static int ops_init(const struct pernet_operations *ops, struct net *net)
        int err = -ENOMEM;
        void *data = NULL;
 
-       if (ops->id && ops->size) {
+       if (ops->id) {
                data = kzalloc(ops->size, GFP_KERNEL);
                if (!data)
                        goto out;
@@ -140,7 +140,7 @@ static int ops_init(const struct pernet_operations *ops, struct net *net)
        if (!err)
                return 0;
 
-       if (ops->id && ops->size) {
+       if (ops->id) {
                ng = rcu_dereference_protected(net->gen,
                                               lockdep_is_held(&pernet_ops_rwsem));
                ng->ptr[*ops->id] = NULL;
@@ -182,7 +182,8 @@ static void ops_free_list(const struct pernet_operations *ops,
                          struct list_head *net_exit_list)
 {
        struct net *net;
-       if (ops->size && ops->id) {
+
+       if (ops->id) {
                list_for_each_entry(net, net_exit_list, exit_list)
                        kfree(net_generic(net, *ops->id));
        }
@@ -1244,7 +1245,7 @@ static int __register_pernet_operations(struct list_head *list,
        LIST_HEAD(net_exit_list);
 
        list_add_tail(&ops->list, list);
-       if (ops->init || (ops->id && ops->size)) {
+       if (ops->init || ops->id) {
                /* We held write locked pernet_ops_rwsem, and parallel
                 * setup_net() and cleanup_net() are not possible.
                 */
@@ -1310,6 +1311,9 @@ static int register_pernet_operations(struct list_head *list,
 {
        int error;
 
+       if (WARN_ON(!!ops->id ^ !!ops->size))
+               return -EINVAL;
+
        if (ops->id) {
                error = ida_alloc_min(&net_generic_ids, MIN_PERNET_OPS_ID,
                                GFP_KERNEL);