From c7ac8679bec9397afe8918f788cbcef88c38da54 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Fri, 10 Jun 2011 01:27:09 +0000 Subject: rtnetlink: Compute and store minimum ifinfo dump size The message size allocated for rtnl ifinfo dumps was limited to a single page. This is not enough for additional interface info available with devices that support SR-IOV and caused a bug in which VF info would not be displayed if more than approximately 40 VFs were created per interface. Implement a new function pointer for the rtnl_register service that will calculate the amount of data required for the ifinfo dump and allocate enough data to satisfy the request. Signed-off-by: Greg Rose Signed-off-by: Jeff Kirsher --- net/core/neighbour.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 799f06e03a22..ceb505b1507c 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2909,12 +2909,13 @@ EXPORT_SYMBOL(neigh_sysctl_unregister); static int __init neigh_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL); - rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL); - rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info); + rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, NULL); - rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info); - rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, + NULL); + rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, NULL); return 0; } -- cgit v1.2.2 From cd0893369ca85fd11bc517081b2d9079d2ef2f90 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 11 Jul 2011 01:28:12 -0700 Subject: neigh: Store hash shift instead of mask. And mask the hash function result by simply shifting down the "->hash_shift" most significant bits. Currently which bits we use is arbitrary since jhash produces entropy evenly across the whole hash function result. But soon we'll be using universal hashing functions, and in those cases more entropy exists in the higher bits than the lower bits, because they use multiplies. Signed-off-by: David S. Miller --- net/core/neighbour.c | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index ceb505b1507c..4d5fc9433fd9 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -137,7 +137,7 @@ static int neigh_forced_gc(struct neigh_table *tbl) write_lock_bh(&tbl->lock); nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); - for (i = 0; i <= nht->hash_mask; i++) { + for (i = 0; i < (1 << nht->hash_shift); i++) { struct neighbour *n; struct neighbour __rcu **np; @@ -210,7 +210,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev) nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); - for (i = 0; i <= nht->hash_mask; i++) { + for (i = 0; i < (1 << nht->hash_shift); i++) { struct neighbour *n; struct neighbour __rcu **np = &nht->hash_buckets[i]; @@ -312,9 +312,9 @@ out_entries: goto out; } -static struct neigh_hash_table *neigh_hash_alloc(unsigned int entries) +static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift) { - size_t size = entries * sizeof(struct neighbour *); + size_t size = (1 << shift) * sizeof(struct neighbour *); struct neigh_hash_table *ret; struct neighbour __rcu **buckets; @@ -332,7 +332,7 @@ static struct neigh_hash_table *neigh_hash_alloc(unsigned int entries) return NULL; } ret->hash_buckets = buckets; - ret->hash_mask = entries - 1; + ret->hash_shift = shift; get_random_bytes(&ret->hash_rnd, sizeof(ret->hash_rnd)); return ret; } @@ -342,7 +342,7 @@ static void neigh_hash_free_rcu(struct rcu_head *head) struct neigh_hash_table *nht = container_of(head, struct neigh_hash_table, rcu); - size_t size = (nht->hash_mask + 1) * sizeof(struct neighbour *); + size_t size = (1 << nht->hash_shift) * sizeof(struct neighbour *); struct neighbour __rcu **buckets = nht->hash_buckets; if (size <= PAGE_SIZE) @@ -353,21 +353,20 @@ static void neigh_hash_free_rcu(struct rcu_head *head) } static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl, - unsigned long new_entries) + unsigned long new_shift) { unsigned int i, hash; struct neigh_hash_table *new_nht, *old_nht; NEIGH_CACHE_STAT_INC(tbl, hash_grows); - BUG_ON(!is_power_of_2(new_entries)); old_nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); - new_nht = neigh_hash_alloc(new_entries); + new_nht = neigh_hash_alloc(new_shift); if (!new_nht) return old_nht; - for (i = 0; i <= old_nht->hash_mask; i++) { + for (i = 0; i < (1 << old_nht->hash_shift); i++) { struct neighbour *n, *next; for (n = rcu_dereference_protected(old_nht->hash_buckets[i], @@ -377,7 +376,7 @@ static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl, hash = tbl->hash(n->primary_key, n->dev, new_nht->hash_rnd); - hash &= new_nht->hash_mask; + hash >>= (32 - new_nht->hash_shift); next = rcu_dereference_protected(n->next, lockdep_is_held(&tbl->lock)); @@ -406,7 +405,7 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, rcu_read_lock_bh(); nht = rcu_dereference_bh(tbl->nht); - hash_val = tbl->hash(pkey, dev, nht->hash_rnd) & nht->hash_mask; + hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift); for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); n != NULL; @@ -436,7 +435,7 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, rcu_read_lock_bh(); nht = rcu_dereference_bh(tbl->nht); - hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) & nht->hash_mask; + hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) >> (32 - nht->hash_shift); for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); n != NULL; @@ -492,10 +491,10 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); - if (atomic_read(&tbl->entries) > (nht->hash_mask + 1)) - nht = neigh_hash_grow(tbl, (nht->hash_mask + 1) << 1); + if (atomic_read(&tbl->entries) > (1 << nht->hash_shift)) + nht = neigh_hash_grow(tbl, nht->hash_shift + 1); - hash_val = tbl->hash(pkey, dev, nht->hash_rnd) & nht->hash_mask; + hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift); if (n->parms->dead) { rc = ERR_PTR(-EINVAL); @@ -784,7 +783,7 @@ static void neigh_periodic_work(struct work_struct *work) neigh_rand_reach_time(p->base_reachable_time); } - for (i = 0 ; i <= nht->hash_mask; i++) { + for (i = 0 ; i < (1 << nht->hash_shift); i++) { np = &nht->hash_buckets[i]; while ((n = rcu_dereference_protected(*np, @@ -1540,7 +1539,7 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl) panic("cannot create neighbour proc dir entry"); #endif - RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(8)); + RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3)); phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *); tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL); @@ -1857,7 +1856,7 @@ static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl, rcu_read_lock_bh(); nht = rcu_dereference_bh(tbl->nht); ndc.ndtc_hash_rnd = nht->hash_rnd; - ndc.ndtc_hash_mask = nht->hash_mask; + ndc.ndtc_hash_mask = ((1 << nht->hash_shift) - 1); rcu_read_unlock_bh(); NLA_PUT(skb, NDTA_CONFIG, sizeof(ndc), &ndc); @@ -2200,7 +2199,7 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, rcu_read_lock_bh(); nht = rcu_dereference_bh(tbl->nht); - for (h = 0; h <= nht->hash_mask; h++) { + for (h = 0; h < (1 << nht->hash_shift); h++) { if (h < s_h) continue; if (h > s_h) @@ -2264,7 +2263,7 @@ void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void nht = rcu_dereference_bh(tbl->nht); read_lock(&tbl->lock); /* avoid resizes */ - for (chain = 0; chain <= nht->hash_mask; chain++) { + for (chain = 0; chain < (1 << nht->hash_shift); chain++) { struct neighbour *n; for (n = rcu_dereference_bh(nht->hash_buckets[chain]); @@ -2286,7 +2285,7 @@ void __neigh_for_each_release(struct neigh_table *tbl, nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); - for (chain = 0; chain <= nht->hash_mask; chain++) { + for (chain = 0; chain < (1 << nht->hash_shift); chain++) { struct neighbour *n; struct neighbour __rcu **np; @@ -2323,7 +2322,7 @@ static struct neighbour *neigh_get_first(struct seq_file *seq) int bucket = state->bucket; state->flags &= ~NEIGH_SEQ_IS_PNEIGH; - for (bucket = 0; bucket <= nht->hash_mask; bucket++) { + for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) { n = rcu_dereference_bh(nht->hash_buckets[bucket]); while (n) { @@ -2390,7 +2389,7 @@ next: if (n) break; - if (++state->bucket > nht->hash_mask) + if (++state->bucket >= (1 << nht->hash_shift)) break; n = rcu_dereference_bh(nht->hash_buckets[state->bucket]); -- cgit v1.2.2 From f610b74b14d74a069f61583131e689550fd5bab3 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 11 Jul 2011 01:37:28 -0700 Subject: ipv4: Use universal hash for ARP. We need to make sure the multiplier is odd. Signed-off-by: David S. Miller --- net/core/neighbour.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 4d5fc9433fd9..50bd960983e0 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -334,6 +334,7 @@ static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift) ret->hash_buckets = buckets; ret->hash_shift = shift; get_random_bytes(&ret->hash_rnd, sizeof(ret->hash_rnd)); + ret->hash_rnd |= 1; return ret; } -- cgit v1.2.2 From e69dd336ee3a05a589629b505b18ba5e7a5b4c54 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 12 Jul 2011 23:28:12 -0700 Subject: net: Push protocol type directly down to header_ops->cache() Signed-off-by: David S. Miller --- net/core/neighbour.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 50bd960983e0..8f7e1d8d92a0 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1247,7 +1247,7 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, hh->hh_type = protocol; atomic_set(&hh->hh_refcnt, 2); - if (dev->header_ops->cache(n, hh)) { + if (dev->header_ops->cache(n, hh, protocol)) { kfree(hh); return; } -- cgit v1.2.2 From 5c25f686db352082eef8daa21b760192351a023a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 13 Jul 2011 00:51:10 -0700 Subject: net: Kill support for multiple hh_cache entries per neighbour This never, ever, happens. Neighbour entries are always tied to one address family, and therefore one set of dst_ops, and therefore one dst_ops->protocol "hh_type" value. This capability was blindly imported by Alexey Kuznetsov when he wrote the neighbour layer. Signed-off-by: David S. Miller --- net/core/neighbour.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 8f7e1d8d92a0..f879bb552994 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -702,9 +702,9 @@ void neigh_destroy(struct neighbour *neigh) if (neigh_del_timer(neigh)) printk(KERN_WARNING "Impossible event.\n"); - while ((hh = neigh->hh) != NULL) { - neigh->hh = hh->hh_next; - hh->hh_next = NULL; + hh = neigh->hh; + if (hh) { + neigh->hh = NULL; write_seqlock_bh(&hh->hh_lock); hh->hh_output = neigh_blackhole; @@ -737,7 +737,8 @@ static void neigh_suspect(struct neighbour *neigh) neigh->output = neigh->ops->output; - for (hh = neigh->hh; hh; hh = hh->hh_next) + hh = neigh->hh; + if (hh) hh->hh_output = neigh->ops->output; } @@ -754,7 +755,8 @@ static void neigh_connect(struct neighbour *neigh) neigh->output = neigh->ops->connected_output; - for (hh = neigh->hh; hh; hh = hh->hh_next) + hh = neigh->hh; + if (hh) hh->hh_output = neigh->ops->hh_output; } @@ -1025,7 +1027,8 @@ static void neigh_update_hhs(const struct neighbour *neigh) update = neigh->dev->header_ops->cache_update; if (update) { - for (hh = neigh->hh; hh; hh = hh->hh_next) { + hh = neigh->hh; + if (hh) { write_seqlock_bh(&hh->hh_lock); update(hh, neigh->dev, neigh->ha); write_sequnlock_bh(&hh->hh_lock); @@ -1211,19 +1214,17 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl, } EXPORT_SYMBOL(neigh_event_ns); -static inline bool neigh_hh_lookup(struct neighbour *n, struct dst_entry *dst, - __be16 protocol) +static inline bool neigh_hh_lookup(struct neighbour *n, struct dst_entry *dst) { struct hh_cache *hh; smp_rmb(); /* paired with smp_wmb() in neigh_hh_init() */ - for (hh = n->hh; hh; hh = hh->hh_next) { - if (hh->hh_type == protocol) { - atomic_inc(&hh->hh_refcnt); - if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL)) - hh_cache_put(hh); - return true; - } + hh = n->hh; + if (hh) { + atomic_inc(&hh->hh_refcnt); + if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL)) + hh_cache_put(hh); + return true; } return false; } @@ -1235,7 +1236,7 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, struct hh_cache *hh; struct net_device *dev = dst->dev; - if (likely(neigh_hh_lookup(n, dst, protocol))) + if (likely(neigh_hh_lookup(n, dst))) return; /* slow path */ @@ -1244,7 +1245,6 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, return; seqlock_init(&hh->hh_lock); - hh->hh_type = protocol; atomic_set(&hh->hh_refcnt, 2); if (dev->header_ops->cache(n, hh, protocol)) { @@ -1255,7 +1255,7 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, write_lock_bh(&n->lock); /* must check if another thread already did the insert */ - if (neigh_hh_lookup(n, dst, protocol)) { + if (neigh_hh_lookup(n, dst)) { kfree(hh); goto end; } @@ -1265,7 +1265,6 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, else hh->hh_output = n->ops->output; - hh->hh_next = n->hh; smp_wmb(); /* paired with smp_rmb() in neigh_hh_lookup() */ n->hh = hh; -- cgit v1.2.2 From f6b72b6217f8c24f2a54988e58af858b4e66024d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 14 Jul 2011 07:53:20 -0700 Subject: net: Embed hh_cache inside of struct neighbour. Now that there is a one-to-one correspondance between neighbour and hh_cache entries, we no longer need: 1) dynamic allocation 2) attachment to dst->hh 3) refcounting Initialization of the hh_cache entry is indicated by hh_len being non-zero, and such initialization is always done with the neighbour's lock held as a writer. Signed-off-by: David S. Miller --- net/core/neighbour.c | 81 ++++++++++++++-------------------------------------- 1 file changed, 22 insertions(+), 59 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index f879bb552994..77a399f2ad03 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -297,6 +297,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl) n->updated = n->used = now; n->nud_state = NUD_NONE; n->output = neigh_blackhole; + seqlock_init(&n->hh.hh_lock); n->parms = neigh_parms_clone(&tbl->parms); setup_timer(&n->timer, neigh_timer_handler, (unsigned long)n); @@ -702,14 +703,11 @@ void neigh_destroy(struct neighbour *neigh) if (neigh_del_timer(neigh)) printk(KERN_WARNING "Impossible event.\n"); - hh = neigh->hh; - if (hh) { - neigh->hh = NULL; - + hh = &neigh->hh; + if (hh->hh_len) { write_seqlock_bh(&hh->hh_lock); hh->hh_output = neigh_blackhole; write_sequnlock_bh(&hh->hh_lock); - hh_cache_put(hh); } skb_queue_purge(&neigh->arp_queue); @@ -737,8 +735,8 @@ static void neigh_suspect(struct neighbour *neigh) neigh->output = neigh->ops->output; - hh = neigh->hh; - if (hh) + hh = &neigh->hh; + if (hh->hh_len) hh->hh_output = neigh->ops->output; } @@ -755,8 +753,8 @@ static void neigh_connect(struct neighbour *neigh) neigh->output = neigh->ops->connected_output; - hh = neigh->hh; - if (hh) + hh = &neigh->hh; + if (hh->hh_len) hh->hh_output = neigh->ops->hh_output; } @@ -1017,7 +1015,7 @@ out_unlock_bh: } EXPORT_SYMBOL(__neigh_event_send); -static void neigh_update_hhs(const struct neighbour *neigh) +static void neigh_update_hhs(struct neighbour *neigh) { struct hh_cache *hh; void (*update)(struct hh_cache*, const struct net_device*, const unsigned char *) @@ -1027,8 +1025,8 @@ static void neigh_update_hhs(const struct neighbour *neigh) update = neigh->dev->header_ops->cache_update; if (update) { - hh = neigh->hh; - if (hh) { + hh = &neigh->hh; + if (hh->hh_len) { write_seqlock_bh(&hh->hh_lock); update(hh, neigh->dev, neigh->ha); write_sequnlock_bh(&hh->hh_lock); @@ -1214,62 +1212,29 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl, } EXPORT_SYMBOL(neigh_event_ns); -static inline bool neigh_hh_lookup(struct neighbour *n, struct dst_entry *dst) -{ - struct hh_cache *hh; - - smp_rmb(); /* paired with smp_wmb() in neigh_hh_init() */ - hh = n->hh; - if (hh) { - atomic_inc(&hh->hh_refcnt); - if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL)) - hh_cache_put(hh); - return true; - } - return false; -} - /* called with read_lock_bh(&n->lock); */ -static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, - __be16 protocol) +static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst) { - struct hh_cache *hh; struct net_device *dev = dst->dev; - - if (likely(neigh_hh_lookup(n, dst))) - return; - - /* slow path */ - hh = kzalloc(sizeof(*hh), GFP_ATOMIC); - if (!hh) - return; - - seqlock_init(&hh->hh_lock); - atomic_set(&hh->hh_refcnt, 2); - - if (dev->header_ops->cache(n, hh, protocol)) { - kfree(hh); - return; - } + __be16 prot = dst->ops->protocol; + struct hh_cache *hh = &n->hh; write_lock_bh(&n->lock); - /* must check if another thread already did the insert */ - if (neigh_hh_lookup(n, dst)) { - kfree(hh); + /* Only one thread can come in here and initialize the + * hh_cache entry. + */ + if (hh->hh_len) + goto end; + + if (dev->header_ops->cache(n, hh, prot)) goto end; - } if (n->nud_state & NUD_CONNECTED) hh->hh_output = n->ops->hh_output; else hh->hh_output = n->ops->output; - smp_wmb(); /* paired with smp_rmb() in neigh_hh_lookup() */ - n->hh = hh; - - if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL)) - hh_cache_put(hh); end: write_unlock_bh(&n->lock); } @@ -1312,10 +1277,8 @@ int neigh_resolve_output(struct sk_buff *skb) struct net_device *dev = neigh->dev; unsigned int seq; - if (dev->header_ops->cache && - !dst->hh && - !(dst->flags & DST_NOCACHE)) - neigh_hh_init(neigh, dst, dst->ops->protocol); + if (dev->header_ops->cache && !neigh->hh.hh_len) + neigh_hh_init(neigh, dst); do { seq = read_seqbegin(&neigh->ha_lock); -- cgit v1.2.2 From 0895b08adeb3f660cdff21990d0a9c2b59a919e7 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 16 Jul 2011 17:36:53 -0700 Subject: neigh: Simply destroy handling wrt. hh_cache. Now that hh_cache entries are embedded inside of neighbour entries, their lifetimes and accesses are now synchronous to that of the encompassing neighbour object. Therefore we don't need to hook up the blackhole op to hh_output on destroy. Signed-off-by: David S. Miller --- net/core/neighbour.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 77a399f2ad03..83f9998f3347 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -689,8 +689,6 @@ static void neigh_destroy_rcu(struct rcu_head *head) */ void neigh_destroy(struct neighbour *neigh) { - struct hh_cache *hh; - NEIGH_CACHE_STAT_INC(neigh->tbl, destroys); if (!neigh->dead) { @@ -703,13 +701,6 @@ void neigh_destroy(struct neighbour *neigh) if (neigh_del_timer(neigh)) printk(KERN_WARNING "Impossible event.\n"); - hh = &neigh->hh; - if (hh->hh_len) { - write_seqlock_bh(&hh->hh_lock); - hh->hh_output = neigh_blackhole; - write_sequnlock_bh(&hh->hh_lock); - } - skb_queue_purge(&neigh->arp_queue); dev_put(neigh->dev); -- cgit v1.2.2 From 47ec132a40d788d45e2f088545dea68798034dab Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 16 Jul 2011 17:39:57 -0700 Subject: neigh: Kill neigh_ops->hh_output It's always dev_queue_xmit(). Signed-off-by: David S. Miller --- net/core/neighbour.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 83f9998f3347..c22def5ae486 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -746,7 +746,7 @@ static void neigh_connect(struct neighbour *neigh) hh = &neigh->hh; if (hh->hh_len) - hh->hh_output = neigh->ops->hh_output; + hh->hh_output = dev_queue_xmit; } static void neigh_periodic_work(struct work_struct *work) @@ -1222,7 +1222,7 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst) goto end; if (n->nud_state & NUD_CONNECTED) - hh->hh_output = n->ops->hh_output; + hh->hh_output = dev_queue_xmit; else hh->hh_output = n->ops->output; -- cgit v1.2.2 From b23b5455b6458920179a1f27513ce42e70d11f37 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 16 Jul 2011 17:45:02 -0700 Subject: neigh: Kill hh_cache->hh_output It's just taking on one of two possible values, either neigh_ops->output or dev_queue_xmit(). And this is purely depending upon whether nud_state has NUD_CONNECTED set or not. Signed-off-by: David S. Miller --- net/core/neighbour.c | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index c22def5ae486..2feda6e7a31d 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -720,15 +720,9 @@ EXPORT_SYMBOL(neigh_destroy); */ static void neigh_suspect(struct neighbour *neigh) { - struct hh_cache *hh; - NEIGH_PRINTK2("neigh %p is suspected.\n", neigh); neigh->output = neigh->ops->output; - - hh = &neigh->hh; - if (hh->hh_len) - hh->hh_output = neigh->ops->output; } /* Neighbour state is OK; @@ -738,15 +732,9 @@ static void neigh_suspect(struct neighbour *neigh) */ static void neigh_connect(struct neighbour *neigh) { - struct hh_cache *hh; - NEIGH_PRINTK2("neigh %p is connected.\n", neigh); neigh->output = neigh->ops->connected_output; - - hh = &neigh->hh; - if (hh->hh_len) - hh->hh_output = dev_queue_xmit; } static void neigh_periodic_work(struct work_struct *work) @@ -1215,18 +1203,9 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst) /* Only one thread can come in here and initialize the * hh_cache entry. */ - if (hh->hh_len) - goto end; - - if (dev->header_ops->cache(n, hh, prot)) - goto end; - - if (n->nud_state & NUD_CONNECTED) - hh->hh_output = dev_queue_xmit; - else - hh->hh_output = n->ops->output; + if (!hh->hh_len) + dev->header_ops->cache(n, hh, prot); -end: write_unlock_bh(&n->lock); } -- cgit v1.2.2 From 542d4d685febf3110d1a08d0bcb9f6ef060b76f7 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 16 Jul 2011 18:06:24 -0700 Subject: neigh: Kill ndisc_ops->queue_xmit It is always dev_queue_xmit(). Signed-off-by: David S. Miller --- net/core/neighbour.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 2feda6e7a31d..b031cf63d6ad 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1257,7 +1257,7 @@ int neigh_resolve_output(struct sk_buff *skb) } while (read_seqretry(&neigh->ha_lock, seq)); if (err >= 0) - rc = neigh->ops->queue_xmit(skb); + rc = dev_queue_xmit(skb); else goto out_kfree_skb; } @@ -1292,7 +1292,7 @@ int neigh_connected_output(struct sk_buff *skb) } while (read_seqretry(&neigh->ha_lock, seq)); if (err >= 0) - err = neigh->ops->queue_xmit(skb); + err = dev_queue_xmit(skb); else { err = -EINVAL; kfree_skb(skb); -- cgit v1.2.2 From 8f40b161de4f27402b4c0659ad2ae83fad5a0cdd Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 17 Jul 2011 13:34:11 -0700 Subject: neigh: Pass neighbour entry to output ops. This will get us closer to being able to do "neigh stuff" completely independent of the underlying dst_entry for protocols (ipv4/ipv6) that wish to do so. We will also be able to make dst entries neigh-less. Signed-off-by: David S. Miller --- net/core/neighbour.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index b031cf63d6ad..cefb8e52615e 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -98,7 +98,7 @@ static const struct file_operations neigh_stat_seq_fops; static DEFINE_RWLOCK(neigh_tbl_lock); -static int neigh_blackhole(struct sk_buff *skb) +static int neigh_blackhole(struct neighbour *neigh, struct sk_buff *skb) { kfree_skb(skb); return -ENETDOWN; @@ -1158,7 +1158,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, /* On shaper/eql skb->dst->neighbour != neigh :( */ if (skb_dst(skb) && skb_dst(skb)->neighbour) n1 = skb_dst(skb)->neighbour; - n1->output(skb); + n1->output(n1, skb); write_lock_bh(&neigh->lock); } skb_queue_purge(&neigh->arp_queue); @@ -1214,7 +1214,7 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst) * but resolution is not made yet. */ -int neigh_compat_output(struct sk_buff *skb) +int neigh_compat_output(struct neighbour *neigh, struct sk_buff *skb) { struct net_device *dev = skb->dev; @@ -1231,13 +1231,12 @@ EXPORT_SYMBOL(neigh_compat_output); /* Slow and careful. */ -int neigh_resolve_output(struct sk_buff *skb) +int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); - struct neighbour *neigh; int rc = 0; - if (!dst || !(neigh = dst->neighbour)) + if (!dst) goto discard; __skb_pull(skb, skb_network_offset(skb)); @@ -1265,7 +1264,7 @@ out: return rc; discard: NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n", - dst, dst ? dst->neighbour : NULL); + dst, neigh); out_kfree_skb: rc = -EINVAL; kfree_skb(skb); @@ -1275,13 +1274,11 @@ EXPORT_SYMBOL(neigh_resolve_output); /* As fast as possible without hh cache */ -int neigh_connected_output(struct sk_buff *skb) +int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb) { - int err; - struct dst_entry *dst = skb_dst(skb); - struct neighbour *neigh = dst->neighbour; struct net_device *dev = neigh->dev; unsigned int seq; + int err; __skb_pull(skb, skb_network_offset(skb)); @@ -1301,6 +1298,12 @@ int neigh_connected_output(struct sk_buff *skb) } EXPORT_SYMBOL(neigh_connected_output); +int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb) +{ + return dev_queue_xmit(skb); +} +EXPORT_SYMBOL(neigh_direct_output); + static void neigh_proxy_process(unsigned long arg) { struct neigh_table *tbl = (struct neigh_table *)arg; -- cgit v1.2.2 From 69cce1d1404968f78b177a0314f5822d5afdbbfb Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 17 Jul 2011 23:09:49 -0700 Subject: net: Abstract dst->neighbour accesses behind helpers. dst_{get,set}_neighbour() Signed-off-by: David S. Miller --- net/core/neighbour.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net/core/neighbour.c') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index cefb8e52615e..8fab9b0bb203 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1153,11 +1153,12 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, while (neigh->nud_state & NUD_VALID && (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { - struct neighbour *n1 = neigh; + struct dst_entry *dst = skb_dst(skb); + struct neighbour *n2, *n1 = neigh; write_unlock_bh(&neigh->lock); /* On shaper/eql skb->dst->neighbour != neigh :( */ - if (skb_dst(skb) && skb_dst(skb)->neighbour) - n1 = skb_dst(skb)->neighbour; + if (dst && (n2 = dst_get_neighbour(dst)) != NULL) + n1 = n2; n1->output(n1, skb); write_lock_bh(&neigh->lock); } -- cgit v1.2.2