basic modification from way back
[powerpc.git] / net / netlink / af_netlink.c
index e73d8f5..1f15821 100644 (file)
@@ -45,7 +45,6 @@
 #include <linux/rtnetlink.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-#include <linux/smp_lock.h>
 #include <linux/notifier.h>
 #include <linux/security.h>
 #include <linux/jhash.h>
@@ -56,6 +55,7 @@
 #include <linux/types.h>
 #include <linux/audit.h>
 #include <linux/selinux.h>
+#include <linux/mutex.h>
 
 #include <net/sock.h>
 #include <net/scm.h>
@@ -76,7 +76,8 @@ struct netlink_sock {
        unsigned long           state;
        wait_queue_head_t       wait;
        struct netlink_callback *cb;
-       spinlock_t              cb_lock;
+       struct mutex            *cb_mutex;
+       struct mutex            cb_def_mutex;
        void                    (*data_ready)(struct sock *sk, int bytes);
        struct module           *module;
 };
@@ -108,6 +109,7 @@ struct netlink_table {
        unsigned long *listeners;
        unsigned int nl_nonroot;
        unsigned int groups;
+       struct mutex *cb_mutex;
        struct module *module;
        int registered;
 };
@@ -118,6 +120,7 @@ static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait);
 
 static int netlink_dump(struct sock *sk);
 static void netlink_destroy_callback(struct netlink_callback *cb);
+static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb);
 
 static DEFINE_RWLOCK(nl_table_lock);
 static atomic_t nl_table_users = ATOMIC_INIT(0);
@@ -136,6 +139,14 @@ static struct hlist_head *nl_pid_hashfn(struct nl_pid_hash *hash, u32 pid)
 
 static void netlink_sock_destruct(struct sock *sk)
 {
+       struct netlink_sock *nlk = nlk_sk(sk);
+
+       if (nlk->cb) {
+               if (nlk->cb->done)
+                       nlk->cb->done(nlk->cb);
+               netlink_destroy_callback(nlk->cb);
+       }
+
        skb_queue_purge(&sk->sk_receive_queue);
 
        if (!sock_flag(sk, SOCK_DEAD)) {
@@ -144,7 +155,6 @@ static void netlink_sock_destruct(struct sock *sk)
        }
        BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
        BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(!nlk_sk(sk)->cb);
        BUG_TRAP(!nlk_sk(sk)->groups);
 }
 
@@ -370,7 +380,8 @@ static struct proto netlink_proto = {
        .obj_size = sizeof(struct netlink_sock),
 };
 
-static int __netlink_create(struct socket *sock, int protocol)
+static int __netlink_create(struct socket *sock, struct mutex *cb_mutex,
+                           int protocol)
 {
        struct sock *sk;
        struct netlink_sock *nlk;
@@ -384,7 +395,12 @@ static int __netlink_create(struct socket *sock, int protocol)
        sock_init_data(sock, sk);
 
        nlk = nlk_sk(sk);
-       spin_lock_init(&nlk->cb_lock);
+       if (cb_mutex)
+               nlk->cb_mutex = cb_mutex;
+       else {
+               nlk->cb_mutex = &nlk->cb_def_mutex;
+               mutex_init(nlk->cb_mutex);
+       }
        init_waitqueue_head(&nlk->wait);
 
        sk->sk_destruct = netlink_sock_destruct;
@@ -395,8 +411,8 @@ static int __netlink_create(struct socket *sock, int protocol)
 static int netlink_create(struct socket *sock, int protocol)
 {
        struct module *module = NULL;
+       struct mutex *cb_mutex;
        struct netlink_sock *nlk;
-       unsigned int groups;
        int err = 0;
 
        sock->state = SS_UNCONNECTED;
@@ -418,10 +434,10 @@ static int netlink_create(struct socket *sock, int protocol)
        if (nl_table[protocol].registered &&
            try_module_get(nl_table[protocol].module))
                module = nl_table[protocol].module;
-       groups = nl_table[protocol].groups;
+       cb_mutex = nl_table[protocol].cb_mutex;
        netlink_unlock_table();
 
-       if ((err = __netlink_create(sock, protocol)) < 0)
+       if ((err = __netlink_create(sock, cb_mutex, protocol)) < 0)
                goto out_module;
 
        nlk = nlk_sk(sock->sk);
@@ -443,21 +459,14 @@ static int netlink_release(struct socket *sock)
                return 0;
 
        netlink_remove(sk);
+       sock_orphan(sk);
        nlk = nlk_sk(sk);
 
-       spin_lock(&nlk->cb_lock);
-       if (nlk->cb) {
-               if (nlk->cb->done)
-                       nlk->cb->done(nlk->cb);
-               netlink_destroy_callback(nlk->cb);
-               nlk->cb = NULL;
-       }
-       spin_unlock(&nlk->cb_lock);
-
-       /* OK. Socket is unlinked, and, therefore,
-          no new packets will arrive */
+       /*
+        * OK. Socket is unlinked, any packets that arrive now
+        * will be purged.
+        */
 
-       sock_orphan(sk);
        sock->sk = NULL;
        wake_up_interruptible_all(&nlk->wait);
 
@@ -1215,7 +1224,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
                copied = len;
        }
 
-       skb->h.raw = skb->data;
+       skb_reset_transport_header(skb);
        err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
        if (msg->msg_name) {
@@ -1235,13 +1244,14 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
                siocb->scm = &scm;
        }
        siocb->scm->creds = *NETLINK_CREDS(skb);
+       if (flags & MSG_TRUNC)
+               copied = skb->len;
        skb_free_datagram(sk, skb);
 
        if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2)
                netlink_dump(sk);
 
        scm_recv(sock, msg, siocb->scm, flags);
-
 out:
        netlink_rcv_wake(sk);
        return err ? : copied;
@@ -1265,7 +1275,7 @@ static void netlink_data_ready(struct sock *sk, int len)
 struct sock *
 netlink_kernel_create(int unit, unsigned int groups,
                      void (*input)(struct sock *sk, int len),
-                     struct module *module)
+                     struct mutex *cb_mutex, struct module *module)
 {
        struct socket *sock;
        struct sock *sk;
@@ -1280,7 +1290,7 @@ netlink_kernel_create(int unit, unsigned int groups,
        if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
                return NULL;
 
-       if (__netlink_create(sock, unit) < 0)
+       if (__netlink_create(sock, cb_mutex, unit) < 0)
                goto out_sock_release;
 
        if (groups < 32)
@@ -1304,6 +1314,7 @@ netlink_kernel_create(int unit, unsigned int groups,
        netlink_table_grab();
        nl_table[unit].groups = groups;
        nl_table[unit].listeners = listeners;
+       nl_table[unit].cb_mutex = cb_mutex;
        nl_table[unit].module = module;
        nl_table[unit].registered = 1;
        netlink_table_ungrab();
@@ -1346,7 +1357,7 @@ static int netlink_dump(struct sock *sk)
        if (!skb)
                goto errout;
 
-       spin_lock(&nlk->cb_lock);
+       mutex_lock(nlk->cb_mutex);
 
        cb = nlk->cb;
        if (cb == NULL) {
@@ -1357,7 +1368,7 @@ static int netlink_dump(struct sock *sk)
        len = cb->dump(skb, cb);
 
        if (len > 0) {
-               spin_unlock(&nlk->cb_lock);
+               mutex_unlock(nlk->cb_mutex);
                skb_queue_tail(&sk->sk_receive_queue, skb);
                sk->sk_data_ready(sk, len);
                return 0;
@@ -1375,13 +1386,13 @@ static int netlink_dump(struct sock *sk)
        if (cb->done)
                cb->done(cb);
        nlk->cb = NULL;
-       spin_unlock(&nlk->cb_lock);
+       mutex_unlock(nlk->cb_mutex);
 
        netlink_destroy_callback(cb);
        return 0;
 
 errout_skb:
-       spin_unlock(&nlk->cb_lock);
+       mutex_unlock(nlk->cb_mutex);
        kfree_skb(skb);
 errout:
        return err;
@@ -1413,19 +1424,24 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        }
        nlk = nlk_sk(sk);
        /* A dump is in progress... */
-       spin_lock(&nlk->cb_lock);
+       mutex_lock(nlk->cb_mutex);
        if (nlk->cb) {
-               spin_unlock(&nlk->cb_lock);
+               mutex_unlock(nlk->cb_mutex);
                netlink_destroy_callback(cb);
                sock_put(sk);
                return -EBUSY;
        }
        nlk->cb = cb;
-       spin_unlock(&nlk->cb_lock);
+       mutex_unlock(nlk->cb_mutex);
 
        netlink_dump(sk);
        sock_put(sk);
-       return 0;
+
+       /* We successfully started a dump, by returning -EINTR we
+        * signal the queue mangement to interrupt processing of
+        * any netlink messages so userspace gets a chance to read
+        * the results. */
+       return -EINTR;
 }
 
 void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
@@ -1462,27 +1478,35 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
 }
 
 static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
-                                                    struct nlmsghdr *, int *))
+                                                    struct nlmsghdr *))
 {
        struct nlmsghdr *nlh;
        int err;
 
        while (skb->len >= nlmsg_total_size(0)) {
-               nlh = (struct nlmsghdr *) skb->data;
+               nlh = nlmsg_hdr(skb);
+               err = 0;
 
                if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)
                        return 0;
 
-               if (cb(skb, nlh, &err) < 0) {
-                       /* Not an error, but we have to interrupt processing
-                        * here. Note: that in this case we do not pull
-                        * message from skb, it will be processed later.
-                        */
-                       if (err == 0)
-                               return -1;
+               /* Only requests are handled by the kernel */
+               if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
+                       goto skip;
+
+               /* Skip control messages */
+               if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
+                       goto skip;
+
+               err = cb(skb, nlh);
+               if (err == -EINTR) {
+                       /* Not an error, but we interrupt processing */
+                       netlink_queue_skip(nlh, skb);
+                       return err;
+               }
+skip:
+               if (nlh->nlmsg_flags & NLM_F_ACK || err)
                        netlink_ack(skb, nlh, err);
-               } else if (nlh->nlmsg_flags & NLM_F_ACK)
-                       netlink_ack(skb, nlh, 0);
 
                netlink_queue_skip(nlh, skb);
        }
