diff options
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/datagram.c | 54 | ||||
-rw-r--r-- | net/core/dev.c | 61 | ||||
-rw-r--r-- | net/core/dev_mcast.c | 28 | ||||
-rw-r--r-- | net/core/dst.c | 11 | ||||
-rw-r--r-- | net/core/fib_rules.c | 104 | ||||
-rw-r--r-- | net/core/flow.c | 7 | ||||
-rw-r--r-- | net/core/gen_estimator.c | 23 | ||||
-rw-r--r-- | net/core/gen_stats.c | 10 | ||||
-rw-r--r-- | net/core/neighbour.c | 262 | ||||
-rw-r--r-- | net/core/net-sysfs.c | 20 | ||||
-rw-r--r-- | net/core/net_namespace.c | 11 | ||||
-rw-r--r-- | net/core/netpoll.c | 62 | ||||
-rw-r--r-- | net/core/pktgen.c | 104 | ||||
-rw-r--r-- | net/core/request_sock.c | 5 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 56 | ||||
-rw-r--r-- | net/core/skbuff.c | 246 | ||||
-rw-r--r-- | net/core/sock.c | 200 | ||||
-rw-r--r-- | net/core/stream.c | 85 | ||||
-rw-r--r-- | net/core/sysctl_net_core.c | 70 | ||||
-rw-r--r-- | net/core/utils.c | 27 |
20 files changed, 842 insertions, 604 deletions
diff --git a/net/core/datagram.c b/net/core/datagram.c index 029b93e246b..8a28fc93b72 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -115,10 +115,10 @@ out_noerr: } /** - * skb_recv_datagram - Receive a datagram skbuff + * __skb_recv_datagram - Receive a datagram skbuff * @sk: socket * @flags: MSG_ flags - * @noblock: blocking operation? + * @peeked: returns non-zero if this packet has been seen before * @err: error code returned * * Get a datagram skbuff, understands the peeking, nonblocking wakeups @@ -143,8 +143,8 @@ out_noerr: * quite explicitly by POSIX 1003.1g, don't change them without having * the standard around please. */ -struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, - int noblock, int *err) +struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, + int *peeked, int *err) { struct sk_buff *skb; long timeo; @@ -156,7 +156,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, if (error) goto no_packet; - timeo = sock_rcvtimeo(sk, noblock); + timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); do { /* Again only user level code calls this function, so nothing @@ -165,18 +165,19 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, * Look at current nfs client by the way... * However, this function was corrent in any case. 8) */ - if (flags & MSG_PEEK) { - unsigned long cpu_flags; - - spin_lock_irqsave(&sk->sk_receive_queue.lock, - cpu_flags); - skb = skb_peek(&sk->sk_receive_queue); - if (skb) + unsigned long cpu_flags; + + spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags); + skb = skb_peek(&sk->sk_receive_queue); + if (skb) { + *peeked = skb->peeked; + if (flags & MSG_PEEK) { + skb->peeked = 1; atomic_inc(&skb->users); - spin_unlock_irqrestore(&sk->sk_receive_queue.lock, - cpu_flags); - } else - skb = skb_dequeue(&sk->sk_receive_queue); + } else + __skb_unlink(skb, &sk->sk_receive_queue); + } + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags); if (skb) return skb; @@ -194,10 +195,21 @@ no_packet: *err = error; return NULL; } +EXPORT_SYMBOL(__skb_recv_datagram); + +struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, + int noblock, int *err) +{ + int peeked; + + return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), + &peeked, err); +} void skb_free_datagram(struct sock *sk, struct sk_buff *skb) { kfree_skb(skb); + sk_mem_reclaim(sk); } /** @@ -217,20 +229,28 @@ void skb_free_datagram(struct sock *sk, struct sk_buff *skb) * This function currently only disables BH when acquiring the * sk_receive_queue lock. Therefore it must not be used in a * context where that lock is acquired in an IRQ context. + * + * It returns 0 if the packet was removed by us. */ -void skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags) +int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags) { + int err = 0; + if (flags & MSG_PEEK) { + err = -ENOENT; spin_lock_bh(&sk->sk_receive_queue.lock); if (skb == skb_peek(&sk->sk_receive_queue)) { __skb_unlink(skb, &sk->sk_receive_queue); atomic_dec(&skb->users); + err = 0; } spin_unlock_bh(&sk->sk_receive_queue.lock); } kfree_skb(skb); + sk_mem_reclaim(sk); + return err; } EXPORT_SYMBOL(skb_kill_datagram); diff --git a/net/core/dev.c b/net/core/dev.c index 0879f52115e..c9c593e1ba6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -150,8 +150,11 @@ * 86DD IPv6 */ +#define PTYPE_HASH_SIZE (16) +#define PTYPE_HASH_MASK (PTYPE_HASH_SIZE - 1) + static DEFINE_SPINLOCK(ptype_lock); -static struct list_head ptype_base[16] __read_mostly; /* 16 way hashed list */ +static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; static struct list_head ptype_all __read_mostly; /* Taps */ #ifdef CONFIG_NET_DMA @@ -362,7 +365,7 @@ void dev_add_pack(struct packet_type *pt) if (pt->type == htons(ETH_P_ALL)) list_add_rcu(&pt->list, &ptype_all); else { - hash = ntohs(pt->type) & 15; + hash = ntohs(pt->type) & PTYPE_HASH_MASK; list_add_rcu(&pt->list, &ptype_base[hash]); } spin_unlock_bh(&ptype_lock); @@ -391,7 +394,7 @@ void __dev_remove_pack(struct packet_type *pt) if (pt->type == htons(ETH_P_ALL)) head = &ptype_all; else - head = &ptype_base[ntohs(pt->type) & 15]; + head = &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK]; list_for_each_entry(pt1, head, list) { if (pt == pt1) { @@ -672,7 +675,7 @@ struct net_device *dev_getbyhwaddr(struct net *net, unsigned short type, char *h ASSERT_RTNL(); - for_each_netdev(&init_net, dev) + for_each_netdev(net, dev) if (dev->type == type && !memcmp(dev->dev_addr, ha, dev->addr_len)) return dev; @@ -1420,7 +1423,8 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) } rcu_read_lock(); - list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { + list_for_each_entry_rcu(ptype, + &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) { if (ptype->type == type && !ptype->dev && ptype->gso_segment) { if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { err = ptype->gso_send_check(skb); @@ -2077,7 +2081,8 @@ ncls: goto out; type = skb->protocol; - list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) { + list_for_each_entry_rcu(ptype, + &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) { if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) { if (pt_prev) @@ -2363,8 +2368,9 @@ static int dev_ifconf(struct net *net, char __user *arg) * in detail. */ void *dev_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(dev_base_lock) { - struct net *net = seq->private; + struct net *net = seq_file_net(seq); loff_t off; struct net_device *dev; @@ -2382,13 +2388,14 @@ void *dev_seq_start(struct seq_file *seq, loff_t *pos) void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) { - struct net *net = seq->private; + struct net *net = seq_file_net(seq); ++*pos; return v == SEQ_START_TOKEN ? first_net_device(net) : next_net_device((struct net_device *)v); } void dev_seq_stop(struct seq_file *seq, void *v) + __releases(dev_base_lock) { read_unlock(&dev_base_lock); } @@ -2481,26 +2488,8 @@ static const struct seq_operations dev_seq_ops = { static int dev_seq_open(struct inode *inode, struct file *file) { - struct seq_file *seq; - int res; - res = seq_open(file, &dev_seq_ops); - if (!res) { - seq = file->private_data; - seq->private = get_proc_net(inode); - if (!seq->private) { - seq_release(inode, file); - res = -ENXIO; - } - } - return res; -} - -static int dev_seq_release(struct inode *inode, struct file *file) -{ - struct seq_file *seq = file->private_data; - struct net *net = seq->private; - put_net(net); - return seq_release(inode, file); + return seq_open_net(inode, file, &dev_seq_ops, + sizeof(struct seq_net_private)); } static const struct file_operations dev_seq_fops = { @@ -2508,7 +2497,7 @@ static const struct file_operations dev_seq_fops = { .open = dev_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = dev_seq_release, + .release = seq_release_net, }; static const struct seq_operations softnet_seq_ops = { @@ -2543,7 +2532,7 @@ static void *ptype_get_idx(loff_t pos) ++i; } - for (t = 0; t < 16; t++) { + for (t = 0; t < PTYPE_HASH_SIZE; t++) { list_for_each_entry_rcu(pt, &ptype_base[t], list) { if (i == pos) return pt; @@ -2554,6 +2543,7 @@ static void *ptype_get_idx(loff_t pos) } static void *ptype_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(RCU) { rcu_read_lock(); return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN; @@ -2577,10 +2567,10 @@ static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos) hash = 0; nxt = ptype_base[0].next; } else - hash = ntohs(pt->type) & 15; + hash = ntohs(pt->type) & PTYPE_HASH_MASK; while (nxt == &ptype_base[hash]) { - if (++hash >= 16) + if (++hash >= PTYPE_HASH_SIZE) return NULL; nxt = ptype_base[hash].next; } @@ -2589,6 +2579,7 @@ found: } static void ptype_seq_stop(struct seq_file *seq, void *v) + __releases(RCU) { rcu_read_unlock(); } @@ -3505,7 +3496,7 @@ static int dev_new_index(struct net *net) /* Delayed registration/unregisteration */ static DEFINE_SPINLOCK(net_todo_list_lock); -static struct list_head net_todo_list = LIST_HEAD_INIT(net_todo_list); +static LIST_HEAD(net_todo_list); static void net_set_todo(struct net_device *dev) { @@ -3984,6 +3975,8 @@ void synchronize_net(void) void unregister_netdevice(struct net_device *dev) { + ASSERT_RTNL(); + rollback_registered(dev); /* Finish processing unregister after unlock */ net_set_todo(dev); @@ -4416,7 +4409,7 @@ static int __init net_dev_init(void) goto out; INIT_LIST_HEAD(&ptype_all); - for (i = 0; i < 16; i++) + for (i = 0; i < PTYPE_HASH_SIZE; i++) INIT_LIST_HEAD(&ptype_base[i]); if (register_pernet_subsys(&netdev_net_ops)) diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 69fff16ece1..cadbfbf7e7f 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -186,8 +186,9 @@ EXPORT_SYMBOL(dev_mc_unsync); #ifdef CONFIG_PROC_FS static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(dev_base_lock) { - struct net *net = seq->private; + struct net *net = seq_file_net(seq); struct net_device *dev; loff_t off = 0; @@ -206,6 +207,7 @@ static void *dev_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void dev_mc_seq_stop(struct seq_file *seq, void *v) + __releases(dev_base_lock) { read_unlock(&dev_base_lock); } @@ -241,26 +243,8 @@ static const struct seq_operations dev_mc_seq_ops = { static int dev_mc_seq_open(struct inode *inode, struct file *file) { - struct seq_file *seq; - int res; - res = seq_open(file, &dev_mc_seq_ops); - if (!res) { - seq = file->private_data; - seq->private = get_proc_net(inode); - if (!seq->private) { - seq_release(inode, file); - res = -ENXIO; - } - } - return res; -} - -static int dev_mc_seq_release(struct inode *inode, struct file *file) -{ - struct seq_file *seq = file->private_data; - struct net *net = seq->private; - put_net(net); - return seq_release(inode, file); + return seq_open_net(inode, file, &dev_mc_seq_ops, + sizeof(struct seq_net_private)); } static const struct file_operations dev_mc_seq_fops = { @@ -268,7 +252,7 @@ static const struct file_operations dev_mc_seq_fops = { .open = dev_mc_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = dev_mc_seq_release, + .release = seq_release_net, }; #endif diff --git a/net/core/dst.c b/net/core/dst.c index 03daead3592..7deef483c79 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -153,18 +153,19 @@ loop: #endif } -static int dst_discard(struct sk_buff *skb) +int dst_discard(struct sk_buff *skb) { kfree_skb(skb); return 0; } +EXPORT_SYMBOL(dst_discard); void * dst_alloc(struct dst_ops * ops) { struct dst_entry * dst; if (ops->gc && atomic_read(&ops->entries) > ops->gc_thresh) { - if (ops->gc()) + if (ops->gc(ops)) return NULL; } dst = kmem_cache_zalloc(ops->kmem_cachep, GFP_ATOMIC); @@ -278,13 +279,13 @@ static inline void dst_ifdown(struct dst_entry *dst, struct net_device *dev, if (!unregister) { dst->input = dst->output = dst_discard; } else { - dst->dev = init_net.loopback_dev; + dst->dev = dst->dev->nd_net->loopback_dev; dev_hold(dst->dev); dev_put(dev); if (dst->neighbour && dst->neighbour->dev == dev) { - dst->neighbour->dev = init_net.loopback_dev; + dst->neighbour->dev = dst->dev; + dev_hold(dst->dev); dev_put(dev); - dev_hold(dst->neighbour->dev); } } } diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 848132b6cb7..42ccaf5b850 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -15,9 +15,6 @@ #include <net/sock.h> #include <net/fib_rules.h> -static LIST_HEAD(rules_ops); -static DEFINE_SPINLOCK(rules_mod_lock); - int fib_default_rule_add(struct fib_rules_ops *ops, u32 pref, u32 table, u32 flags) { @@ -32,6 +29,7 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->pref = pref; r->table = table; r->flags = flags; + r->fr_net = ops->fro_net; /* The lock is not required here, the list in unreacheable * at the moment this function is called */ @@ -44,12 +42,12 @@ static void notify_rule_change(int event, struct fib_rule *rule, struct fib_rules_ops *ops, struct nlmsghdr *nlh, u32 pid); -static struct fib_rules_ops *lookup_rules_ops(int family) +static struct fib_rules_ops *lookup_rules_ops(struct net *net, int family) { struct fib_rules_ops *ops; rcu_read_lock(); - list_for_each_entry_rcu(ops, &rules_ops, list) { + list_for_each_entry_rcu(ops, &net->rules_ops, list) { if (ops->family == family) { if (!try_module_get(ops->owner)) ops = NULL; @@ -78,6 +76,9 @@ int fib_rules_register(struct fib_rules_ops *ops) { int err = -EEXIST; struct fib_rules_ops *o; + struct net *net; + + net = ops->fro_net; if (ops->rule_size < sizeof(struct fib_rule)) return -EINVAL; @@ -87,22 +88,23 @@ int fib_rules_register(struct fib_rules_ops *ops) ops->action == NULL) return -EINVAL; - spin_lock(&rules_mod_lock); - list_for_each_entry(o, &rules_ops, list) + spin_lock(&net->rules_mod_lock); + list_for_each_entry(o, &net->rules_ops, list) if (ops->family == o->family) goto errout; - list_add_tail_rcu(&ops->list, &rules_ops); + hold_net(net); + list_add_tail_rcu(&ops->list, &net->rules_ops); err = 0; errout: - spin_unlock(&rules_mod_lock); + spin_unlock(&net->rules_mod_lock); return err; } EXPORT_SYMBOL_GPL(fib_rules_register); -static void cleanup_ops(struct fib_rules_ops *ops) +void fib_rules_cleanup_ops(struct fib_rules_ops *ops) { struct fib_rule *rule, *tmp; @@ -111,28 +113,19 @@ static void cleanup_ops(struct fib_rules_ops *ops) fib_rule_put(rule); } } +EXPORT_SYMBOL_GPL(fib_rules_cleanup_ops); -int fib_rules_unregister(struct fib_rules_ops *ops) +void fib_rules_unregister(struct fib_rules_ops *ops) { - int err = 0; - struct fib_rules_ops *o; - - spin_lock(&rules_mod_lock); - list_for_each_entry(o, &rules_ops, list) { - if (o == ops) { - list_del_rcu(&o->list); - cleanup_ops(ops); - goto out; - } - } + struct net *net = ops->fro_net; - err = -ENOENT; -out: - spin_unlock(&rules_mod_lock); + spin_lock(&net->rules_mod_lock); + list_del_rcu(&ops->list); + fib_rules_cleanup_ops(ops); + spin_unlock(&net->rules_mod_lock); synchronize_rcu(); - - return err; + release_net(net); } EXPORT_SYMBOL_GPL(fib_rules_unregister); @@ -231,7 +224,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) goto errout; - ops = lookup_rules_ops(frh->family); + ops = lookup_rules_ops(net, frh->family); if (ops == NULL) { err = EAFNOSUPPORT; goto errout; @@ -250,6 +243,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) err = -ENOMEM; goto errout; } + rule->fr_net = net; if (tb[FRA_PRIORITY]) rule->pref = nla_get_u32(tb[FRA_PRIORITY]); @@ -281,7 +275,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) rule->table = frh_get_table(frh, tb); if (!rule->pref && ops->default_pref) - rule->pref = ops->default_pref(); + rule->pref = ops->default_pref(ops); err = -EINVAL; if (tb[FRA_GOTO]) { @@ -358,6 +352,7 @@ errout: static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { + struct net *net = skb->sk->sk_net; struct fib_rule_hdr *frh = nlmsg_data(nlh); struct fib_rules_ops *ops = NULL; struct fib_rule *rule, *tmp; @@ -367,7 +362,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) goto errout; - ops = lookup_rules_ops(frh->family); + ops = lookup_rules_ops(net, frh->family); if (ops == NULL) { err = EAFNOSUPPORT; goto errout; @@ -539,13 +534,14 @@ skip: static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) { + struct net *net = skb->sk->sk_net; struct fib_rules_ops *ops; int idx = 0, family; family = rtnl_msg_family(cb->nlh); if (family != AF_UNSPEC) { /* Protocol specific dump request */ - ops = lookup_rules_ops(family); + ops = lookup_rules_ops(net, family); if (ops == NULL) return -EAFNOSUPPORT; @@ -553,7 +549,7 @@ static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) } rcu_read_lock(); - list_for_each_entry_rcu(ops, &rules_ops, list) { + list_for_each_entry_rcu(ops, &net->rules_ops, list) { if (idx < cb->args[0] || !try_module_get(ops->owner)) goto skip; @@ -574,9 +570,11 @@ static void notify_rule_change(int event, struct fib_rule *rule, struct fib_rules_ops *ops, struct nlmsghdr *nlh, u32 pid) { + struct net *net; struct sk_buff *skb; int err = -ENOBUFS; + net = ops->fro_net; skb = nlmsg_new(fib_rule_nlmsg_size(ops, rule), GFP_KERNEL); if (skb == NULL) goto errout; @@ -588,10 +586,11 @@ static void notify_rule_change(int event, struct fib_rule *rule, kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, pid, ops->nlgroup, nlh, GFP_KERNEL); + + err = rtnl_notify(skb, net, pid, ops->nlgroup, nlh, GFP_KERNEL); errout: if (err < 0) - rtnl_set_sk_err(ops->nlgroup, err); + rtnl_set_sk_err(net, ops->nlgroup, err); } static void attach_rules(struct list_head *rules, struct net_device *dev) @@ -619,22 +618,20 @@ static int fib_rules_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = ptr; + struct net *net = dev->nd_net; struct fib_rules_ops *ops; - if (dev->nd_net != &init_net) - return NOTIFY_DONE; - ASSERT_RTNL(); rcu_read_lock(); switch (event) { case NETDEV_REGISTER: - list_for_each_entry(ops, &rules_ops, list) + list_for_each_entry(ops, &net->rules_ops, list) attach_rules(&ops->rules_list, dev); break; case NETDEV_UNREGISTER: - list_for_each_entry(ops, &rules_ops, list) + list_for_each_entry(ops, &net->rules_ops, list) detach_rules(&ops->rules_list, dev); break; } @@ -648,13 +645,40 @@ static struct notifier_block fib_rules_notifier = { .notifier_call = fib_rules_event, }; +static int fib_rules_net_init(struct net *net) +{ + INIT_LIST_HEAD(&net->rules_ops); + spin_lock_init(&net->rules_mod_lock); + return 0; +} + +static struct pernet_operations fib_rules_net_ops = { + .init = fib_rules_net_init, +}; + static int __init fib_rules_init(void) { + int err; rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL); rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL); rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule); - return register_netdevice_notifier(&fib_rules_notifier); + err = register_netdevice_notifier(&fib_rules_notifier); + if (err < 0) + goto fail; + + err = register_pernet_subsys(&fib_rules_net_ops); + if (err < 0) + goto fail_unregister; + return 0; + +fail_unregister: + unregister_netdevice_notifier(&fib_rules_notifier); +fail: + rtnl_unregister(PF_UNSPEC, RTM_NEWRULE); + rtnl_unregister(PF_UNSPEC, RTM_DELRULE); + rtnl_unregister(PF_UNSPEC, RTM_GETRULE); + return err; } subsys_initcall(fib_rules_init); diff --git a/net/core/flow.c b/net/core/flow.c index 3ed2b4b1d6d..46b38e06e0d 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -293,7 +293,7 @@ void flow_cache_flush(void) static DEFINE_MUTEX(flow_flush_sem); /* Don't want cpus going down or up during this. */ - lock_cpu_hotplug(); + get_online_cpus(); mutex_lock(&flow_flush_sem); atomic_set(&info.cpuleft, num_online_cpus()); init_completion(&info.completion); @@ -305,7 +305,7 @@ void flow_cache_flush(void) wait_for_completion(&info.completion); mutex_unlock(&flow_flush_sem); - unlock_cpu_hotplug(); + put_online_cpus(); } static void __devinit flow_cache_cpu_prepare(int cpu) @@ -352,8 +352,7 @@ static int __init flow_cache_init(void) flow_lwm = 2 * flow_hash_size; flow_hwm = 4 * flow_hash_size; - init_timer(&flow_hash_rnd_timer); - flow_hash_rnd_timer.function = flow_cache_new_hashrnd; + setup_timer(&flow_hash_rnd_timer, flow_cache_new_hashrnd, 0); flow_hash_rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD; add_timer(&flow_hash_rnd_timer); diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index daadbcc4e8d..57abe8266be 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -135,7 +135,7 @@ skip: } if (!list_empty(&elist[idx].list)) - mod_timer(&elist[idx].timer, jiffies + ((HZ<<idx)/4)); + mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx)); rcu_read_unlock(); } @@ -159,13 +159,13 @@ skip: int gen_new_estimator(struct gnet_stats_basic *bstats, struct gnet_stats_rate_est *rate_est, spinlock_t *stats_lock, - struct rtattr *opt) + struct nlattr *opt) { struct gen_estimator *est; - struct gnet_estimator *parm = RTA_DATA(opt); + struct gnet_estimator *parm = nla_data(opt); int idx; - if (RTA_PAYLOAD(opt) < sizeof(*parm)) + if (nla_len(opt) < sizeof(*parm)) return -EINVAL; if (parm->interval < -2 || parm->interval > 3) @@ -191,7 +191,7 @@ int gen_new_estimator(struct gnet_stats_basic *bstats, } if (list_empty(&elist[idx].list)) - mod_timer(&elist[idx].timer, jiffies + ((HZ<<idx)/4)); + mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx)); list_add_rcu(&est->list, &elist[idx].list); return 0; @@ -241,7 +241,7 @@ void gen_kill_estimator(struct gnet_stats_basic *bstats, } /** - * gen_replace_estimator - replace rate estimator configruation + * gen_replace_estimator - replace rate estimator configuration * @bstats: basic statistics * @rate_est: rate estimator statistics * @stats_lock: statistics lock @@ -252,13 +252,12 @@ void gen_kill_estimator(struct gnet_stats_basic *bstats, * * Returns 0 on success or a negative error code. */ -int -gen_replace_estimator(struct gnet_stats_basic *bstats, - struct gnet_stats_rate_est *rate_est, spinlock_t *stats_lock, - struct rtattr *opt) +int gen_replace_estimator(struct gnet_stats_basic *bstats, + struct gnet_stats_rate_est *rate_est, + spinlock_t *stats_lock, struct nlattr *opt) { - gen_kill_estimator(bstats, rate_est); - return gen_new_estimator(bstats, rate_est, stats_lock, opt); + gen_kill_estimator(bstats, rate_est); + return gen_new_estimator(bstats, rate_est, stats_lock, opt); } diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c index bcc25591d8a..c3d0ffeac24 100644 --- a/net/core/gen_stats.c +++ b/net/core/gen_stats.c @@ -20,16 +20,17 @@ #include <linux/socket.h> #include <linux/rtnetlink.h> #include <linux/gen_stats.h> +#include <net/netlink.h> #include <net/gen_stats.h> static inline int gnet_stats_copy(struct gnet_dump *d, int type, void *buf, int size) { - RTA_PUT(d->skb, type, size, buf); + NLA_PUT(d->skb, type, size, buf); return 0; -rtattr_failure: +nla_put_failure: spin_unlock_bh(d->lock); return -1; } @@ -55,13 +56,14 @@ rtattr_failure: int gnet_stats_start_copy_compat(struct sk_buff *skb, int type, int tc_stats_type, int xstats_type, spinlock_t *lock, struct gnet_dump *d) + __acquires(lock) { memset(d, 0, sizeof(*d)); spin_lock_bh(lock); d->lock = lock; if (type) - d->tail = (struct rtattr *)skb_tail_pointer(skb); + d->tail = (struct nlattr *)skb_tail_pointer(skb); d->skb = skb; d->compat_tc_stats = tc_stats_type; d->compat_xstats = xstats_type; @@ -212,7 +214,7 @@ int gnet_stats_finish_copy(struct gnet_dump *d) { if (d->tail) - d->tail->rta_len = skb_tail_pointer(d->skb) - (u8 *)d->tail; + d->tail->nla_len = skb_tail_pointer(d->skb) - (u8 *)d->tail; if (d->compat_tc_stats) if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats, diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 29b8ee4e35d..a16cf1ec5e5 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -59,7 +59,6 @@ static void neigh_timer_handler(unsigned long arg); static void __neigh_notify(struct neighbour *n, int type, int flags); static void neigh_update_notify(struct neighbour *neigh); static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev); -void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); static struct neigh_table *neigh_tables; #ifdef CONFIG_PROC_FS @@ -165,6 +164,16 @@ static int neigh_forced_gc(struct neigh_table *tbl) return shrunk; } +static void neigh_add_timer(struct neighbour *n, unsigned long when) +{ + neigh_hold(n); + if (unlikely(mod_timer(&n->timer, when))) { + printk("NEIGH: BUG, double timer add, state is %x\n", + n->nud_state); + dump_stack(); + } +} + static int neigh_del_timer(struct neighbour *n) { if ((n->nud_state & NUD_IN_TIMER) && @@ -270,9 +279,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl) n->nud_state = NUD_NONE; n->output = neigh_blackhole; n->parms = neigh_parms_clone(&tbl->parms); - init_timer(&n->timer); - n->timer.function = neigh_timer_handler; - n->timer.data = (unsigned long)n; + setup_timer(&n->timer, neigh_timer_handler, (unsigned long)n); NEIGH_CACHE_STAT_INC(tbl, allocs); n->tbl = tbl; @@ -367,7 +374,8 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, return n; } -struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, const void *pkey) +struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, + const void *pkey) { struct neighbour *n; int key_len = tbl->key_len; @@ -377,7 +385,8 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, const void *pkey) read_lock_bh(&tbl->lock); for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) { - if (!memcmp(n->primary_key, pkey, key_len)) { + if (!memcmp(n->primary_key, pkey, key_len) && + (net == n->dev->nd_net)) { neigh_hold(n); NEIGH_CACHE_STAT_INC(tbl, hits); break; @@ -455,7 +464,8 @@ out_neigh_release: goto out; } -struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey, +struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, + struct net *net, const void *pkey, struct net_device *dev, int creat) { struct pneigh_entry *n; @@ -471,6 +481,7 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey, for (n = tbl->phash_buckets[hash_val]; n; n = n->next) { if (!memcmp(n->key, pkey, key_len) && + (n->net == net) && (n->dev == dev || !n->dev)) { read_unlock_bh(&tbl->lock); goto out; @@ -487,6 +498,7 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey, if (!n) goto out; + n->net = hold_net(net); memcpy(n->key, pkey, key_len); n->dev = dev; if (dev) @@ -509,7 +521,7 @@ out: } -int pneigh_delete(struct neigh_table *tbl, const void *pkey, +int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey, struct net_device *dev) { struct pneigh_entry *n, **np; @@ -524,13 +536,15 @@ int pneigh_delete(struct neigh_table *tbl, const void *pkey, write_lock_bh(&tbl->lock); for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL; np = &n->next) { - if (!memcmp(n->key, pkey, key_len) && n->dev == dev) { + if (!memcmp(n->key, pkey, key_len) && n->dev == dev && + (n->net == net)) { *np = n->next; write_unlock_bh(&tbl->lock); if (tbl->pdestructor) tbl->pdestructor(n); if (n->dev) dev_put(n->dev); + release_net(n->net); kfree(n); return 0; } @@ -553,6 +567,7 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev) tbl->pdestructor(n); if (n->dev) dev_put(n->dev); + release_net(n->net); kfree(n); continue; } @@ -562,6 +577,13 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev) return -ENOENT; } +static void neigh_parms_destroy(struct neigh_parms *parms); + +static inline void neigh_parms_put(struct neigh_parms *parms) +{ + if (atomic_dec_and_test(&parms->refcnt)) + neigh_parms_destroy(parms); +} |