]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
inetpeer: avoid false sharing in inet_peer_xrlim_allow()
authorEric Dumazet <edumazet@google.com>
Thu, 19 Dec 2024 15:03:30 +0000 (15:03 +0000)
committerJakub Kicinski <kuba@kernel.org>
Fri, 20 Dec 2024 21:04:40 +0000 (13:04 -0800)
Under DOS, inet_peer_xrlim_allow() might be called millions
of times per second from different cpus.

Make sure to write over peer->rate_tokens and peer->rate_last
only when really needed.

Note the inherent races of this function are still there,
we do not care of precise ICMP rate limiting.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20241219150330.3159027-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/inetpeer.c

index e02484f4d22b8ea47cbaeed46c5fb0a7411462a1..b8b23a77ceb4f0f1a3d3adaacea2a7c59a7da3c9 100644 (file)
@@ -246,23 +246,27 @@ void inet_putpeer(struct inet_peer *p)
 #define XRLIM_BURST_FACTOR 6
 bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout)
 {
-       unsigned long now, token;
+       unsigned long now, token, otoken, delta;
        bool rc = false;
 
        if (!peer)
                return true;
 
-       token = peer->rate_tokens;
+       token = otoken = READ_ONCE(peer->rate_tokens);
        now = jiffies;
-       token += now - peer->rate_last;
-       peer->rate_last = now;
-       if (token > XRLIM_BURST_FACTOR * timeout)
-               token = XRLIM_BURST_FACTOR * timeout;
+       delta = now - READ_ONCE(peer->rate_last);
+       if (delta) {
+               WRITE_ONCE(peer->rate_last, now);
+               token += delta;
+               if (token > XRLIM_BURST_FACTOR * timeout)
+                       token = XRLIM_BURST_FACTOR * timeout;
+       }
        if (token >= timeout) {
                token -= timeout;
                rc = true;
        }
-       peer->rate_tokens = token;
+       if (token != otoken)
+               WRITE_ONCE(peer->rate_tokens, token);
        return rc;
 }
 EXPORT_SYMBOL(inet_peer_xrlim_allow);