Merge remote-tracking branch 'stable/linux-3.0.y' into develop-3.0
[firefly-linux-kernel-4.4.55.git] / net / netlink / af_netlink.c
index 6ef64adf7362a421240c28e5196407ce5729f1dd..10851ee55d188e87b69b60a0e4f825e26da4c4dd 100644 (file)
@@ -137,6 +137,8 @@ static void netlink_destroy_callback(struct netlink_callback *cb);
 static DEFINE_RWLOCK(nl_table_lock);
 static atomic_t nl_table_users = ATOMIC_INIT(0);
 
+#define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock));
+
 static ATOMIC_NOTIFIER_HEAD(netlink_chain);
 
 static u32 netlink_group_mask(u32 group)
@@ -331,6 +333,11 @@ netlink_update_listeners(struct sock *sk)
        struct hlist_node *node;
        unsigned long mask;
        unsigned int i;
+       struct listeners *listeners;
+
+       listeners = nl_deref_protected(tbl->listeners);
+       if (!listeners)
+               return;
 
        for (i = 0; i < NLGRPLONGS(tbl->groups); i++) {
                mask = 0;
@@ -338,7 +345,7 @@ netlink_update_listeners(struct sock *sk)
                        if (i < NLGRPLONGS(nlk_sk(sk)->ngroups))
                                mask |= nlk_sk(sk)->groups[i];
                }
-               tbl->listeners->masks[i] = mask;
+               listeners->masks[i] = mask;
        }
        /* this function is only called with the netlink table "grabbed", which
         * makes sure updates are visible before bind or setsockopt return. */
@@ -519,7 +526,11 @@ static int netlink_release(struct socket *sock)
        if (netlink_is_kernel(sk)) {
                BUG_ON(nl_table[sk->sk_protocol].registered == 0);
                if (--nl_table[sk->sk_protocol].registered == 0) {
-                       kfree(nl_table[sk->sk_protocol].listeners);
+                       struct listeners *old;
+
+                       old = nl_deref_protected(nl_table[sk->sk_protocol].listeners);
+                       RCU_INIT_POINTER(nl_table[sk->sk_protocol].listeners, NULL);
+                       kfree_rcu(old, rcu);
                        nl_table[sk->sk_protocol].module = NULL;
                        nl_table[sk->sk_protocol].registered = 0;
                }
@@ -830,12 +841,19 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
        return 0;
 }
 
-int netlink_sendskb(struct sock *sk, struct sk_buff *skb)
+static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb)
 {
        int len = skb->len;
 
        skb_queue_tail(&sk->sk_receive_queue, skb);
        sk->sk_data_ready(sk, len);
+       return len;
+}
+
+int netlink_sendskb(struct sock *sk, struct sk_buff *skb)
+{
+       int len = __netlink_sendskb(sk, skb);
+
        sock_put(sk);
        return len;
 }
@@ -943,7 +961,7 @@ int netlink_has_listeners(struct sock *sk, unsigned int group)
        rcu_read_lock();
        listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
 
-       if (group - 1 < nl_table[sk->sk_protocol].groups)
+       if (listeners && group - 1 < nl_table[sk->sk_protocol].groups)
                res = test_bit(group - 1, listeners->masks);
 
        rcu_read_unlock();
@@ -960,8 +978,7 @@ static inline int netlink_broadcast_deliver(struct sock *sk,
        if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&
            !test_bit(0, &nlk->state)) {
                skb_set_owner_r(skb, sk);
-               skb_queue_tail(&sk->sk_receive_queue, skb);
-               sk->sk_data_ready(sk, skb->len);
+               __netlink_sendskb(sk, skb);
                return atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf;
        }
        return -1;
@@ -1339,7 +1356,8 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
                dst_pid = addr->nl_pid;
                dst_group = ffs(addr->nl_groups);
                err =  -EPERM;
-               if (dst_group && !netlink_capable(sock, NL_NONROOT_SEND))
+               if ((dst_group || dst_pid) &&
+                   !netlink_capable(sock, NL_NONROOT_SEND))
                        goto out;
        } else {
                dst_pid = nlk->dst_pid;
@@ -1578,7 +1596,7 @@ int __netlink_change_ngroups(struct sock *sk, unsigned int groups)
                new = kzalloc(sizeof(*new) + NLGRPSZ(groups), GFP_ATOMIC);
                if (!new)
                        return -ENOMEM;
-               old = rcu_dereference_raw(tbl->listeners);
+               old = nl_deref_protected(tbl->listeners);
                memcpy(new->masks, old->masks, NLGRPSZ(tbl->groups));
                rcu_assign_pointer(tbl->listeners, new);
 
@@ -1659,13 +1677,10 @@ static int netlink_dump(struct sock *sk)
 {
        struct netlink_sock *nlk = nlk_sk(sk);
        struct netlink_callback *cb;
-       struct sk_buff *skb;
+       struct sk_buff *skb = NULL;
        struct nlmsghdr *nlh;
        int len, err = -ENOBUFS;
-
-       skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL);
-       if (!skb)
-               goto errout;
+       int alloc_size;
 
        mutex_lock(nlk->cb_mutex);
 
@@ -1675,6 +1690,12 @@ static int netlink_dump(struct sock *sk)
                goto errout_skb;
        }
 
+       alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE);
+
+       skb = sock_rmalloc(sk, alloc_size, 0, GFP_KERNEL);
+       if (!skb)
+               goto errout;
+
        len = cb->dump(skb, cb);
 
        if (len > 0) {
@@ -1682,10 +1703,8 @@ static int netlink_dump(struct sock *sk)
 
                if (sk_filter(sk, skb))
                        kfree_skb(skb);
-               else {
-                       skb_queue_tail(&sk->sk_receive_queue, skb);
-                       sk->sk_data_ready(sk, skb->len);
-               }
+               else
+                       __netlink_sendskb(sk, skb);
                return 0;
        }
 
@@ -1697,10 +1716,8 @@ static int netlink_dump(struct sock *sk)
 
        if (sk_filter(sk, skb))
                kfree_skb(skb);
-       else {
-               skb_queue_tail(&sk->sk_receive_queue, skb);
-               sk->sk_data_ready(sk, skb->len);
-       }
+       else
+               __netlink_sendskb(sk, skb);
 
        if (cb->done)
                cb->done(cb);
@@ -1721,7 +1738,8 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
                       const struct nlmsghdr *nlh,
                       int (*dump)(struct sk_buff *skb,
                                   struct netlink_callback *),
-                      int (*done)(struct netlink_callback *))
+                      int (*done)(struct netlink_callback *),
+                      u16 min_dump_alloc)
 {
        struct netlink_callback *cb;
        struct sock *sk;
@@ -1735,6 +1753,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        cb->dump = dump;
        cb->done = done;
        cb->nlh = nlh;
+       cb->min_dump_alloc = min_dump_alloc;
        atomic_inc(&skb->users);
        cb->skb = skb;
 
@@ -2096,6 +2115,7 @@ static void __init netlink_add_usersock_entry(void)
        rcu_assign_pointer(nl_table[NETLINK_USERSOCK].listeners, listeners);
        nl_table[NETLINK_USERSOCK].module = THIS_MODULE;
        nl_table[NETLINK_USERSOCK].registered = 1;
+       nl_table[NETLINK_USERSOCK].nl_nonroot = NL_NONROOT_SEND;
 
        netlink_table_ungrab();
 }