[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <749f8a12b2caf634249e7590597f0c53e5b37c7a.1579281705.git.pabeni@redhat.com>
Date: Fri, 17 Jan 2020 18:27:56 +0100
From: Paolo Abeni <pabeni@...hat.com>
To: netdev@...r.kernel.org
Cc: "David S. Miller" <davem@...emloft.net>,
Willem de Bruijn <willemdebruijn.kernel@...il.com>
Subject: [PATCH net 3/3] udp: avoid bulk memory scheduling on memory pressure.
Williem reported that after commit 0d4a6608f68c ("udp: do rmem bulk
free even if the rx sk queue is empty") the memory allocated by
an almost idle system with many UDP sockets can grow a lot.
This change addresses the issue enabling memory pressure tracking
for UDP and flushing the fwd allocated memory on dequeue if the
UDP protocol is under memory pressure.
Note that with this patch applied, the system allocates more
liberally memory for UDP sockets while the total memory usage is
below udp_mem[1], while the vanilla kernel would allow at most a
single page per socket when UDP memory usage goes above udp_mem[0]
- see __sk_mem_raise_allocated().
Reported-and-diagnosed-by: Willem de Bruijn <willemdebruijn.kernel@...il.com>
Fixes: commit 0d4a6608f68c ("udp: do rmem bulk free even if the rx sk queue is empty")
Signed-off-by: Paolo Abeni <pabeni@...hat.com>
---
include/net/udp.h | 2 ++
net/ipv4/udp.c | 13 ++++++++++++-
net/ipv6/udp.c | 2 ++
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/include/net/udp.h b/include/net/udp.h
index bad74f780831..cff730798291 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -94,6 +94,8 @@ static inline struct udp_hslot *udp_hashslot2(struct udp_table *table,
extern struct proto udp_prot;
extern atomic_long_t udp_memory_allocated;
+extern unsigned long udp_memory_pressure;
+extern struct percpu_counter udp_sockets_allocated;
/* sysctl variables for udp */
extern long sysctl_udp_mem[3];
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 93a355b6b092..3a68ec6c3410 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -119,6 +119,12 @@ EXPORT_SYMBOL(udp_table);
long sysctl_udp_mem[3] __read_mostly;
EXPORT_SYMBOL(sysctl_udp_mem);
+unsigned long udp_memory_pressure __read_mostly;
+EXPORT_SYMBOL(udp_memory_pressure);
+
+struct percpu_counter udp_sockets_allocated;
+EXPORT_SYMBOL(udp_sockets_allocated);
+
atomic_long_t udp_memory_allocated;
EXPORT_SYMBOL(udp_memory_allocated);
@@ -1368,7 +1374,8 @@ static void udp_rmem_release(struct sock *sk, int size, int partial,
if (likely(partial)) {
up->forward_deficit += size;
size = up->forward_deficit;
- if (size < (sk->sk_rcvbuf >> 2))
+ if (size < (sk->sk_rcvbuf >> 2) &&
+ !READ_ONCE(udp_memory_pressure))
return;
} else {
size += up->forward_deficit;
@@ -2789,7 +2796,9 @@ struct proto udp_prot = {
.unhash = udp_lib_unhash,
.rehash = udp_v4_rehash,
.get_port = udp_v4_get_port,
+ .memory_pressure = &udp_memory_pressure,
.memory_allocated = &udp_memory_allocated,
+ .sockets_allocated = &udp_sockets_allocated,
.sysctl_mem = sysctl_udp_mem,
.sysctl_wmem_offset = offsetof(struct net, ipv4.sysctl_udp_wmem_min),
.sysctl_rmem_offset = offsetof(struct net, ipv4.sysctl_udp_rmem_min),
@@ -3062,6 +3071,8 @@ void __init udp_init(void)
sysctl_udp_mem[1] = limit;
sysctl_udp_mem[2] = sysctl_udp_mem[0] * 2;
+ percpu_counter_init(&udp_sockets_allocated, 0, GFP_KERNEL);
+
__udp_sysctl_init(&init_net);
/* 16 spinlocks per cpu */
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 9fec580c968e..b29d92574ccc 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1670,7 +1670,9 @@ struct proto udpv6_prot = {
.unhash = udp_lib_unhash,
.rehash = udp_v6_rehash,
.get_port = udp_v6_get_port,
+ .memory_pressure = &udp_memory_pressure,
.memory_allocated = &udp_memory_allocated,
+ .sockets_allocated = &udp_sockets_allocated,
.sysctl_mem = sysctl_udp_mem,
.sysctl_wmem_offset = offsetof(struct net, ipv4.sysctl_udp_wmem_min),
.sysctl_rmem_offset = offsetof(struct net, ipv4.sysctl_udp_rmem_min),
--
2.21.0
Powered by blists - more mailing lists