]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/netlink/af_netlink.c
Merge branch 'master' into tk71
[mv-sheeva.git] / net / netlink / af_netlink.c
index cd96ed3ccee4602a9fee20464e4a54d3fb0783b2..1f924595bdefd8e6f632563b29aebf670c566869 100644 (file)
@@ -83,9 +83,9 @@ struct netlink_sock {
        struct module           *module;
 };
 
-struct listeners_rcu_head {
-       struct rcu_head rcu_head;
-       void *ptr;
+struct listeners {
+       struct rcu_head         rcu;
+       unsigned long           masks[0];
 };
 
 #define NETLINK_KERNEL_SOCKET  0x1
@@ -119,7 +119,7 @@ struct nl_pid_hash {
 struct netlink_table {
        struct nl_pid_hash hash;
        struct hlist_head mc_list;
-       unsigned long *listeners;
+       struct listeners __rcu *listeners;
        unsigned int nl_nonroot;
        unsigned int groups;
        struct mutex *cb_mutex;
@@ -338,7 +338,7 @@ netlink_update_listeners(struct sock *sk)
                        if (i < NLGRPLONGS(nlk_sk(sk)->ngroups))
                                mask |= nlk_sk(sk)->groups[i];
                }
-               tbl->listeners[i] = mask;
+               tbl->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. */
@@ -936,7 +936,7 @@ EXPORT_SYMBOL(netlink_unicast);
 int netlink_has_listeners(struct sock *sk, unsigned int group)
 {
        int res = 0;
-       unsigned long *listeners;
+       struct listeners *listeners;
 
        BUG_ON(!netlink_is_kernel(sk));
 
@@ -944,7 +944,7 @@ int netlink_has_listeners(struct sock *sk, unsigned int group)
        listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
 
        if (group - 1 < nl_table[sk->sk_protocol].groups)
-               res = test_bit(group - 1, listeners);
+               res = test_bit(group - 1, listeners->masks);
 
        rcu_read_unlock();
 
@@ -1407,7 +1407,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
        int noblock = flags&MSG_DONTWAIT;
        size_t copied;
        struct sk_buff *skb, *data_skb;
-       int err;
+       int err, ret;
 
        if (flags&MSG_OOB)
                return -EOPNOTSUPP;
@@ -1470,8 +1470,13 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
 
        skb_free_datagram(sk, skb);
 
-       if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2)
-               netlink_dump(sk);
+       if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) {
+               ret = netlink_dump(sk);
+               if (ret) {
+                       sk->sk_err = ret;
+                       sk->sk_error_report(sk);
+               }
+       }
 
        scm_recv(sock, msg, siocb->scm, flags);
 out:
@@ -1498,7 +1503,7 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups,
        struct socket *sock;
        struct sock *sk;
        struct netlink_sock *nlk;
-       unsigned long *listeners = NULL;
+       struct listeners *listeners = NULL;
 
        BUG_ON(!nl_table);
 
@@ -1523,8 +1528,7 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups,
        if (groups < 32)
                groups = 32;
 
-       listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head),
-                           GFP_KERNEL);
+       listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
        if (!listeners)
                goto out_sock_release;
 
@@ -1541,7 +1545,7 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups,
        netlink_table_grab();
        if (!nl_table[unit].registered) {
                nl_table[unit].groups = groups;
-               nl_table[unit].listeners = listeners;
+               rcu_assign_pointer(nl_table[unit].listeners, listeners);
                nl_table[unit].cb_mutex = cb_mutex;
                nl_table[unit].module = module;
                nl_table[unit].registered = 1;
@@ -1572,43 +1576,28 @@ netlink_kernel_release(struct sock *sk)
 EXPORT_SYMBOL(netlink_kernel_release);
 
 
-static void netlink_free_old_listeners(struct rcu_head *rcu_head)
+static void listeners_free_rcu(struct rcu_head *head)
 {
-       struct listeners_rcu_head *lrh;
-
-       lrh = container_of(rcu_head, struct listeners_rcu_head, rcu_head);
-       kfree(lrh->ptr);
+       kfree(container_of(head, struct listeners, rcu));
 }
 
 int __netlink_change_ngroups(struct sock *sk, unsigned int groups)
 {
-       unsigned long *listeners, *old = NULL;
-       struct listeners_rcu_head *old_rcu_head;
+       struct listeners *new, *old;
        struct netlink_table *tbl = &nl_table[sk->sk_protocol];
 
        if (groups < 32)
                groups = 32;
 
        if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) {
-               listeners = kzalloc(NLGRPSZ(groups) +
-                                   sizeof(struct listeners_rcu_head),
-                                   GFP_ATOMIC);
-               if (!listeners)
+               new = kzalloc(sizeof(*new) + NLGRPSZ(groups), GFP_ATOMIC);
+               if (!new)
                        return -ENOMEM;
-               old = tbl->listeners;
-               memcpy(listeners, old, NLGRPSZ(tbl->groups));
-               rcu_assign_pointer(tbl->listeners, listeners);
-               /*
-                * Free the old memory after an RCU grace period so we
-                * don't leak it. We use call_rcu() here in order to be
-                * able to call this function from atomic contexts. The
-                * allocation of this memory will have reserved enough
-                * space for struct listeners_rcu_head at the end.
-                */
-               old_rcu_head = (void *)(tbl->listeners +
-                                       NLGRPLONGS(tbl->groups));
-               old_rcu_head->ptr = old;
-               call_rcu(&old_rcu_head->rcu_head, netlink_free_old_listeners);
+               old = rcu_dereference_raw(tbl->listeners);
+               memcpy(new->masks, old->masks, NLGRPSZ(tbl->groups));
+               rcu_assign_pointer(tbl->listeners, new);
+
+               call_rcu(&old->rcu, listeners_free_rcu);
        }
        tbl->groups = groups;
 
@@ -1752,6 +1741,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        struct netlink_callback *cb;
        struct sock *sk;
        struct netlink_sock *nlk;
+       int ret;
 
        cb = kzalloc(sizeof(*cb), GFP_KERNEL);
        if (cb == NULL)
@@ -1780,9 +1770,13 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        nlk->cb = cb;
        mutex_unlock(nlk->cb_mutex);
 
-       netlink_dump(sk);
+       ret = netlink_dump(sk);
+
        sock_put(sk);
 
+       if (ret)
+               return ret;
+
        /* We successfully started a dump, by returning -EINTR we
         * signal not to send ACK even if it was requested.
         */
@@ -2104,18 +2098,17 @@ static void __net_exit netlink_net_exit(struct net *net)
 
 static void __init netlink_add_usersock_entry(void)
 {
-       unsigned long *listeners;
+       struct listeners *listeners;
        int groups = 32;
 
-       listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head),
-                           GFP_KERNEL);
+       listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
        if (!listeners)
-               panic("netlink_add_usersock_entry: Cannot allocate listneres\n");
+               panic("netlink_add_usersock_entry: Cannot allocate listeners\n");
 
        netlink_table_grab();
 
        nl_table[NETLINK_USERSOCK].groups = groups;
-       nl_table[NETLINK_USERSOCK].listeners = listeners;
+       rcu_assign_pointer(nl_table[NETLINK_USERSOCK].listeners, listeners);
        nl_table[NETLINK_USERSOCK].module = THIS_MODULE;
        nl_table[NETLINK_USERSOCK].registered = 1;