diff -Nur linux-2.4.28/include/net/neighbour.h linux-2.4.28.new/include/net/neighbour.h --- linux-2.4.28/include/net/neighbour.h 2004-11-17 12:15:31.000000000 -0500 +++ linux-2.4.28.new/include/net/neighbour.h 2004-12-09 09:54:27.000000000 -0500 @@ -5,13 +5,8 @@ * Generic neighbour manipulation * * Authors: - * Pedro Roque + * Pedro Roque * Alexey Kuznetsov - * - * Changes: - * - * Harald Welte: - * - Add neighbour cache statistics like rtstat */ /* The following flags & states are exported to user space, @@ -50,7 +45,6 @@ #include #include -#include #define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE) #define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) @@ -84,25 +78,12 @@ struct neigh_statistics { - unsigned long allocs; /* number of allocated neighs */ - unsigned long destroys; /* number of destroyed neighs */ - unsigned long hash_grows; /* number of hash resizes */ - - unsigned long res_failed; /* nomber of failed resolutions */ - - unsigned long lookups; /* number of lookups */ - unsigned long hits; /* number of hits (among lookups) */ - - unsigned long rcv_probes_mcast; /* number of received mcast ipv6 */ - unsigned long rcv_probes_ucast; /* number of received ucast ipv6 */ - - unsigned long periodic_gc_runs; /* number of periodic GC runs */ - unsigned long forced_gc_runs; /* number of forced GC runs */ + unsigned long allocs; + unsigned long res_failed; + unsigned long rcv_probes_mcast; + unsigned long rcv_probes_ucast; }; -#define NEIGH_CACHE_STAT_INC(tbl, field) \ - ((tbl)->stats[smp_processor_id()].field++) - struct neighbour { struct neighbour *next; @@ -147,6 +128,9 @@ u8 key[0]; }; +#define NEIGH_HASHMASK 0x1F +#define PNEIGH_HASHMASK 0xF + /* * neighbour table manipulation */ @@ -174,21 +158,15 @@ struct timer_list gc_timer; struct timer_list proxy_timer; struct sk_buff_head proxy_queue; - atomic_t entries; + int entries; rwlock_t lock; unsigned long last_rand; struct neigh_parms *parms_list; kmem_cache_t *kmem_cachep; struct tasklet_struct gc_task; - struct neigh_statistics stats[NR_CPUS]; - struct neighbour **hash_buckets; - unsigned int hash_mask; - __u32 hash_rnd; - unsigned int hash_chain_gc; - struct pneigh_entry **phash_buckets; -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *pde; -#endif + struct neigh_statistics stats; + struct neighbour *hash_buckets[NEIGH_HASHMASK+1]; + struct pneigh_entry *phash_buckets[PNEIGH_HASHMASK+1]; }; extern void neigh_table_init(struct neigh_table *tbl); @@ -196,8 +174,6 @@ extern struct neighbour * neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev); -extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl, - const void *pkey); extern struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey, struct net_device *dev); @@ -229,24 +205,6 @@ extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); extern void neigh_app_ns(struct neighbour *n); -extern void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie); -extern void __neigh_for_each_release(struct neigh_table *tbl, int (*cb)(struct neighbour *)); -extern void pneigh_for_each(struct neigh_table *tbl, void (*cb)(struct pneigh_entry *)); - -struct neigh_seq_state { - struct neigh_table *tbl; - void *(*neigh_sub_iter)(struct neigh_seq_state *state, - struct neighbour *n, loff_t *pos); - unsigned int bucket; - unsigned int flags; -#define NEIGH_SEQ_NEIGH_ONLY 0x00000001 -#define NEIGH_SEQ_IS_PNEIGH 0x00000002 -#define NEIGH_SEQ_SKIP_NOARP 0x00000004 -}; -extern void *neigh_seq_start(struct seq_file *, loff_t *, struct neigh_table *, unsigned int); -extern void *neigh_seq_next(struct seq_file *, void *, loff_t *); -extern void neigh_seq_stop(struct seq_file *, void *); - extern int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, int p_id, int pdev_id, char *p_name); extern void neigh_sysctl_unregister(struct neigh_parms *p); diff -Nur linux-2.4.28/net/core/neighbour.c linux-2.4.28.new/net/core/neighbour.c --- linux-2.4.28/net/core/neighbour.c 2004-11-17 12:15:32.000000000 -0500 +++ linux-2.4.28.new/net/core/neighbour.c 2004-12-09 09:54:27.000000000 -0500 @@ -2,7 +2,7 @@ * Generic address resolution entity * * Authors: - * Pedro Roque + * Pedro Roque * Alexey Kuznetsov * * This program is free software; you can redistribute it and/or @@ -12,18 +12,14 @@ * * Fixes: * Vitaly E. Lavrov releasing NULL neighbor in neigh_add. - * Harald Welte Add neighbour cache statistics like rtstat - * Harald Welte port neighbour cache rework from 2.6.9-rcX */ #include #include #include -#include #include #include #include -#include #ifdef CONFIG_SYSCTL #include #endif @@ -31,8 +27,6 @@ #include #include #include -#include -#include #define NEIGH_DEBUG 1 @@ -51,8 +45,6 @@ #define NEIGH_PRINTK2 NEIGH_PRINTK #endif -#define PNEIGH_HASHMASK 0xF - static void neigh_timer_handler(unsigned long arg); #ifdef CONFIG_ARPD static void neigh_app_notify(struct neighbour *n); @@ -62,7 +54,6 @@ static int neigh_glbl_allocs; static struct neigh_table *neigh_tables; -static struct file_operations neigh_stat_seq_fops; /* Neighbour hash table buckets are protected with rwlock tbl->lock. @@ -120,21 +111,27 @@ int shrunk = 0; int i; - NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs); - - write_lock_bh(&tbl->lock); - for (i = 0; i <= tbl->hash_mask; i++) { + for (i=0; i<=NEIGH_HASHMASK; i++) { struct neighbour *n, **np; np = &tbl->hash_buckets[i]; + write_lock_bh(&tbl->lock); while ((n = *np) != NULL) { /* Neighbour record may be discarded if: - * - nobody refers to it. - * - it is not permanent + - nobody refers to it. + - it is not permanent + - (NEW and probably wrong) + INCOMPLETE entries are kept at least for + n->parms->retrans_time, otherwise we could + flood network with resolution requests. + It is not clear, what is better table overflow + or flooding. */ write_lock(&n->lock); if (atomic_read(&n->refcnt) == 1 && - !(n->nud_state&NUD_PERMANENT)) { + !(n->nud_state&NUD_PERMANENT) && + (n->nud_state != NUD_INCOMPLETE || + jiffies - n->used > n->parms->retrans_time)) { *np = n->next; n->dead = 1; shrunk = 1; @@ -145,12 +142,10 @@ write_unlock(&n->lock); np = &n->next; } + write_unlock_bh(&tbl->lock); } tbl->last_flush = jiffies; - - write_unlock_bh(&tbl->lock); - return shrunk; } @@ -181,7 +176,7 @@ write_lock_bh(&tbl->lock); - for (i=0; i <= tbl->hash_mask; i++) { + for (i=0; i <= NEIGH_HASHMASK; i++) { struct neighbour *n, **np; np = &tbl->hash_buckets[i]; @@ -208,7 +203,7 @@ write_lock_bh(&tbl->lock); - for (i = 0; i <= tbl->hash_mask; i++) { + for (i=0; i<=NEIGH_HASHMASK; i++) { struct neighbour *n, **np; np = &tbl->hash_buckets[i]; @@ -259,11 +254,11 @@ struct neighbour *n; unsigned long now = jiffies; - if (atomic_read(&tbl->entries) > tbl->gc_thresh3 || - (atomic_read(&tbl->entries) > tbl->gc_thresh2 && + if (tbl->entries > tbl->gc_thresh3 || + (tbl->entries > tbl->gc_thresh2 && now - tbl->last_flush > 5*HZ)) { if (neigh_forced_gc(tbl) == 0 && - atomic_read(&tbl->entries) > tbl->gc_thresh3) + tbl->entries > tbl->gc_thresh3) return NULL; } @@ -282,113 +277,29 @@ init_timer(&n->timer); n->timer.function = neigh_timer_handler; n->timer.data = (unsigned long)n; - NEIGH_CACHE_STAT_INC(tbl, allocs); + tbl->stats.allocs++; neigh_glbl_allocs++; - atomic_inc(&tbl->entries); + tbl->entries++; n->tbl = tbl; atomic_set(&n->refcnt, 1); n->dead = 1; return n; } -static struct neighbour **neigh_hash_alloc(unsigned int entries) -{ - unsigned long size = entries * sizeof(struct neighbour *); - struct neighbour **ret; - - if (size <= PAGE_SIZE) { - ret = kmalloc(size, GFP_ATOMIC); - } else { - ret = (struct neighbour **) - __get_free_pages(GFP_ATOMIC, get_order(size)); - } - if (ret) - memset(ret, 0, size); - - return ret; -} - -static void neigh_hash_free(struct neighbour **hash, unsigned int entries) -{ - unsigned long size = entries * sizeof(struct neighbour *); - - if (size <= PAGE_SIZE) - kfree(hash); - else - free_pages((unsigned long)hash, get_order(size)); -} - -static void neigh_hash_grow(struct neigh_table *tbl, unsigned long new_entries) -{ - struct neighbour **new_hash, **old_hash; - unsigned int i, new_hash_mask, old_entries; - - NEIGH_CACHE_STAT_INC(tbl, hash_grows); - - BUG_ON(new_entries & (new_entries - 1)); - new_hash = neigh_hash_alloc(new_entries); - if (!new_hash) - return; - - old_entries = tbl->hash_mask + 1; - new_hash_mask = new_entries - 1; - old_hash = tbl->hash_buckets; - - get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd)); - for (i = 0; i < old_entries; i++) { - struct neighbour *n, *next; - - for (n = old_hash[i]; n; n = next) { - unsigned int hash_val = tbl->hash(n->primary_key, n->dev); - - hash_val &= new_hash_mask; - next = n->next; - - n->next = new_hash[hash_val]; - new_hash[hash_val] = n; - } - } - tbl->hash_buckets = new_hash; - tbl->hash_mask = new_hash_mask; - - neigh_hash_free(old_hash, old_entries); -} - struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev) { struct neighbour *n; + u32 hash_val; int key_len = tbl->key_len; - u32 hash_val = tbl->hash(pkey, dev) & tbl->hash_mask; - NEIGH_CACHE_STAT_INC(tbl, lookups); + hash_val = tbl->hash(pkey, dev); read_lock_bh(&tbl->lock); for (n = tbl->hash_buckets[hash_val]; n; n = n->next) { if (dev == n->dev && memcmp(n->primary_key, pkey, key_len) == 0) { neigh_hold(n); - NEIGH_CACHE_STAT_INC(tbl, hits); - break; - } - } - read_unlock_bh(&tbl->lock); - return n; -} - -struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, const void *pkey) -{ - struct neighbour *n; - int key_len = tbl->key_len; - u32 hash_val = tbl->hash(pkey, NULL) & tbl->hash_mask; - - NEIGH_CACHE_STAT_INC(tbl, lookups); - - read_lock_bh(&tbl->lock); - for (n = tbl->hash_buckets[hash_val]; n; n = n->next) { - if (!memcmp(n->primary_key, pkey, key_len)) { - neigh_hold(n); - NEIGH_CACHE_STAT_INC(tbl, hits); break; } } @@ -427,11 +338,9 @@ n->confirmed = jiffies - (n->parms->base_reachable_time<<1); - hash_val = tbl->hash(pkey, dev) & tbl->hash_mask; + hash_val = tbl->hash(pkey, dev); write_lock_bh(&tbl->lock); - if (atomic_read(&tbl->entries) > (tbl->hash_mask + 1)) - neigh_hash_grow(tbl, (tbl->hash_mask + 1) << 1); for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) { if (dev == n1->dev && memcmp(n1->primary_key, pkey, key_len) == 0) { @@ -509,9 +418,9 @@ hash_val ^= hash_val>>4; hash_val &= PNEIGH_HASHMASK; - 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) == 0 && n->dev == dev) { + write_lock_bh(&tbl->lock); *np = n->next; write_unlock_bh(&tbl->lock); if (tbl->pdestructor) @@ -520,7 +429,6 @@ return 0; } } - write_unlock_bh(&tbl->lock); return -ENOENT; } @@ -554,8 +462,6 @@ { struct hh_cache *hh; - NEIGH_CACHE_STAT_INC(neigh->tbl, destroys); - if (!neigh->dead) { printk("Destroying alive neighbour %p\n", neigh); dump_stack(); @@ -585,7 +491,7 @@ NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh); neigh_glbl_allocs--; - atomic_dec(&neigh->tbl->entries); + neigh->tbl->entries--; kmem_cache_free(neigh->tbl->kmem_cachep, neigh); } @@ -660,10 +566,9 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg) { struct neigh_table *tbl = (struct neigh_table*)arg; - struct neighbour *n, **np; - unsigned long expire, now = jiffies; + unsigned long now = jiffies; + int i; - NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs); write_lock(&tbl->lock); @@ -678,49 +583,46 @@ p->reachable_time = neigh_rand_reach_time(p->base_reachable_time); } - np = &tbl->hash_buckets[tbl->hash_chain_gc]; - tbl->hash_chain_gc = ((tbl->hash_chain_gc + 1) & tbl->hash_mask); + for (i=0; i <= NEIGH_HASHMASK; i++) { + struct neighbour *n, **np; - while ((n = *np) != NULL) { - unsigned int state; + np = &tbl->hash_buckets[i]; + while ((n = *np) != NULL) { + unsigned state; - write_lock(&n->lock); - - state = n->nud_state; - if (state & (NUD_PERMANENT | NUD_IN_TIMER)) { - write_unlock(&n->lock); - goto next_elt; - } + write_lock(&n->lock); + + state = n->nud_state; + if (state&(NUD_PERMANENT|NUD_IN_TIMER)) { + write_unlock(&n->lock); + goto next_elt; + } - if (time_before(n->used, n->confirmed)) - n->used = n->confirmed; + if ((long)(n->used - n->confirmed) < 0) + n->used = n->confirmed; - if (atomic_read(&n->refcnt) == 1 && - (state == NUD_FAILED || - time_after(now, n->used + n->parms->gc_staletime))) { - *np = n->next; - n->dead = 1; + if (atomic_read(&n->refcnt) == 1 && + (state == NUD_FAILED || now - n->used > n->parms->gc_staletime)) { + *np = n->next; + n->dead = 1; + write_unlock(&n->lock); + neigh_release(n); + continue; + } + + if (n->nud_state&NUD_REACHABLE && + now - n->confirmed > n->parms->reachable_time) { + n->nud_state = NUD_STALE; + neigh_suspect(n); + } write_unlock(&n->lock); - neigh_release(n); - continue; - } - write_unlock(&n->lock); next_elt: - np = &n->next; + np = &n->next; + } } - - /* Cycle through all hash buckets every base_reachable_time/2 ticks. - * ARP entry timeouts range from 1/2 base_reachable_time to 3/2 - * base_reachable_time. - */ - expire = tbl->parms.base_reachable_time >> 1; - expire /= (tbl->hash_mask + 1); - if (!expire) - expire = 1; - - mod_timer(&tbl->gc_timer, now + expire); + mod_timer(&tbl->gc_timer, now + tbl->gc_interval); write_unlock(&tbl->lock); } @@ -746,7 +648,6 @@ { unsigned long now = jiffies; struct neighbour *neigh = (struct neighbour*)arg; - struct sk_buff *skb; unsigned state; int notify = 0; @@ -779,7 +680,7 @@ neigh->nud_state = NUD_FAILED; notify = 1; - NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed); + neigh->tbl->stats.res_failed++; NEIGH_PRINTK2("neigh %p is failed.\n", neigh); /* It is very thin place. report_unreachable is very complicated @@ -798,20 +699,10 @@ neigh->timer.expires = now + neigh->parms->retrans_time; add_timer(&neigh->timer); - - /* keep skb alive even if arp_queue overflows */ - skb = skb_peek(&neigh->arp_queue); - if (skb) - skb_get(skb); - write_unlock(&neigh->lock); - neigh->ops->solicit(neigh, skb); + neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue)); atomic_inc(&neigh->probes); - - if (skb) - kfree_skb(skb); - return; out: @@ -1241,7 +1132,6 @@ void neigh_table_init(struct neigh_table *tbl) { unsigned long now = jiffies; - unsigned long phsize; tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time); @@ -1251,30 +1141,6 @@ 0, SLAB_HWCACHE_ALIGN, NULL, NULL); - if (!tbl->kmem_cachep) - panic("cannot create neighbour cache"); - -#ifdef CONFIG_PROC_FS - tbl->pde = create_proc_entry(tbl->id, 0, proc_net_stat); - if (!tbl->pde) - panic("cannot create neighbour proc dir entry"); - tbl->pde->proc_fops = &neigh_stat_seq_fops; - tbl->pde->data = tbl; -#endif - - tbl->hash_mask = 1; - tbl->hash_buckets = neigh_hash_alloc(tbl->hash_mask + 1); - - phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *); - tbl->phash_buckets = kmalloc(phsize, GFP_KERNEL); - - if (!tbl->hash_buckets || !tbl->phash_buckets) - panic("cannot allocate neighbour cache hashes"); - - memset(tbl->phash_buckets, 0, phsize); - - get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd)); - #ifdef CONFIG_SMP tasklet_init(&tbl->gc_task, SMP_TIMER_NAME(neigh_periodic_timer), (unsigned long)tbl); #endif @@ -1282,7 +1148,7 @@ tbl->lock = RW_LOCK_UNLOCKED; tbl->gc_timer.data = (unsigned long)tbl; tbl->gc_timer.function = neigh_periodic_timer; - tbl->gc_timer.expires = now + 1; + tbl->gc_timer.expires = now + tbl->gc_interval + tbl->parms.reachable_time; add_timer(&tbl->gc_timer); init_timer(&tbl->proxy_timer); @@ -1308,7 +1174,7 @@ del_timer_sync(&tbl->proxy_timer); pneigh_queue_purge(&tbl->proxy_queue); neigh_ifdown(tbl, NULL); - if (atomic_read(&tbl->entries)) + if (tbl->entries) printk(KERN_CRIT "neighbour leakage\n"); write_lock(&neigh_tbl_lock); for (tp = &neigh_tables; *tp; tp = &(*tp)->next) { @@ -1318,13 +1184,6 @@ } } write_unlock(&neigh_tbl_lock); - - neigh_hash_free(tbl->hash_buckets, tbl->hash_mask + 1); - tbl->hash_buckets = NULL; - - kfree(tbl->phash_buckets); - tbl->phash_buckets = NULL; - #ifdef CONFIG_SYSCTL neigh_sysctl_unregister(&tbl->parms); #endif @@ -1505,7 +1364,7 @@ s_h = cb->args[1]; s_idx = idx = cb->args[2]; - for (h=0; h <= tbl->hash_mask; h++) { + for (h=0; h <= NEIGH_HASHMASK; h++) { if (h < s_h) continue; if (h > s_h) s_idx = 0; @@ -1556,361 +1415,6 @@ return skb->len; } -void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) -{ - int chain; - - read_lock_bh(&tbl->lock); - for (chain = 0; chain <= tbl->hash_mask; chain++) { - struct neighbour *n; - - for (n = tbl->hash_buckets[chain]; n; n = n->next) - cb(n, cookie); - } - read_unlock_bh(&tbl->lock); -} -EXPORT_SYMBOL(neigh_for_each); - -/* The tbl->lock must be held as a writer and BH disabled. */ -void __neigh_for_each_release(struct neigh_table *tbl, - int (*cb)(struct neighbour *)) -{ - int chain; - - for (chain = 0; chain <= tbl->hash_mask; chain++) { - struct neighbour *n, **np; - - np = &tbl->hash_buckets[chain]; - while ((n = *np) != NULL) { - int release; - - write_lock(&n->lock); - release = cb(n); - if (release) { - *np = n->next; - n->dead = 1; - } else - np = &n->next; - write_unlock(&n->lock); - if (release) - neigh_release(n); - } - } -} -EXPORT_SYMBOL(__neigh_for_each_release); - -#ifdef CONFIG_PROC_FS - -static struct neighbour *neigh_get_first(struct seq_file *seq) -{ - struct neigh_seq_state *state = seq->private; - struct neigh_table *tbl = state->tbl; - struct neighbour *n = NULL; - int bucket = state->bucket; - - state->flags &= ~NEIGH_SEQ_IS_PNEIGH; - for (bucket = 0; bucket <= tbl->hash_mask; bucket++) { - n = tbl->hash_buckets[bucket]; - - while (n) { - if (state->neigh_sub_iter) { - loff_t fakep = 0; - void *v; - - v = state->neigh_sub_iter(state, n, &fakep); - if (!v) - goto next; - } - if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) - break; - if (n->nud_state & ~NUD_NOARP) - break; - next: - n = n->next; - } - - if (n) - break; - } - state->bucket = bucket; - - return n; -} - -static struct neighbour *neigh_get_next(struct seq_file *seq, - struct neighbour *n, - loff_t *pos) -{ - struct neigh_seq_state *state = seq->private; - struct neigh_table *tbl = state->tbl; - - if (state->neigh_sub_iter) { - void *v = state->neigh_sub_iter(state, n, pos); - if (v) - return n; - } - n = n->next; - - while (1) { - while (n) { - if (state->neigh_sub_iter) { - void *v = state->neigh_sub_iter(state, n, pos); - if (v) - return n; - goto next; - } - if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) - break; - - if (n->nud_state & ~NUD_NOARP) - break; - next: - n = n->next; - } - - if (n) - break; - - if (++state->bucket > tbl->hash_mask) - break; - - n = tbl->hash_buckets[state->bucket]; - } - - if (n && pos) - --(*pos); - return n; -} - -static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos) -{ - struct neighbour *n = neigh_get_first(seq); - - if (n) { - while (*pos) { - n = neigh_get_next(seq, n, pos); - if (!n) - break; - } - } - return *pos ? NULL : n; -} - -static struct pneigh_entry *pneigh_get_first(struct seq_file *seq) -{ - struct neigh_seq_state *state = seq->private; - struct neigh_table *tbl = state->tbl; - struct pneigh_entry *pn = NULL; - int bucket = state->bucket; - - state->flags |= NEIGH_SEQ_IS_PNEIGH; - for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) { - pn = tbl->phash_buckets[bucket]; - if (pn) - break; - } - state->bucket = bucket; - - return pn; -} - -static struct pneigh_entry *pneigh_get_next(struct seq_file *seq, - struct pneigh_entry *pn, - loff_t *pos) -{ - struct neigh_seq_state *state = seq->private; - struct neigh_table *tbl = state->tbl; - - pn = pn->next; - while (!pn) { - if (++state->bucket > PNEIGH_HASHMASK) - break; - pn = tbl->phash_buckets[state->bucket]; - if (pn) - break; - } - - if (pn && pos) - --(*pos); - - return pn; -} - -static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t *pos) -{ - struct pneigh_entry *pn = pneigh_get_first(seq); - - if (pn) { - while (*pos) { - pn = pneigh_get_next(seq, pn, pos); - if (!pn) - break; - } - } - return *pos ? NULL : pn; -} - -static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos) -{ - struct neigh_seq_state *state = seq->private; - void *rc; - - rc = neigh_get_idx(seq, pos); - if (!rc && !(state->flags & NEIGH_SEQ_NEIGH_ONLY)) - rc = pneigh_get_idx(seq, pos); - - return rc; -} - -void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags) -{ - struct neigh_seq_state *state = seq->private; - loff_t pos_minus_one; - - state->tbl = tbl; - state->bucket = 0; - state->flags = (neigh_seq_flags & ~NEIGH_SEQ_IS_PNEIGH); - - read_lock_bh(&tbl->lock); - - pos_minus_one = *pos - 1; - return *pos ? neigh_get_idx_any(seq, &pos_minus_one) : SEQ_START_TOKEN; -} - -void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct neigh_seq_state *state; - void *rc; - - if (v == SEQ_START_TOKEN) { - rc = neigh_get_idx(seq, pos); - goto out; - } - - state = seq->private; - if (!(state->flags & NEIGH_SEQ_IS_PNEIGH)) { - rc = neigh_get_next(seq, v, NULL); - if (rc) - goto out; - if (!(state->flags & NEIGH_SEQ_NEIGH_ONLY)) - rc = pneigh_get_first(seq); - } else { - BUG_ON(state->flags & NEIGH_SEQ_NEIGH_ONLY); - rc = pneigh_get_next(seq, v, NULL); - } -out: - ++(*pos); - return rc; -} - -void neigh_seq_stop(struct seq_file *seq, void *v) -{ - struct neigh_seq_state *state = seq->private; - struct neigh_table *tbl = state->tbl; - - read_unlock_bh(&tbl->lock); -} - -/* statistics via seq_file */ - -static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct proc_dir_entry *pde = seq->private; - struct neigh_table *tbl = pde->data; - int lcpu; - - if (*pos == 0) - return SEQ_START_TOKEN; - - for (lcpu = *pos-1; lcpu < smp_num_cpus; ++lcpu) { - int i = cpu_logical_map(lcpu); - *pos = lcpu+1; - return &tbl->stats[i]; - } - return NULL; -} - -static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct proc_dir_entry *pde = seq->private; - struct neigh_table *tbl = pde->data; - int lcpu; - - for (lcpu = *pos; lcpu < smp_num_cpus; ++lcpu) { - int i = cpu_logical_map(lcpu); - *pos = lcpu+1; - return &tbl->stats[i]; - } - return NULL; -} - -static void neigh_stat_seq_stop(struct seq_file *seq, void *v) -{ - -} - -static int neigh_stat_seq_show(struct seq_file *seq, void *v) -{ - struct proc_dir_entry *pde = seq->private; - struct neigh_table *tbl = pde->data; - struct neigh_statistics *st = v; - - if (v == SEQ_START_TOKEN) { - seq_printf(seq, "entries allocs destroys hash_grows lookups hits res_failed rcv_probes_mcast rcv_probes_ucast periodic_gc_runs forced_gc_runs forced_gc_goal_miss\n"); - return 0; - } - - seq_printf(seq, "%08x %08lx %08lx %08lx %08lx %08lx %08lx " - "%08lx %08lx %08lx %08lx\n", - atomic_read(&tbl->entries), - - st->allocs, - st->destroys, - st->hash_grows, - - st->lookups, - st->hits, - - st->res_failed, - - st->rcv_probes_mcast, - st->rcv_probes_ucast, - - st->periodic_gc_runs, - st->forced_gc_runs - ); - - return 0; -} - -static struct seq_operations neigh_stat_seq_ops = { - .start = neigh_stat_seq_start, - .next = neigh_stat_seq_next, - .stop = neigh_stat_seq_stop, - .show = neigh_stat_seq_show, -}; - -static int neigh_stat_seq_open(struct inode *inode, struct file *file) -{ - int ret = seq_open(file, &neigh_stat_seq_ops); - - if (!ret) { - struct seq_file *sf = file->private_data; - sf->private = PDE(inode); - } - return ret; -}; - -static struct file_operations neigh_stat_seq_fops = { - .owner = THIS_MODULE, - .open = neigh_stat_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -#endif /* CONFIG_PROC_FS */ - #ifdef CONFIG_ARPD void neigh_app_ns(struct neighbour *n) { diff -Nur linux-2.4.28/net/ipv4/arp.c linux-2.4.28.new/net/ipv4/arp.c --- linux-2.4.28/net/ipv4/arp.c 2004-11-17 12:15:32.000000000 -0500 +++ linux-2.4.28.new/net/ipv4/arp.c 2004-12-09 09:54:27.000000000 -0500 @@ -70,13 +70,11 @@ * arp_xmit so intermediate drivers like * bonding can change the skb before * sending (e.g. insert 8021q tag). - * Harald Welte : convert to make use of jenkins hash */ #include #include #include -#include #include #include #include @@ -94,8 +92,6 @@ #include #include #include -#include -#include #ifdef CONFIG_SYSCTL #include #endif @@ -222,7 +218,15 @@ static u32 arp_hash(const void *pkey, const struct net_device *dev) { - return jhash_2words(*(u32 *)pkey, dev->ifindex, arp_tbl.hash_rnd); + u32 hash_val; + + hash_val = *(u32*)pkey; + hash_val ^= (hash_val>>16); + hash_val ^= hash_val>>8; + hash_val ^= hash_val>>3; + hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK; + + return hash_val; } static int arp_constructor(struct neighbour *neigh) @@ -1020,26 +1024,8 @@ if (!dev) return -EINVAL; } - switch (dev->type) { -#ifdef CONFIG_FDDI - case ARPHRD_FDDI: - /* - * According to RFC 1390, FDDI devices should accept ARP - * hardware types of 1 (Ethernet). However, to be more - * robust, we'll accept hardware types of either 1 (Ethernet) - * or 6 (IEEE 802.2). - */ - if (r->arp_ha.sa_family != ARPHRD_FDDI && - r->arp_ha.sa_family != ARPHRD_ETHER && - r->arp_ha.sa_family != ARPHRD_IEEE802) - return -EINVAL; - break; -#endif - default: - if (r->arp_ha.sa_family != dev->type) - return -EINVAL; - break; - } + if (r->arp_ha.sa_family != dev->type) + return -EINVAL; neigh = __neigh_lookup_errno(&arp_tbl, &ip, dev); err = PTR_ERR(neigh); @@ -1199,155 +1185,129 @@ return err; } -#ifdef CONFIG_PROC_FS -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - -/* ------------------------------------------------------------------------ */ /* - * ax25 -> ASCII conversion + * Write the contents of the ARP cache to a PROCfs file. */ -static char *ax2asc2(ax25_address *a, char *buf) -{ - char c, *s; - int n; - - for (n = 0, s = buf; n < 6; n++) { - c = (a->ax25_call[n] >> 1) & 0x7F; - - if (c != ' ') *s++ = c; - } - - *s++ = '-'; - - if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { - *s++ = '1'; - n -= 10; - } - - *s++ = n + '0'; - *s++ = '\0'; - - if (*buf == '\0' || *buf == '-') - return "*"; - - return buf; - -} -#endif /* CONFIG_AX25 */ - +#ifndef CONFIG_PROC_FS +static int arp_get_info(char *buffer, char **start, off_t offset, int length) { return 0; } +#else +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) +static char *ax2asc2(ax25_address *a, char *buf); +#endif #define HBUFFERLEN 30 -static void arp_format_neigh_entry(struct seq_file *seq, - struct neighbour *n) +static int arp_get_info(char *buffer, char **start, off_t offset, int length) { + int len=0; + off_t pos=0; + int size; char hbuffer[HBUFFERLEN]; + int i,j,k; const char hexbuf[] = "0123456789ABCDEF"; - int k, j; - char tbuf[16]; - struct net_device *dev = n->dev; - int hatype = dev->type; - read_lock(&n->lock); + size = sprintf(buffer,"IP address HW type Flags HW address Mask Device\n"); - /* Convert hardware address to XX:XX:XX:XX ... form. */ -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - if (hatype == ARPHRD_AX25 || hatype == ARPHRD_NETROM) - ax2asc2((ax25_address *)n->ha, hbuffer); - else { -#endif - for (k=0,j=0;kaddr_len;j++) { - hbuffer[k++]=hexbuf[(n->ha[j]>>4)&15 ]; - hbuffer[k++]=hexbuf[n->ha[j]&15 ]; - hbuffer[k++]=':'; - } - hbuffer[--k]=0; -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - } -#endif - sprintf(tbuf, "%u.%u.%u.%u", NIPQUAD(*(u32*)n->primary_key)); - seq_printf(seq, "%-16s 0x%-10x0x%-10x%s * %s\n", - tbuf, hatype, arp_state_to_flags(n), hbuffer, dev->name); - read_unlock(&n->lock); -} + pos+=size; + len+=size; -static void arp_format_pneigh_entry(struct seq_file *seq, - struct pneigh_entry *n) -{ - struct net_device *dev = n->dev; - int hatype = dev ? dev->type : 0; - char tbuf[16]; + for(i=0; i<=NEIGH_HASHMASK; i++) { + struct neighbour *n; + read_lock_bh(&arp_tbl.lock); + for (n=arp_tbl.hash_buckets[i]; n; n=n->next) { + struct net_device *dev = n->dev; + int hatype = dev->type; + + /* Do not confuse users "arp -a" with magic entries */ + if (!(n->nud_state&~NUD_NOARP)) + continue; - sprintf(tbuf, "%u.%u.%u.%u", NIPQUAD(*(u32*)n->key)); - seq_printf(seq, "%-16s 0x%-10x0x%-10x%s * %s\n", - tbuf, hatype, ATF_PUBL | ATF_PERM, "00:00:00:00:00:00", - dev ? dev->name : "*"); -} + read_lock(&n->lock); -static int arp_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "IP address HW type Flags " - "HW address Mask Device\n"); - } else { - struct neigh_seq_state *state = seq->private; - - if (state->flags & NEIGH_SEQ_IS_PNEIGH) - arp_format_pneigh_entry(seq, v); - else - arp_format_neigh_entry(seq, v); - } - - return 0; -} +/* + * Convert hardware address to XX:XX:XX:XX ... form. + */ +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + if (hatype == ARPHRD_AX25 || hatype == ARPHRD_NETROM) + ax2asc2((ax25_address *)n->ha, hbuffer); + else { +#endif + for (k=0,j=0;kaddr_len;j++) { + hbuffer[k++]=hexbuf[(n->ha[j]>>4)&15 ]; + hbuffer[k++]=hexbuf[n->ha[j]&15 ]; + hbuffer[k++]=':'; + } + hbuffer[--k]=0; -static void *arp_seq_start(struct seq_file *seq, loff_t *pos) -{ - /* Don't want to confuse "arp -a" w/ magic entries, - * so we tell the generic iterator to skip NUD_NOARP. - */ - return neigh_seq_start(seq, pos, &arp_tbl, NEIGH_SEQ_SKIP_NOARP); -} +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + } +#endif -/* ------------------------------------------------------------------------ */ + { + char tbuf[16]; + sprintf(tbuf, "%u.%u.%u.%u", NIPQUAD(*(u32*)n->primary_key)); + size = sprintf(buffer+len, "%-16s 0x%-10x0x%-10x%s" + " * %s\n", + tbuf, + hatype, + arp_state_to_flags(n), + hbuffer, + dev->name); + } -static struct seq_operations arp_seq_ops = { - .start = arp_seq_start, - .next = neigh_seq_next, - .stop = neigh_seq_stop, - .show = arp_seq_show, -}; + read_unlock(&n->lock); -static int arp_seq_open(struct inode *inode, struct file *file) -{ - struct seq_file *seq; - int rc = -ENOMEM; - struct neigh_seq_state *s = kmalloc(sizeof(*s), GFP_KERNEL); + len += size; + pos += size; + + if (pos <= offset) + len=0; + if (pos >= offset+length) { + read_unlock_bh(&arp_tbl.lock); + goto done; + } + } + read_unlock_bh(&arp_tbl.lock); + } - if (!s) - goto out; + for (i=0; i<=PNEIGH_HASHMASK; i++) { + struct pneigh_entry *n; + for (n=arp_tbl.phash_buckets[i]; n; n=n->next) { + struct net_device *dev = n->dev; + int hatype = dev ? dev->type : 0; + + { + char tbuf[16]; + sprintf(tbuf, "%u.%u.%u.%u", NIPQUAD(*(u32*)n->key)); + size = sprintf(buffer+len, "%-16s 0x%-10x0x%-10x%s" + " * %s\n", + tbuf, + hatype, + ATF_PUBL|ATF_PERM, + "00:00:00:00:00:00", + dev ? dev->name : "*"); + } - memset(s, 0, sizeof(*s)); - rc = seq_open(file, &arp_seq_ops); - if (rc) - goto out_kfree; + len += size; + pos += size; + + if (pos <= offset) + len=0; + if (pos >= offset+length) + goto done; + } + } - seq = file->private_data; - seq->private = s; -out: - return rc; -out_kfree: - kfree(s); - goto out; +done: + + *start = buffer+len-(pos-offset); /* Start of wanted data */ + len = pos-offset; /* Start slop */ + if (len>length) + len = length; /* Ending slop */ + if (len<0) + len = 0; + return len; } - -static struct file_operations arp_seq_fops = { - .owner = THIS_MODULE, - .open = arp_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; -#endif /* CONFIG_PROC_FS */ +#endif static int arp_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { @@ -1395,10 +1355,8 @@ dev_add_pack(&arp_packet_type); -#ifdef CONFIG_PROC_FS - if (!proc_net_fops_create("arp", S_IRUGO, &arp_seq_fops)) - panic("unable to create arp proc entry"); -#endif + proc_net_create ("arp", 0, arp_get_info); + #ifdef CONFIG_SYSCTL neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4"); #endif @@ -1406,3 +1364,39 @@ } +#ifdef CONFIG_PROC_FS +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + +/* + * ax25 -> ASCII conversion + */ +char *ax2asc2(ax25_address *a, char *buf) +{ + char c, *s; + int n; + + for (n = 0, s = buf; n < 6; n++) { + c = (a->ax25_call[n] >> 1) & 0x7F; + + if (c != ' ') *s++ = c; + } + + *s++ = '-'; + + if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { + *s++ = '1'; + n -= 10; + } + + *s++ = n + '0'; + *s++ = '\0'; + + if (*buf == '\0' || *buf == '-') + return "*"; + + return buf; + +} + +#endif +#endif diff -Nur linux-2.4.28/net/netsyms.c linux-2.4.28.new/net/netsyms.c --- linux-2.4.28/net/netsyms.c 2004-11-17 12:15:32.000000000 -0500 +++ linux-2.4.28.new/net/netsyms.c 2004-12-09 09:54:27.000000000 -0500 @@ -179,13 +179,9 @@ EXPORT_SYMBOL(neigh_update); EXPORT_SYMBOL(neigh_create); EXPORT_SYMBOL(neigh_lookup); -EXPORT_SYMBOL(neigh_lookup_nodev); EXPORT_SYMBOL(__neigh_event_send); EXPORT_SYMBOL(neigh_event_ns); EXPORT_SYMBOL(neigh_ifdown); -EXPORT_SYMBOL(neigh_seq_start); -EXPORT_SYMBOL(neigh_seq_next); -EXPORT_SYMBOL(neigh_seq_stop); #ifdef CONFIG_ARPD EXPORT_SYMBOL(neigh_app_ns); #endif