Skip to content

Commit

Permalink
ipv4: Make neigh lookups directly in output packet path.
Browse files Browse the repository at this point in the history
Do not use the dst cached neigh, we'll be getting rid of that.

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 5, 2012
1 parent 1160472 commit a263b30
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 25 deletions.
28 changes: 19 additions & 9 deletions include/net/arp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,34 @@ static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd
return val * hash_rnd;
}

static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key)
static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key)
{
struct neigh_hash_table *nht;
struct neigh_hash_table *nht = rcu_dereference_bh(arp_tbl.nht);
struct neighbour *n;
u32 hash_val;

rcu_read_lock_bh();
nht = rcu_dereference_bh(arp_tbl.nht);
if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
key = 0;

hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift);
for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
n != NULL;
n = rcu_dereference_bh(n->next)) {
if (n->dev == dev && *(u32 *)n->primary_key == key) {
if (!atomic_inc_not_zero(&n->refcnt))
n = NULL;
break;
}
if (n->dev == dev && *(u32 *)n->primary_key == key)
return n;
}

return NULL;
}

static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key)
{
struct neighbour *n;

rcu_read_lock_bh();
n = __ipv4_neigh_lookup_noref(dev, key);
if (n && !atomic_inc_not_zero(&n->refcnt))
n = NULL;
rcu_read_unlock_bh();

return n;
Expand Down
11 changes: 9 additions & 2 deletions include/net/neighbour.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,16 @@ extern struct neighbour * neigh_lookup(struct neigh_table *tbl,
extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl,
struct net *net,
const void *pkey);
extern struct neighbour * neigh_create(struct neigh_table *tbl,
extern struct neighbour * __neigh_create(struct neigh_table *tbl,
const void *pkey,
struct net_device *dev,
bool want_ref);
static inline struct neighbour *neigh_create(struct neigh_table *tbl,
const void *pkey,
struct net_device *dev);
struct net_device *dev)
{
return __neigh_create(tbl, pkey, dev, true);
}
extern void neigh_destroy(struct neighbour *neigh);
extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
extern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
Expand Down
12 changes: 7 additions & 5 deletions net/core/neighbour.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,8 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
}
EXPORT_SYMBOL(neigh_lookup_nodev);

struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
struct net_device *dev)
struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
struct net_device *dev, bool want_ref)
{
u32 hash_val;
int key_len = tbl->key_len;
Expand Down Expand Up @@ -535,14 +535,16 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
n1 = rcu_dereference_protected(n1->next,
lockdep_is_held(&tbl->lock))) {
if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
neigh_hold(n1);
if (want_ref)
neigh_hold(n1);
rc = n1;
goto out_tbl_unlock;
}
}

n->dead = 0;
neigh_hold(n);
if (want_ref)
neigh_hold(n);
rcu_assign_pointer(n->next,
rcu_dereference_protected(nht->hash_buckets[hash_val],
lockdep_is_held(&tbl->lock)));
Expand All @@ -558,7 +560,7 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
neigh_release(n);
goto out;
}
EXPORT_SYMBOL(neigh_create);
EXPORT_SYMBOL(__neigh_create);

static u32 pneigh_hash(const void *pkey, int key_len)
{
Expand Down
12 changes: 8 additions & 4 deletions net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ static inline int ip_finish_output2(struct sk_buff *skb)
struct net_device *dev = dst->dev;
unsigned int hh_len = LL_RESERVED_SPACE(dev);
struct neighbour *neigh;
u32 nexthop;

if (rt->rt_type == RTN_MULTICAST) {
IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);
Expand All @@ -191,15 +192,18 @@ static inline int ip_finish_output2(struct sk_buff *skb)
skb = skb2;
}

rcu_read_lock();
neigh = dst_get_neighbour_noref(dst);
rcu_read_lock_bh();
nexthop = rt->rt_gateway ? rt->rt_gateway : ip_hdr(skb)->daddr;
neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
if (unlikely(!neigh))
neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
if (neigh) {
int res = neigh_output(neigh, skb);

rcu_read_unlock();
rcu_read_unlock_bh();
return res;
}
rcu_read_unlock();
rcu_read_unlock_bh();

net_dbg_ratelimited("%s: No header cache and no neighbour!\n",
__func__);
Expand Down
6 changes: 1 addition & 5 deletions net/ipv4/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -1098,17 +1098,13 @@ static int slow_chain_length(const struct rtable *head)

static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr)
{
static const __be32 inaddr_any = 0;
struct net_device *dev = dst->dev;
const __be32 *pkey = daddr;
const struct rtable *rt;
struct neighbour *n;

rt = (const struct rtable *) dst;

if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
pkey = &inaddr_any;
else if (rt->rt_gateway)
if (rt->rt_gateway)
pkey = (const __be32 *) &rt->rt_gateway;

n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey);
Expand Down

0 comments on commit a263b30

Please sign in to comment.