ipv6: some RCU conversions

ICMP and ND are not fast path, but still we can avoid changing idev
refcount, using RCU.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Eric Dumazet 2011-07-27 21:13:03 +00:00 committed by David S. Miller
parent ab1594e92e
commit cfdf76474e
2 changed files with 21 additions and 35 deletions

View file

@ -490,7 +490,8 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
goto out_dst_release; goto out_dst_release;
} }
idev = in6_dev_get(skb->dev); rcu_read_lock();
idev = __in6_dev_get(skb->dev);
err = ip6_append_data(sk, icmpv6_getfrag, &msg, err = ip6_append_data(sk, icmpv6_getfrag, &msg,
len + sizeof(struct icmp6hdr), len + sizeof(struct icmp6hdr),
@ -500,19 +501,16 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
if (err) { if (err) {
ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS); ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);
ip6_flush_pending_frames(sk); ip6_flush_pending_frames(sk);
goto out_put; } else {
err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
len + sizeof(struct icmp6hdr));
} }
err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, len + sizeof(struct icmp6hdr)); rcu_read_unlock();
out_put:
if (likely(idev != NULL))
in6_dev_put(idev);
out_dst_release: out_dst_release:
dst_release(dst); dst_release(dst);
out: out:
icmpv6_xmit_unlock(sk); icmpv6_xmit_unlock(sk);
} }
EXPORT_SYMBOL(icmpv6_send); EXPORT_SYMBOL(icmpv6_send);
static void icmpv6_echo_reply(struct sk_buff *skb) static void icmpv6_echo_reply(struct sk_buff *skb)
@ -569,7 +567,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
if (hlimit < 0) if (hlimit < 0)
hlimit = ip6_dst_hoplimit(dst); hlimit = ip6_dst_hoplimit(dst);
idev = in6_dev_get(skb->dev); idev = __in6_dev_get(skb->dev);
msg.skb = skb; msg.skb = skb;
msg.offset = 0; msg.offset = 0;
@ -583,13 +581,10 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
if (err) { if (err) {
ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS); ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);
ip6_flush_pending_frames(sk); ip6_flush_pending_frames(sk);
goto out_put; } else {
err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
skb->len + sizeof(struct icmp6hdr));
} }
err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, skb->len + sizeof(struct icmp6hdr));
out_put:
if (likely(idev != NULL))
in6_dev_put(idev);
dst_release(dst); dst_release(dst);
out: out:
icmpv6_xmit_unlock(sk); icmpv6_xmit_unlock(sk);

View file

