fs3270: remove extra locks
[safe/jmp/linux-2.6] / net / ipv4 / inet_diag.c
index def007e..89cb047 100644 (file)
@@ -1,8 +1,6 @@
 /*
  * inet_diag.c Module for monitoring INET transport protocols sockets.
  *
- * Version:    $Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $
- *
  * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  *     This program is free software; you can redistribute it and/or
@@ -11,6 +9,7 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -50,6 +49,29 @@ static struct sock *idiagnl;
 #define INET_DIAG_PUT(skb, attrtype, attrlen) \
        RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
 
+static DEFINE_MUTEX(inet_diag_table_mutex);
+
+static const struct inet_diag_handler *inet_diag_lock_handler(int type)
+{
+#ifdef CONFIG_KMOD
+       if (!inet_diag_table[type])
+               request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
+                              NETLINK_INET_DIAG, type);
+#endif
+
+       mutex_lock(&inet_diag_table_mutex);
+       if (!inet_diag_table[type])
+               return ERR_PTR(-ENOENT);
+
+       return inet_diag_table[type];
+}
+
+static inline void inet_diag_unlock_handler(
+       const struct inet_diag_handler *handler)
+{
+       mutex_unlock(&inet_diag_table_mutex);
+}
+
 static int inet_csk_diag_fill(struct sock *sk,
                              struct sk_buff *skb,
                              int ext, u32 pid, u32 seq, u16 nlmsg_flags,
@@ -112,7 +134,7 @@ static int inet_csk_diag_fill(struct sock *sk,
        }
 #endif
 
-#define EXPIRES_IN_MS(tmo)  ((tmo - jiffies) * 1000 + HZ - 1) / HZ
+#define EXPIRES_IN_MS(tmo)  DIV_ROUND_UP((tmo - jiffies) * 1000, HZ)
 
        if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
                r->idiag_timer = 1;
@@ -190,7 +212,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
        r->id.idiag_dst[0]    = tw->tw_daddr;
        r->idiag_state        = tw->tw_substate;
        r->idiag_timer        = 3;
-       r->idiag_expires      = (tmo * 1000 + HZ - 1) / HZ;
+       r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
        r->idiag_rqueue       = 0;
        r->idiag_wqueue       = 0;
        r->idiag_uid          = 0;
@@ -234,18 +256,23 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
        struct inet_hashinfo *hashinfo;
        const struct inet_diag_handler *handler;
 
-       handler = inet_diag_table[nlh->nlmsg_type];
-       BUG_ON(handler == NULL);
+       handler = inet_diag_lock_handler(nlh->nlmsg_type);
+       if (IS_ERR(handler)) {
+               err = PTR_ERR(handler);
+               goto unlock;
+       }
+
        hashinfo = handler->idiag_hashinfo;
+       err = -EINVAL;
 
        if (req->idiag_family == AF_INET) {
-               sk = inet_lookup(hashinfo, req->id.idiag_dst[0],
+               sk = inet_lookup(&init_net, hashinfo, req->id.idiag_dst[0],
                                 req->id.idiag_dport, req->id.idiag_src[0],
                                 req->id.idiag_sport, req->id.idiag_if);
        }
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
        else if (req->idiag_family == AF_INET6) {
-               sk = inet6_lookup(hashinfo,
+               sk = inet6_lookup(&init_net, hashinfo,
                                  (struct in6_addr *)req->id.idiag_dst,
                                  req->id.idiag_dport,
                                  (struct in6_addr *)req->id.idiag_src,
@@ -254,11 +281,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
        }
 #endif
        else {
-               return -EINVAL;
+               goto unlock;
        }
 
+       err = -ENOENT;
        if (sk == NULL)
-               return -ENOENT;
+               goto unlock;
 
        err = -ESTALE;
        if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE ||
@@ -295,6 +323,8 @@ out:
                else
                        sock_put(sk);
        }
+unlock:
+       inet_diag_unlock_handler(handler);
        return err;
 }
 
@@ -677,8 +707,10 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
        const struct inet_diag_handler *handler;
        struct inet_hashinfo *hashinfo;
 
-       handler = inet_diag_table[cb->nlh->nlmsg_type];
-       BUG_ON(handler == NULL);
+       handler = inet_diag_lock_handler(cb->nlh->nlmsg_type);
+       if (IS_ERR(handler))
+               goto unlock;
+
        hashinfo = handler->idiag_hashinfo;
 
        s_i = cb->args[1];
@@ -742,18 +774,23 @@ skip_listen_ht:
        }
 
        if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
-               return skb->len;
+               goto unlock;
 
        for (i = s_i; i < hashinfo->ehash_size; i++) {
                struct inet_ehash_bucket *head = &hashinfo->ehash[i];
+               rwlock_t *lock = inet_ehash_lockp(hashinfo, i);
                struct sock *sk;
                struct hlist_node *node;
 
+               num = 0;
+
+               if (hlist_empty(&head->chain) && hlist_empty(&head->twchain))
+                       continue;
+
                if (i > s_i)
                        s_num = 0;
 
-               read_lock_bh(&head->lock);
-               num = 0;
+               read_lock_bh(lock);
                sk_for_each(sk, node, &head->chain) {
                        struct inet_sock *inet = inet_sk(sk);
 
@@ -768,7 +805,7 @@ skip_listen_ht:
                            r->id.idiag_dport)
                                goto next_normal;
                        if (inet_csk_diag_dump(sk, skb, cb) < 0) {
-                               read_unlock_bh(&head->lock);
+                               read_unlock_bh(lock);
                                goto done;
                        }
 next_normal:
@@ -790,19 +827,21 @@ next_normal:
                                    r->id.idiag_dport)
                                        goto next_dying;
                                if (inet_twsk_diag_dump(tw, skb, cb) < 0) {
-                                       read_unlock_bh(&head->lock);
+                                       read_unlock_bh(lock);
                                        goto done;
                                }
 next_dying:
                                ++num;
                        }
                }
-               read_unlock_bh(&head->lock);
+               read_unlock_bh(lock);
        }
 
 done:
        cb->args[1] = i;
        cb->args[2] = num;
+unlock:
+       inet_diag_unlock_handler(handler);
        return skb->len;
 }
 
@@ -814,9 +853,6 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
            nlmsg_len(nlh) < hdrlen)
                return -EINVAL;
 
-       if (inet_diag_table[nlh->nlmsg_type] == NULL)
-               return -ENOENT;
-
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                if (nlmsg_attrlen(nlh, hdrlen)) {
                        struct nlattr *attr;
@@ -838,19 +874,13 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 
 static DEFINE_MUTEX(inet_diag_mutex);
 
-static void inet_diag_rcv(struct sock *sk, int len)
+static void inet_diag_rcv(struct sk_buff *skb)
 {
-       unsigned int qlen = 0;
-
-       do {
-               mutex_lock(&inet_diag_mutex);
-               netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg);
-               mutex_unlock(&inet_diag_mutex);
-       } while (qlen);
+       mutex_lock(&inet_diag_mutex);
+       netlink_rcv_skb(skb, &inet_diag_rcv_msg);
+       mutex_unlock(&inet_diag_mutex);
 }
 
-static DEFINE_SPINLOCK(inet_diag_register_lock);
-
 int inet_diag_register(const struct inet_diag_handler *h)
 {
        const __u16 type = h->idiag_type;
@@ -859,13 +889,13 @@ int inet_diag_register(const struct inet_diag_handler *h)
        if (type >= INET_DIAG_GETSOCK_MAX)
                goto out;
 
-       spin_lock(&inet_diag_register_lock);
+       mutex_lock(&inet_diag_table_mutex);
        err = -EEXIST;
        if (inet_diag_table[type] == NULL) {
                inet_diag_table[type] = h;
                err = 0;
        }
-       spin_unlock(&inet_diag_register_lock);
+       mutex_unlock(&inet_diag_table_mutex);
 out:
        return err;
 }
@@ -878,11 +908,9 @@ void inet_diag_unregister(const struct inet_diag_handler *h)
        if (type >= INET_DIAG_GETSOCK_MAX)
                return;
 
-       spin_lock(&inet_diag_register_lock);
+       mutex_lock(&inet_diag_table_mutex);
        inet_diag_table[type] = NULL;
-       spin_unlock(&inet_diag_register_lock);
-
-       synchronize_rcu();
+       mutex_unlock(&inet_diag_table_mutex);
 }
 EXPORT_SYMBOL_GPL(inet_diag_unregister);
 
@@ -896,8 +924,8 @@ static int __init inet_diag_init(void)
        if (!inet_diag_table)
                goto out;
 
-       idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv,
-                                       NULL, THIS_MODULE);
+       idiagnl = netlink_kernel_create(&init_net, NETLINK_INET_DIAG, 0,
+                                       inet_diag_rcv, NULL, THIS_MODULE);
        if (idiagnl == NULL)
                goto out_free_table;
        err = 0;
@@ -910,10 +938,11 @@ out_free_table:
 
 static void __exit inet_diag_exit(void)
 {
-       sock_release(idiagnl->sk_socket);
+       netlink_kernel_release(idiagnl);
        kfree(inet_diag_table);
 }
 
 module_init(inet_diag_init);
 module_exit(inet_diag_exit);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_INET_DIAG);