Commit a263b309 authored by David S. Miller's avatar David S. Miller

ipv4: Make neigh lookups directly in output packet path.

Do not use the dst cached neigh, we'll be getting rid of that.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 11604721
...@@ -15,24 +15,34 @@ static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd ...@@ -15,24 +15,34 @@ static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd
return val * 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; struct neighbour *n;
u32 hash_val; u32 hash_val;
rcu_read_lock_bh(); if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
nht = rcu_dereference_bh(arp_tbl.nht); key = 0;
hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift); hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift);
for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
n != NULL; n != NULL;
n = rcu_dereference_bh(n->next)) { n = rcu_dereference_bh(n->next)) {
if (n->dev == dev && *(u32 *)n->primary_key == key) { if (n->dev == dev && *(u32 *)n->primary_key == key)
if (!atomic_inc_not_zero(&n->refcnt)) return n;
n = NULL;
break;
}
} }
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(); rcu_read_unlock_bh();
return n; return n;
......
...@@ -202,9 +202,16 @@ extern struct neighbour * neigh_lookup(struct neigh_table *tbl, ...@@ -202,9 +202,16 @@ extern struct neighbour * neigh_lookup(struct neigh_table *tbl,
extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl, extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl,
struct net *net, struct net *net,
const void *pkey); 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, 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 void neigh_destroy(struct neighbour *neigh);
extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
extern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, extern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
......
...@@ -474,8 +474,8 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, ...@@ -474,8 +474,8 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
} }
EXPORT_SYMBOL(neigh_lookup_nodev); EXPORT_SYMBOL(neigh_lookup_nodev);
struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
struct net_device *dev) struct net_device *dev, bool want_ref)
{ {
u32 hash_val; u32 hash_val;
int key_len = tbl->key_len; int key_len = tbl->key_len;
...@@ -535,14 +535,16 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, ...@@ -535,14 +535,16 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
n1 = rcu_dereference_protected(n1->next, n1 = rcu_dereference_protected(n1->next,
lockdep_is_held(&tbl->lock))) { lockdep_is_held(&tbl->lock))) {
if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) { if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
neigh_hold(n1); if (want_ref)
neigh_hold(n1);
rc = n1; rc = n1;
goto out_tbl_unlock; goto out_tbl_unlock;
} }
} }
n->dead = 0; n->dead = 0;
neigh_hold(n); if (want_ref)
neigh_hold(n);
rcu_assign_pointer(n->next, rcu_assign_pointer(n->next,
rcu_dereference_protected(nht->hash_buckets[hash_val], rcu_dereference_protected(nht->hash_buckets[hash_val],
lockdep_is_held(&tbl->lock))); lockdep_is_held(&tbl->lock)));
...@@ -558,7 +560,7 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, ...@@ -558,7 +560,7 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
neigh_release(n); neigh_release(n);
goto out; goto out;
} }
EXPORT_SYMBOL(neigh_create); EXPORT_SYMBOL(__neigh_create);
static u32 pneigh_hash(const void *pkey, int key_len) static u32 pneigh_hash(const void *pkey, int key_len)
{ {
......
...@@ -170,6 +170,7 @@ static inline int ip_finish_output2(struct sk_buff *skb) ...@@ -170,6 +170,7 @@ static inline int ip_finish_output2(struct sk_buff *skb)
struct net_device *dev = dst->dev; struct net_device *dev = dst->dev;
unsigned int hh_len = LL_RESERVED_SPACE(dev); unsigned int hh_len = LL_RESERVED_SPACE(dev);
struct neighbour *neigh; struct neighbour *neigh;
u32 nexthop;
if (rt->rt_type == RTN_MULTICAST) { if (rt->rt_type == RTN_MULTICAST) {
IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len); IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);
...@@ -191,15 +192,18 @@ static inline int ip_finish_output2(struct sk_buff *skb) ...@@ -191,15 +192,18 @@ static inline int ip_finish_output2(struct sk_buff *skb)
skb = skb2; skb = skb2;
} }
rcu_read_lock(); rcu_read_lock_bh();
neigh = dst_get_neighbour_noref(dst); 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) { if (neigh) {
int res = neigh_output(neigh, skb); int res = neigh_output(neigh, skb);
rcu_read_unlock(); rcu_read_unlock_bh();
return res; return res;
} }
rcu_read_unlock(); rcu_read_unlock_bh();
net_dbg_ratelimited("%s: No header cache and no neighbour!\n", net_dbg_ratelimited("%s: No header cache and no neighbour!\n",
__func__); __func__);
......
...@@ -1098,17 +1098,13 @@ static int slow_chain_length(const struct rtable *head) ...@@ -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 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; struct net_device *dev = dst->dev;
const __be32 *pkey = daddr; const __be32 *pkey = daddr;
const struct rtable *rt; const struct rtable *rt;
struct neighbour *n; struct neighbour *n;
rt = (const struct rtable *) dst; rt = (const struct rtable *) dst;
if (rt->rt_gateway)
if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
pkey = &inaddr_any;
else if (rt->rt_gateway)
pkey = (const __be32 *) &rt->rt_gateway; pkey = (const __be32 *) &rt->rt_gateway;
n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey); n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment