Skip to content

Commit 7ba91ec

Browse files
netoptimizerdavem330
authored andcommitted
net: for rate-limited ICMP replies save one atomic operation
It is possible to avoid the atomic operation in icmp{v6,}_xmit_lock, by checking the sysctl_icmp_msgs_per_sec ratelimit before these calls, as pointed out by Eric Dumazet, but the BH disabled state must be correct. The icmp_global_allow() call states it must be called with BH disabled. This protection was given by the calls icmp_xmit_lock and icmpv6_xmit_lock. Thus, split out local_bh_disable/enable from these functions and maintain it explicitly at callers. Suggested-by: Eric Dumazet <[email protected]> Signed-off-by: Jesper Dangaard Brouer <[email protected]> Acked-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c0303ef commit 7ba91ec

File tree

2 files changed

+37
-22
lines changed

2 files changed

+37
-22
lines changed

net/ipv4/icmp.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -209,27 +209,25 @@ static struct sock *icmp_sk(struct net *net)
209209
return *this_cpu_ptr(net->ipv4.icmp_sk);
210210
}
211211

212+
/* Called with BH disabled */
212213
static inline struct sock *icmp_xmit_lock(struct net *net)
213214
{
214215
struct sock *sk;
215216

216-
local_bh_disable();
217-
218217
sk = icmp_sk(net);
219218

220219
if (unlikely(!spin_trylock(&sk->sk_lock.slock))) {
221220
/* This can happen if the output path signals a
222221
* dst_link_failure() for an outgoing ICMP packet.
223222
*/
224-
local_bh_enable();
225223
return NULL;
226224
}
227225
return sk;
228226
}
229227

230228
static inline void icmp_xmit_unlock(struct sock *sk)
231229
{
232-
spin_unlock_bh(&sk->sk_lock.slock);
230+
spin_unlock(&sk->sk_lock.slock);
233231
}
234232

235233
int sysctl_icmp_msgs_per_sec __read_mostly = 1000;
@@ -417,14 +415,17 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
417415
if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb))
418416
return;
419417

420-
sk = icmp_xmit_lock(net);
421-
if (!sk)
422-
return;
423-
inet = inet_sk(sk);
418+
/* Needed by both icmp_global_allow and icmp_xmit_lock */
419+
local_bh_disable();
424420

425421
/* global icmp_msgs_per_sec */
426422
if (!icmpv4_global_allow(net, type, code))
427-
goto out_unlock;
423+
goto out_bh_enable;
424+
425+
sk = icmp_xmit_lock(net);
426+
if (!sk)
427+
goto out_bh_enable;
428+
inet = inet_sk(sk);
428429

429430
icmp_param->data.icmph.checksum = 0;
430431

@@ -459,6 +460,8 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
459460
ip_rt_put(rt);
460461
out_unlock:
461462
icmp_xmit_unlock(sk);
463+
out_bh_enable:
464+
local_bh_enable();
462465
}
463466

464467
#ifdef CONFIG_IP_ROUTE_MULTIPATH
@@ -668,13 +671,16 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
668671
}
669672
}
670673

671-
sk = icmp_xmit_lock(net);
672-
if (!sk)
673-
goto out;
674+
/* Needed by both icmp_global_allow and icmp_xmit_lock */
675+
local_bh_disable();
674676

675677
/* Check global sysctl_icmp_msgs_per_sec ratelimit */
676678
if (!icmpv4_global_allow(net, type, code))
677-
goto out_unlock;
679+
goto out_bh_enable;
680+
681+
sk = icmp_xmit_lock(net);
682+
if (!sk)
683+
goto out_bh_enable;
678684

679685
/*
680686
* Construct source address and options.
@@ -750,6 +756,8 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
750756
ip_rt_put(rt);
751757
out_unlock:
752758
icmp_xmit_unlock(sk);
759+
out_bh_enable:
760+
local_bh_enable();
753761
out:;
754762
}
755763
EXPORT_SYMBOL(icmp_send);

net/ipv6/icmp.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,25 @@ static const struct inet6_protocol icmpv6_protocol = {
110110
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
111111
};
112112

113+
/* Called with BH disabled */
113114
static __inline__ struct sock *icmpv6_xmit_lock(struct net *net)
114115
{
115116
struct sock *sk;
116117

117-
local_bh_disable();
118-
119118
sk = icmpv6_sk(net);
120119
if (unlikely(!spin_trylock(&sk->sk_lock.slock))) {
121120
/* This can happen if the output path (f.e. SIT or
122121
* ip6ip6 tunnel) signals dst_link_failure() for an
123122
* outgoing ICMP6 packet.
124123
*/
125-
local_bh_enable();
126124
return NULL;
127125
}
128126
return sk;
129127
}
130128

131129
static __inline__ void icmpv6_xmit_unlock(struct sock *sk)
132130
{
133-
spin_unlock_bh(&sk->sk_lock.slock);
131+
spin_unlock(&sk->sk_lock.slock);
134132
}
135133

136134
/*
@@ -489,6 +487,13 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
489487
return;
490488
}
491489

490+
/* Needed by both icmp_global_allow and icmpv6_xmit_lock */
491+
local_bh_disable();
492+
493+
/* Check global sysctl_icmp_msgs_per_sec ratelimit */
494+
if (!icmpv6_global_allow(type))
495+
goto out_bh_enable;
496+
492497
mip6_addr_swap(skb);
493498

494499
memset(&fl6, 0, sizeof(fl6));
@@ -507,10 +512,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
507512

508513
sk = icmpv6_xmit_lock(net);
509514
if (!sk)
510-
return;
511-
512-
if (!icmpv6_global_allow(type))
513-
goto out;
515+
goto out_bh_enable;
514516

515517
sk->sk_mark = mark;
516518
np = inet6_sk(sk);
@@ -571,6 +573,8 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
571573
dst_release(dst);
572574
out:
573575
icmpv6_xmit_unlock(sk);
576+
out_bh_enable:
577+
local_bh_enable();
574578
}
575579

576580
/* Slightly more convenient version of icmp6_send.
@@ -684,9 +688,10 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
684688
fl6.flowi6_uid = sock_net_uid(net, NULL);
685689
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
686690

691+
local_bh_disable();
687692
sk = icmpv6_xmit_lock(net);
688693
if (!sk)
689-
return;
694+
goto out_bh_enable;
690695
sk->sk_mark = mark;
691696
np = inet6_sk(sk);
692697

@@ -728,6 +733,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
728733
dst_release(dst);
729734
out:
730735
icmpv6_xmit_unlock(sk);
736+
out_bh_enable:
737+
local_bh_enable();
731738
}
732739

733740
void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)

0 commit comments

Comments
 (0)