net: kill unused macros
[pandora-kernel.git] / net / core / neighbour.c
index 3ffafaa..60a9029 100644 (file)
@@ -41,7 +41,6 @@
 
 #define NEIGH_PRINTK(x...) printk(x)
 #define NEIGH_NOPRINTK(x...) do { ; } while(0)
-#define NEIGH_PRINTK0 NEIGH_PRINTK
 #define NEIGH_PRINTK1 NEIGH_NOPRINTK
 #define NEIGH_PRINTK2 NEIGH_NOPRINTK
 
@@ -294,6 +293,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl)
 
        skb_queue_head_init(&n->arp_queue);
        rwlock_init(&n->lock);
+       seqlock_init(&n->ha_lock);
        n->updated        = n->used = now;
        n->nud_state      = NUD_NONE;
        n->output         = neigh_blackhole;
@@ -709,8 +709,7 @@ void neigh_destroy(struct neighbour *neigh)
                write_seqlock_bh(&hh->hh_lock);
                hh->hh_output = neigh_blackhole;
                write_sequnlock_bh(&hh->hh_lock);
-               if (atomic_dec_and_test(&hh->hh_refcnt))
-                       kfree(hh);
+               hh_cache_put(hh);
        }
 
        skb_queue_purge(&neigh->arp_queue);
@@ -1016,7 +1015,7 @@ out_unlock_bh:
 }
 EXPORT_SYMBOL(__neigh_event_send);
 
-static void neigh_update_hhs(struct neighbour *neigh)
+static void neigh_update_hhs(const struct neighbour *neigh)
 {
        struct hh_cache *hh;
        void (*update)(struct hh_cache*, const struct net_device*, const unsigned char *)
@@ -1152,7 +1151,9 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
        }
 
        if (lladdr != neigh->ha) {
+               write_seqlock(&neigh->ha_lock);
                memcpy(&neigh->ha, lladdr, dev->addr_len);
+               write_sequnlock(&neigh->ha_lock);
                neigh_update_hhs(neigh);
                if (!(new & NUD_CONNECTED))
                        neigh->confirmed = jiffies -
@@ -1210,39 +1211,68 @@ 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)
+{
+       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;
+               }
+       }
+       return false;
+}
+
+/* called with read_lock_bh(&n->lock); */
 static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
                          __be16 protocol)
 {
        struct hh_cache *hh;
        struct net_device *dev = dst->dev;
 
-       for (hh = n->hh; hh; hh = hh->hh_next)
-               if (hh->hh_type == protocol)
-                       break;
+       if (likely(neigh_hh_lookup(n, dst, protocol)))
+               return;
 
-       if (!hh && (hh = kzalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
-               seqlock_init(&hh->hh_lock);
-               hh->hh_type = protocol;
-               atomic_set(&hh->hh_refcnt, 0);
-               hh->hh_next = NULL;
+       /* slow path */
+       hh = kzalloc(sizeof(*hh), GFP_ATOMIC);
+       if (!hh)
+               return;
 
-               if (dev->header_ops->cache(n, hh)) {
-                       kfree(hh);
-                       hh = NULL;
-               } else {
-                       atomic_inc(&hh->hh_refcnt);
-                       hh->hh_next = n->hh;
-                       n->hh       = hh;
-                       if (n->nud_state & NUD_CONNECTED)
-                               hh->hh_output = n->ops->hh_output;
-                       else
-                               hh->hh_output = n->ops->output;
-               }
+       seqlock_init(&hh->hh_lock);
+       hh->hh_type = protocol;
+       atomic_set(&hh->hh_refcnt, 2);
+
+       if (dev->header_ops->cache(n, hh)) {
+               kfree(hh);
+               return;
        }
-       if (hh) {
-               atomic_inc(&hh->hh_refcnt);
-               dst->hh = hh;
+
+       write_lock_bh(&n->lock);
+
+       /* must check if another thread already did the insert */
+       if (neigh_hh_lookup(n, dst, protocol)) {
+               kfree(hh);
+               goto end;
        }
+
+       if (n->nud_state & NUD_CONNECTED)
+               hh->hh_output = n->ops->hh_output;
+       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;
+
+       if (unlikely(cmpxchg(&dst->hh, NULL, hh) != NULL))
+               hh_cache_put(hh);
+end:
+       write_unlock_bh(&n->lock);
 }
 
 /* This function can be used in contexts, where only old dev_queue_xmit
@@ -1281,21 +1311,19 @@ int neigh_resolve_output(struct sk_buff *skb)
        if (!neigh_event_send(neigh, skb)) {
                int err;
                struct net_device *dev = neigh->dev;
+               unsigned int seq;
+
                if (dev->header_ops->cache &&
                    !dst->hh &&
-                   !(dst->flags & DST_NOCACHE)) {
-                       write_lock_bh(&neigh->lock);
-                       if (!dst->hh)
-                               neigh_hh_init(neigh, dst, dst->ops->protocol);
-                       err = dev_hard_header(skb, dev, ntohs(skb->protocol),
-                                             neigh->ha, NULL, skb->len);
-                       write_unlock_bh(&neigh->lock);
-               } else {
-                       read_lock_bh(&neigh->lock);
+                   !(dst->flags & DST_NOCACHE))
+                       neigh_hh_init(neigh, dst, dst->ops->protocol);
+
+               do {
+                       seq = read_seqbegin(&neigh->ha_lock);
                        err = dev_hard_header(skb, dev, ntohs(skb->protocol),
                                              neigh->ha, NULL, skb->len);
-                       read_unlock_bh(&neigh->lock);
-               }
+               } while (read_seqretry(&neigh->ha_lock, seq));
+
                if (err >= 0)
                        rc = neigh->ops->queue_xmit(skb);
                else
@@ -1321,13 +1349,16 @@ int neigh_connected_output(struct sk_buff *skb)
        struct dst_entry *dst = skb_dst(skb);
        struct neighbour *neigh = dst->neighbour;
        struct net_device *dev = neigh->dev;
+       unsigned int seq;
 
        __skb_pull(skb, skb_network_offset(skb));
 
-       read_lock_bh(&neigh->lock);
-       err = dev_hard_header(skb, dev, ntohs(skb->protocol),
-                             neigh->ha, NULL, skb->len);
-       read_unlock_bh(&neigh->lock);
+       do {
+               seq = read_seqbegin(&neigh->ha_lock);
+               err = dev_hard_header(skb, dev, ntohs(skb->protocol),
+                                     neigh->ha, NULL, skb->len);
+       } while (read_seqretry(&neigh->ha_lock, seq));
+
        if (err >= 0)
                err = neigh->ops->queue_xmit(skb);
        else {
@@ -1556,8 +1587,7 @@ int neigh_table_clear(struct neigh_table *tbl)
        struct neigh_table **tp;
 
        /* It is not clean... Fix it to unload IPv6 module safely */
-       cancel_delayed_work(&tbl->gc_work);
-       flush_scheduled_work();
+       cancel_delayed_work_sync(&tbl->gc_work);
        del_timer_sync(&tbl->proxy_timer);
        pneigh_queue_purge(&tbl->proxy_queue);
        neigh_ifdown(tbl, NULL);
@@ -2125,10 +2155,14 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
 
        read_lock_bh(&neigh->lock);
        ndm->ndm_state   = neigh->nud_state;
-       if ((neigh->nud_state & NUD_VALID) &&
-           nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, neigh->ha) < 0) {
-               read_unlock_bh(&neigh->lock);
-               goto nla_put_failure;
+       if (neigh->nud_state & NUD_VALID) {
+               char haddr[MAX_ADDR_LEN];
+
+               neigh_ha_snapshot(haddr, neigh, neigh->dev);
+               if (nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, haddr) < 0) {
+                       read_unlock_bh(&neigh->lock);
+                       goto nla_put_failure;
+               }
        }
 
        ci.ndm_used      = jiffies_to_clock_t(now - neigh->used);