diff options
| author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-06-08 02:07:07 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2011-06-09 03:24:53 -0400 |
| commit | fe6fe792faec3fc2d2db39b69651682b8c4e7fcb (patch) | |
| tree | bf2ea12d05616187177ae7b699c1349f4cf79871 | |
| parent | 6c43e0465f56248d9da56f2c4665ce1696766814 (diff) | |
net: pmtu_expires fixes
commit 2c8cec5c10bc (ipv4: Cache learned PMTU information in inetpeer)
added some racy peer->pmtu_expires accesses.
As its value can be changed by another cpu/thread, we should be more
careful, reading its value once.
Add peer_pmtu_expired() and peer_pmtu_cleaned() helpers
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | net/ipv4/route.c | 78 |
1 files changed, 44 insertions, 34 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 52b0b956508..045f0ec6a4a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
| @@ -1316,6 +1316,23 @@ reject_redirect: | |||
| 1316 | ; | 1316 | ; |
| 1317 | } | 1317 | } |
| 1318 | 1318 | ||
| 1319 | static bool peer_pmtu_expired(struct inet_peer *peer) | ||
| 1320 | { | ||
| 1321 | unsigned long orig = ACCESS_ONCE(peer->pmtu_expires); | ||
| 1322 | |||
| 1323 | return orig && | ||
| 1324 | time_after_eq(jiffies, orig) && | ||
| 1325 | cmpxchg(&peer->pmtu_expires, orig, 0) == orig; | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | static bool peer_pmtu_cleaned(struct inet_peer *peer) | ||
| 1329 | { | ||
| 1330 | unsigned long orig = ACCESS_ONCE(peer->pmtu_expires); | ||
| 1331 | |||
| 1332 | return orig && | ||
| 1333 | cmpxchg(&peer->pmtu_expires, orig, 0) == orig; | ||
| 1334 | } | ||
| 1335 | |||
| 1319 | static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) | 1336 | static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) |
| 1320 | { | 1337 | { |
| 1321 | struct rtable *rt = (struct rtable *)dst; | 1338 | struct rtable *rt = (struct rtable *)dst; |
| @@ -1331,14 +1348,8 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) | |||
| 1331 | rt_genid(dev_net(dst->dev))); | 1348 | rt_genid(dev_net(dst->dev))); |
| 1332 | rt_del(hash, rt); | 1349 | rt_del(hash, rt); |
| 1333 | ret = NULL; | 1350 | ret = NULL; |
| 1334 | } else if (rt->peer && | 1351 | } else if (rt->peer && peer_pmtu_expired(rt->peer)) { |
| 1335 | rt->peer->pmtu_expires && | 1352 | dst_metric_set(dst, RTAX_MTU, rt->peer->pmtu_orig); |
| 1336 | time_after_eq(jiffies, rt->peer->pmtu_expires)) { | ||
| 1337 | unsigned long orig = rt->peer->pmtu_expires; | ||
| 1338 | |||
| 1339 | if (cmpxchg(&rt->peer->pmtu_expires, orig, 0) == orig) | ||
| 1340 | dst_metric_set(dst, RTAX_MTU, | ||
| 1341 | rt->peer->pmtu_orig); | ||
| 1342 | } | 1353 | } |
| 1343 | } | 1354 | } |
| 1344 | return ret; | 1355 | return ret; |
| @@ -1531,8 +1542,10 @@ unsigned short ip_rt_frag_needed(struct net *net, const struct iphdr *iph, | |||
| 1531 | 1542 | ||
| 1532 | static void check_peer_pmtu(struct dst_entry *dst, struct inet_peer *peer) | 1543 | static void check_peer_pmtu(struct dst_entry *dst, struct inet_peer *peer) |
| 1533 | { | 1544 | { |
| 1534 | unsigned long expires = peer->pmtu_expires; | 1545 | unsigned long expires = ACCESS_ONCE(peer->pmtu_expires); |
| 1535 | 1546 | ||
| 1547 | if (!expires) | ||
| 1548 | return; | ||
| 1536 | if (time_before(jiffies, expires)) { | 1549 | if (time_before(jiffies, expires)) { |
| 1537 | u32 orig_dst_mtu = dst_mtu(dst); | 1550 | u32 orig_dst_mtu = dst_mtu(dst); |
| 1538 | if (peer->pmtu_learned < orig_dst_mtu) { | 1551 | if (peer->pmtu_learned < orig_dst_mtu) { |
| @@ -1555,10 +1568,11 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) | |||
| 1555 | rt_bind_peer(rt, rt->rt_dst, 1); | 1568 | rt_bind_peer(rt, rt->rt_dst, 1); |
| 1556 | peer = rt->peer; | 1569 | peer = rt->peer; |
| 1557 | if (peer) { | 1570 | if (peer) { |
| 1571 | unsigned long pmtu_expires = ACCESS_ONCE(peer->pmtu_expires); | ||
| 1572 | |||
| 1558 | if (mtu < ip_rt_min_pmtu) | 1573 | if (mtu < ip_rt_min_pmtu) |
| 1559 | mtu = ip_rt_min_pmtu; | 1574 | mtu = ip_rt_min_pmtu; |
| 1560 | if (!peer->pmtu_expires || mtu < peer->pmtu_learned) { | 1575 | if (!pmtu_expires || mtu < peer->pmtu_learned) { |
| 1561 | unsigned long pmtu_expires; | ||
| 1562 | 1576 | ||
| 1563 | pmtu_expires = jiffies + ip_rt_mtu_expires; | 1577 | pmtu_expires = jiffies + ip_rt_mtu_expires; |
| 1564 | if (!pmtu_expires) | 1578 | if (!pmtu_expires) |
| @@ -1612,13 +1626,14 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) | |||
| 1612 | rt_bind_peer(rt, rt->rt_dst, 0); | 1626 | rt_bind_peer(rt, rt->rt_dst, 0); |
| 1613 | 1627 | ||
| 1614 | peer = rt->peer; | 1628 | peer = rt->peer; |
| 1615 | if (peer && peer->pmtu_expires) | 1629 | if (peer) { |
| 1616 | check_peer_pmtu(dst, peer); | 1630 | check_peer_pmtu(dst, peer); |
| 1617 | 1631 | ||
| 1618 | if (peer && peer->redirect_learned.a4 && | 1632 | if (peer->redirect_learned.a4 && |
| 1619 | peer->redirect_learned.a4 != rt->rt_gateway) { | 1633 | peer->redirect_learned.a4 != rt->rt_gateway) { |
| 1620 | if (check_peer_redir(dst, peer)) | 1634 | if (check_peer_redir(dst, peer)) |
| 1621 | return NULL; | 1635 | return NULL; |
| 1636 | } | ||
| 1622 | } | 1637 | } |
| 1623 | 1638 | ||
| 1624 | rt->rt_peer_genid = rt_peer_genid(); | 1639 | rt->rt_peer_genid = rt_peer_genid(); |
| @@ -1649,14 +1664,8 @@ static void ipv4_link_failure(struct sk_buff *skb) | |||
| 1649 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); | 1664 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); |
| 1650 | 1665 | ||
| 1651 | rt = skb_rtable(skb); | 1666 | rt = skb_rtable(skb); |
| 1652 | if (rt && | 1667 | if (rt && rt->peer && peer_pmtu_cleaned(rt->peer)) |
| 1653 | rt->peer && | 1668 | dst_metric_set(&rt->dst, RTAX_MTU, rt->peer->pmtu_orig); |
| 1654 | rt->peer->pmtu_expires) { | ||
| 1655 | unsigned long orig = rt->peer->pmtu_expires; | ||
| 1656 | |||
| 1657 | if (cmpxchg(&rt->peer->pmtu_expires, orig, 0) == orig) | ||
| 1658 | dst_metric_set(&rt->dst, RTAX_MTU, rt->peer->pmtu_orig); | ||
| 1659 | } | ||
| 1660 | } | 1669 | } |
| 1661 | 1670 | ||
| 1662 | static int ip_rt_bug(struct sk_buff *skb) | 1671 | static int ip_rt_bug(struct sk_buff *skb) |
| @@ -1770,8 +1779,7 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, | |||
| 1770 | sizeof(u32) * RTAX_MAX); | 1779 | sizeof(u32) * RTAX_MAX); |
| 1771 | dst_init_metrics(&rt->dst, peer->metrics, false); | 1780 | dst_init_metrics(&rt->dst, peer->metrics, false); |
| 1772 | 1781 | ||
| 1773 | if (peer->pmtu_expires) | 1782 | check_peer_pmtu(&rt->dst, peer); |
| 1774 | check_peer_pmtu(&rt->dst, peer); | ||
| 1775 | if (peer->redirect_learned.a4 && | 1783 | if (peer->redirect_learned.a4 && |
| 1776 | peer->redirect_learned.a4 != rt->rt_gateway) { | 1784 | peer->redirect_learned.a4 != rt->rt_gateway) { |
| 1777 | rt->rt_gateway = peer->redirect_learned.a4; | 1785 | rt->rt_gateway = peer->redirect_learned.a4; |
| @@ -2775,7 +2783,8 @@ static int rt_fill_info(struct net *net, | |||
| 2775 | struct rtable *rt = skb_rtable(skb); | 2783 | struct rtable *rt = skb_rtable(skb); |
| 2776 | struct rtmsg *r; | 2784 | struct rtmsg *r; |
| 2777 | struct nlmsghdr *nlh; | 2785 | struct nlmsghdr *nlh; |
| 2778 | long expires; | 2786 | long expires = 0; |
| 2787 | const struct inet_peer *peer = rt->peer; | ||
| 2779 | u32 id = 0, ts = 0, tsage = 0, error; | 2788 | u32 id = 0, ts = 0, tsage = 0, error; |
| 2780 | 2789 | ||
| 2781 | nlh = nlmsg_put(skb, pid, seq, event, sizeof(*r), flags); | 2790 | nlh = nlmsg_put(skb, pid, seq, event, sizeof(*r), flags); |
| @@ -2823,15 +2832,16 @@ static int rt_fill_info(struct net *net, | |||
| 2823 | NLA_PUT_BE32(skb, RTA_MARK, rt->rt_mark); | 2832 | NLA_PUT_BE32(skb, RTA_MARK, rt->rt_mark); |
| 2824 | 2833 | ||
| 2825 | error = rt->dst.error; | 2834 | error = rt->dst.error; |
| 2826 | expires = (rt->peer && rt->peer->pmtu_expires) ? | 2835 | if (peer) { |
| 2827 | rt->peer->pmtu_expires - jiffies : 0; | ||
| 2828 | if (rt->peer) { | ||
| 2829 | inet_peer_refcheck(rt->peer); | 2836 | inet_peer_refcheck(rt->peer); |
| 2830 | id = atomic_read(&rt->peer->ip_id_count) & 0xffff; | 2837 | id = atomic_read(&peer->ip_id_count) & 0xffff; |
| 2831 | if (rt->peer->tcp_ts_stamp) { | 2838 | if (peer->tcp_ts_stamp) { |
| 2832 | ts = rt->peer->tcp_ts; | 2839 | ts = peer->tcp_ts; |
| 2833 | tsage = get_seconds() - rt->peer->tcp_ts_stamp; | 2840 | tsage = get_seconds() - peer->tcp_ts_stamp; |
| 2834 | } | 2841 | } |
| 2842 | expires = ACCESS_ONCE(peer->pmtu_expires); | ||
| 2843 | if (expires) | ||
| 2844 | expires -= jiffies; | ||
| 2835 | } | 2845 | } |
| 2836 | 2846 | ||
| 2837 | if (rt_is_input_route(rt)) { | 2847 | if (rt_is_input_route(rt)) { |
