vserver 1.9.3
[linux-2.6.git] / net / decnet / dn_neigh.c
index ab64b85..dc48de8 100644 (file)
@@ -35,6 +35,8 @@
 #include <linux/netfilter_decnet.h>
 #include <linux/spinlock.h>
 #include <linux/seq_file.h>
+#include <linux/rcupdate.h>
+#include <linux/jhash.h>
 #include <asm/atomic.h>
 #include <net/neighbour.h>
 #include <net/dst.h>
@@ -121,26 +123,32 @@ struct neigh_table dn_neigh_table = {
 
 static u32 dn_neigh_hash(const void *pkey, const struct net_device *dev)
 {
-       u32 hash_val;
-
-       hash_val = *(dn_address *)pkey;
-       hash_val ^= (hash_val >> 10);
-       hash_val ^= (hash_val >> 3);
-
-       return hash_val & NEIGH_HASHMASK;
+       return jhash_2words(*(dn_address *)pkey, 0, dn_neigh_table.hash_rnd);
 }
 
 static int dn_neigh_construct(struct neighbour *neigh)
 {
        struct net_device *dev = neigh->dev;
        struct dn_neigh *dn = (struct dn_neigh *)neigh;
-       struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+       struct dn_dev *dn_db;
+       struct neigh_parms *parms;
 
-       if (dn_db == NULL)
+       rcu_read_lock();
+       dn_db = rcu_dereference(dev->dn_ptr);
+       if (dn_db == NULL) {
+               rcu_read_unlock();
                return -EINVAL;
+       }
 
-       if (dn_db->neigh_parms)
-               neigh->parms = dn_db->neigh_parms;
+       parms = dn_db->neigh_parms;
+       if (!parms) {
+               rcu_read_unlock();
+               return -EINVAL;
+       }
+
+       __neigh_parms_put(neigh->parms);
+       neigh->parms = neigh_parms_clone(parms);
+       rcu_read_unlock();
 
        if (dn_db->use_long)
                neigh->ops = &dn_long_ops;
@@ -346,27 +354,6 @@ static int dn_phase3_output(struct sk_buff *skb)
  * basically does a neigh_lookup(), but without comparing the device
  * field. This is required for the On-Ethernet cache
  */
-struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, const void *ptr)
-{
-       struct neighbour *neigh;
-       u32 hash_val;
-
-       hash_val = tbl->hash(ptr, NULL);
-
-       read_lock_bh(&tbl->lock);
-       for(neigh = tbl->hash_buckets[hash_val]; neigh != NULL; neigh = neigh->next) {
-               if (memcmp(neigh->primary_key, ptr, tbl->key_len) == 0) {
-                       atomic_inc(&neigh->refcnt);
-                       read_unlock_bh(&tbl->lock);
-                       return neigh;
-               }
-       }
-       read_unlock_bh(&tbl->lock);
-
-       return NULL;
-}
-
-
 /*
  * Any traffic on a pointopoint link causes the timer to be reset
  * for the entry in the neighbour table.
@@ -412,7 +399,7 @@ int dn_neigh_router_hello(struct sk_buff *skb)
                        neigh->updated = jiffies;
 
                        if (neigh->dev->type == ARPHRD_ETHER)
-                               memcpy(neigh->ha, &skb->mac.ethernet->h_source, ETH_ALEN);
+                               memcpy(neigh->ha, &eth_hdr(skb)->h_source, ETH_ALEN);
 
                        dn->blksize  = dn_ntohs(msg->blksize);
                        dn->priority = msg->priority;
@@ -468,7 +455,7 @@ int dn_neigh_endnode_hello(struct sk_buff *skb)
                        neigh->updated = jiffies;
 
                        if (neigh->dev->type == ARPHRD_ETHER)
-                               memcpy(neigh->ha, &skb->mac.ethernet->h_source, ETH_ALEN);
+                               memcpy(neigh->ha, &eth_hdr(skb)->h_source, ETH_ALEN);
                        dn->flags   &= ~(DN_NDFLAG_R1 | DN_NDFLAG_R2);
                        dn->blksize  = dn_ntohs(msg->blksize);
                        dn->priority = 0;
@@ -501,141 +488,66 @@ static char *dn_find_slot(char *base, int max, int priority)
        return (*min < priority) ? (min - 6) : NULL;
 }
 
-int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
-{
-       int t = 0;
-       int i;
-       struct neighbour *neigh;
-       struct dn_neigh *dn;
-       struct neigh_table *tbl = &dn_neigh_table;
-       unsigned char *rs = ptr;
-       struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
-
-       read_lock_bh(&tbl->lock);
-
-       for(i = 0; i < NEIGH_HASHMASK; i++) {
-               for(neigh = tbl->hash_buckets[i]; neigh != NULL; neigh = neigh->next) {
-                       if (neigh->dev != dev)
-                               continue;
-                       dn = (struct dn_neigh *)neigh;
-                       if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
-                               continue;
-                       if (dn_db->parms.forwarding == 1 && (dn->flags & DN_NDFLAG_R2))
-                               continue;
-                       if (t == n)
-                               rs = dn_find_slot(ptr, n, dn->priority);
-                       else
-                               t++;
-                       if (rs == NULL)
-                               continue;
-                       dn_dn2eth(rs, dn->addr);
-                       rs += 6;
-                       *rs = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
-                       *rs |= dn->priority;
-                       rs++;
-               }
-       }
-
-       read_unlock_bh(&tbl->lock);
-
-       return t;
-}
-
-
-#ifdef CONFIG_PROC_FS
-
-struct dn_neigh_iter_state {
-       int bucket;
+struct elist_cb_state {
+       struct net_device *dev;
+       unsigned char *ptr;
+       unsigned char *rs;
+       int t, n;
 };
 
-static struct neighbour *neigh_get_first(struct seq_file *seq)
+static void neigh_elist_cb(struct neighbour *neigh, void *_info)
 {
-       struct dn_neigh_iter_state *state = seq->private;
-       struct neighbour *n = NULL;
-
-       for(state->bucket = 0;
-           state->bucket <= NEIGH_HASHMASK;
-           ++state->bucket) {
-               n = dn_neigh_table.hash_buckets[state->bucket];
-               if (n)
-                       break;
-       }
-
-       return n;
-}
-
-static struct neighbour *neigh_get_next(struct seq_file *seq,
-                                       struct neighbour *n)
-{
-       struct dn_neigh_iter_state *state = seq->private;
+       struct elist_cb_state *s = _info;
+       struct dn_dev *dn_db;
+       struct dn_neigh *dn;
 
-       n = n->next;
-try_again:
-       if (n)
-               goto out;
-       if (++state->bucket > NEIGH_HASHMASK)
-               goto out;
-       n = dn_neigh_table.hash_buckets[state->bucket];
-       goto try_again;
-out:
-       return n;
-}
+       if (neigh->dev != s->dev)
+               return;
 
-static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
-{
-       struct neighbour *n = neigh_get_first(seq);
+       dn = (struct dn_neigh *) neigh;
+       if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
+               return;
 
-       if (n)
-               while(*pos && (n = neigh_get_next(seq, n)))
-                       --*pos;
-       return *pos ? NULL : n;
-}
+       dn_db = (struct dn_dev *) s->dev->dn_ptr;
+       if (dn_db->parms.forwarding == 1 && (dn->flags & DN_NDFLAG_R2))
+               return;
 
-static void *dn_neigh_get_idx(struct seq_file *seq, loff_t pos)
-{
-       void *rc;
-       read_lock_bh(&dn_neigh_table.lock);
-       rc = neigh_get_idx(seq, &pos);
-       if (!rc) {
-               read_unlock_bh(&dn_neigh_table.lock);
-       }
-       return rc;
-}
-
-static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       return *pos ? dn_neigh_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
+       if (s->t == s->n)
+               s->rs = dn_find_slot(s->ptr, s->n, dn->priority);
+       else
+               s->t++;
+       if (s->rs == NULL)
+               return;
+
+       dn_dn2eth(s->rs, dn->addr);
+       s->rs += 6;
+       *(s->rs) = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
+       *(s->rs) |= dn->priority;
+       s->rs++;
 }
 
-static void *dn_neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
 {
-       void *rc;
+       struct elist_cb_state state;
 
+       state.dev = dev;
+       state.t = 0;
+       state.n = n;
+       state.ptr = ptr;
+       state.rs = ptr;
 
-       if (v == SEQ_START_TOKEN) {
-               rc = dn_neigh_get_idx(seq, 0);
-               goto out;
-       }
+       neigh_for_each(&dn_neigh_table, neigh_elist_cb, &state);
 
-       rc = neigh_get_next(seq, v);
-       if (rc)
-               goto out;
-       read_unlock_bh(&dn_neigh_table.lock);
-out:
-       ++*pos;
-       return rc;
+       return state.t;
 }
 
-static void dn_neigh_seq_stop(struct seq_file *seq, void *v)
-{
-       if (v && v != SEQ_START_TOKEN)
-               read_unlock_bh(&dn_neigh_table.lock);
-}
+
+#ifdef CONFIG_PROC_FS
 
 static inline void dn_neigh_format_entry(struct seq_file *seq,
                                         struct neighbour *n)
 {
-       struct dn_neigh *dn = (struct dn_neigh *)n;
+       struct dn_neigh *dn = (struct dn_neigh *) n;
        char buf[DN_ASCBUF_LEN];
 
        read_lock(&n->lock);
@@ -662,10 +574,16 @@ static int dn_neigh_seq_show(struct seq_file *seq, void *v)
        return 0;
 }
 
+static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       return neigh_seq_start(seq, pos, &dn_neigh_table,
+                              NEIGH_SEQ_NEIGH_ONLY);
+}
+
 static struct seq_operations dn_neigh_seq_ops = {
        .start = dn_neigh_seq_start,
-       .next  = dn_neigh_seq_next,
-       .stop  = dn_neigh_seq_stop,
+       .next  = neigh_seq_next,
+       .stop  = neigh_seq_stop,
        .show  = dn_neigh_seq_show,
 };
 
@@ -673,11 +591,12 @@ static int dn_neigh_seq_open(struct inode *inode, struct file *file)
 {
        struct seq_file *seq;
        int rc = -ENOMEM;
-       struct dn_neigh_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       struct neigh_seq_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
 
        if (!s)
                goto out;
 
+       memset(s, 0, sizeof(*s));
        rc = seq_open(file, &dn_neigh_seq_ops);
        if (rc)
                goto out_kfree;