@@ -1504,9 +1528,14 @@ static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
  *
  * qlen must be initialized to 0 before the initial entry, afterwards
  * the function may be called repeatedly until qlen reaches 0.
+ *
+ * The callback function may return -EINTR to signal that processing
+ * of netlink messages shall be interrupted. In this case the message
+ * currently being processed will NOT be requeued onto the receive
+ * queue.
  */
 void netlink_run_queue(struct sock *sk, unsigned int *qlen,
-                      int (*cb)(struct sk_buff *, struct nlmsghdr *, int *))
+                      int (*cb)(struct sk_buff *, struct nlmsghdr *))
 {
        struct sk_buff *skb;
 
@@ -1537,7 +1566,7 @@ void netlink_run_queue(struct sock *sk, unsigned int *qlen,
  * Pulls the given netlink message off the socket buffer so the next
  * call to netlink_queue_run() will not reconsider the message.
  */
-void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb)
+static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb)
 {
        int msglen = NLMSG_ALIGN(nlh->nlmsg_len);
 
@@ -1820,12 +1849,10 @@ core_initcall(netlink_proto_init);
 
 EXPORT_SYMBOL(netlink_ack);
 EXPORT_SYMBOL(netlink_run_queue);
-EXPORT_SYMBOL(netlink_queue_skip);
 EXPORT_SYMBOL(netlink_broadcast);
 EXPORT_SYMBOL(netlink_dump_start);
 EXPORT_SYMBOL(netlink_kernel_create);
 EXPORT_SYMBOL(netlink_register_notifier);
-EXPORT_SYMBOL(netlink_set_err);
 EXPORT_SYMBOL(netlink_set_nonroot);
 EXPORT_SYMBOL(netlink_unicast);
 EXPORT_SYMBOL(netlink_unregister_notifier);