@ -533,7 +533,8 @@ void ndisc_send_skb(struct sk_buff *skb,
skb_dst_set(skb, dst); skb_dst_set(skb, dst);
idev = in6_dev_get(dst->dev); rcu_read_lock();
idev = __in6_dev_get(dst->dev);
IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev, err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
@ -543,8 +544,7 @@ void ndisc_send_skb(struct sk_buff *skb,
ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
} }
if (likely(idev != NULL)) rcu_read_unlock();
in6_dev_put(idev);
} }
EXPORT_SYMBOL(ndisc_send_skb); EXPORT_SYMBOL(ndisc_send_skb);
@ -1039,7 +1039,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)
if (skb->len < sizeof(*rs_msg)) if (skb->len < sizeof(*rs_msg))
return; return;
idev = in6_dev_get(skb->dev); idev = __in6_dev_get(skb->dev);
if (!idev) { if (!idev) {
if (net_ratelimit()) if (net_ratelimit())
ND_PRINTK1("ICMP6 RS: can't find in6 device\n"); ND_PRINTK1("ICMP6 RS: can't find in6 device\n");
@ -1080,7 +1080,7 @@ static void ndisc_recv_rs(struct sk_buff *skb)
neigh_release(neigh); neigh_release(neigh);
} }
out: out:
in6_dev_put(idev); return;
} }
static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
@ -1179,7 +1179,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
* set the RA_RECV flag in the interface * set the RA_RECV flag in the interface
*/ */
in6_dev = in6_dev_get(skb->dev); in6_dev = __in6_dev_get(skb->dev);
if (in6_dev == NULL) { if (in6_dev == NULL) {
ND_PRINTK0(KERN_ERR ND_PRINTK0(KERN_ERR
"ICMPv6 RA: can't find inet6 device for %s.\n", "ICMPv6 RA: can't find inet6 device for %s.\n",
@ -1188,7 +1188,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
} }
if (!ndisc_parse_options(opt, optlen, &ndopts)) { if (!ndisc_parse_options(opt, optlen, &ndopts)) {
in6_dev_put(in6_dev);
ND_PRINTK2(KERN_WARNING ND_PRINTK2(KERN_WARNING
"ICMP6 RA: invalid ND options\n"); "ICMP6 RA: invalid ND options\n");
return; return;
@ -1255,7 +1254,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
ND_PRINTK0(KERN_ERR ND_PRINTK0(KERN_ERR
"ICMPv6 RA: %s() failed to add default route.\n", "ICMPv6 RA: %s() failed to add default route.\n",
__func__); __func__);
in6_dev_put(in6_dev);
return; return;
} }
@ -1265,7 +1263,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
"ICMPv6 RA: %s() got default router without neighbour.\n", "ICMPv6 RA: %s() got default router without neighbour.\n",
__func__); __func__);
dst_release(&rt->dst); dst_release(&rt->dst);
in6_dev_put(in6_dev);
return; return;
} }
neigh->flags |= NTF_ROUTER; neigh->flags |= NTF_ROUTER;
@ -1422,7 +1419,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
dst_release(&rt->dst); dst_release(&rt->dst);
else if (neigh) else if (neigh)
neigh_release(neigh); neigh_release(neigh);
in6_dev_put(in6_dev);
} }
static void ndisc_redirect_rcv(struct sk_buff *skb) static void ndisc_redirect_rcv(struct sk_buff *skb)
@ -1481,13 +1477,11 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
return; return;
} }
in6_dev = in6_dev_get(skb->dev); in6_dev = __in6_dev_get(skb->dev);
if (!in6_dev) if (!in6_dev)
return; return;
if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) { if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
in6_dev_put(in6_dev);
return; return;
}
/* RFC2461 8.1: /* RFC2461 8.1:
* The IP source address of the Redirect MUST be the same as the current * The IP source address of the Redirect MUST be the same as the current
@ -1497,7 +1491,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) { if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
ND_PRINTK2(KERN_WARNING ND_PRINTK2(KERN_WARNING
"ICMPv6 Redirect: invalid ND options\n"); "ICMPv6 Redirect: invalid ND options\n");
in6_dev_put(in6_dev);
return; return;
} }
if (ndopts.nd_opts_tgt_lladdr) { if (ndopts.nd_opts_tgt_lladdr) {
@ -1506,7 +1499,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
if (!lladdr) { if (!lladdr) {
ND_PRINTK2(KERN_WARNING ND_PRINTK2(KERN_WARNING
"ICMPv6 Redirect: invalid link-layer address length\n"); "ICMPv6 Redirect: invalid link-layer address length\n");
in6_dev_put(in6_dev);
return; return;
} }
} }
@ -1518,7 +1510,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
on_link); on_link);
neigh_release(neigh); neigh_release(neigh);
} }
in6_dev_put(in6_dev);
} }
void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
@ -1651,7 +1642,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
csum_partial(icmph, len, 0)); csum_partial(icmph, len, 0));
skb_dst_set(buff, dst); skb_dst_set(buff, dst);
idev = in6_dev_get(dst->dev); rcu_read_lock();
idev = __in6_dev_get(dst->dev);
IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev, err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
dst_output); dst_output);
@ -1660,8 +1652,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
} }
if (likely(idev != NULL)) rcu_read_unlock();
in6_dev_put(idev);
return; return;
release: release: