X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=net%2Fipv4%2Finet_diag.c;h=e468e7a7aac4d8c7940955eac466af2e6303858f;hb=19295529db35381d46dbaf246f69b4e3b3393996;hp=def007ec1d6f85b3f1f9830554c8a4c0be70a905;hpb=973ed7c49a5c2271a10ce54ac06bba5617fca3a8;p=linux-2.6-omap-h63xx.git diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index def007ec1d6..e468e7a7aac 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -11,6 +11,7 @@ * 2 of the License, or (at your option) any later version. */ +#include #include #include #include @@ -50,6 +51,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 +136,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 +214,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,9 +258,12 @@ 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 (!handler) + return -ENOENT; + hashinfo = handler->idiag_hashinfo; + err = -EINVAL; if (req->idiag_family == AF_INET) { sk = inet_lookup(hashinfo, req->id.idiag_dst[0], @@ -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 (!handler) + goto no_handler; + hashinfo = handler->idiag_hashinfo; s_i = cb->args[1]; @@ -742,17 +774,18 @@ 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; if (i > s_i) s_num = 0; - read_lock_bh(&head->lock); + read_lock_bh(lock); num = 0; sk_for_each(sk, node, &head->chain) { struct inet_sock *inet = inet_sk(sk); @@ -768,7 +801,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 +823,22 @@ 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); +no_handler: return skb->len; } @@ -814,9 +850,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 +871,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 +886,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 +905,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 +921,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; @@ -917,3 +942,4 @@ static void __exit inet_diag_exit(void) module_init(inet_diag_init); module_exit(inet_diag_exit); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_INET_DIAG);