[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <CALCETrWUtYmSWw9-K1To8UDHe5THqEiwVyeSRNFQBaGuHs4cgg@mail.gmail.com>
Date: Thu, 15 Feb 2024 13:17:30 -0800
From: Andy Lutomirski <luto@...capital.net>
To: Wei Wang <weiwan@...gle.com>, Network Development <netdev@...r.kernel.org>,
Eric Dumazet <edumazet@...gle.com>, "David S. Miller" <davem@...emloft.net>
Subject: SO_RESERVE_MEM doesn't quite work, at least on UDP
With SO_RESERVE_MEM, I can reserve memory, and it gets credited to
sk_forward_alloc. But, as far as I can tell, nothing keeps it from
getting "reclaimed" (i.e. un-credited from sk_forward_alloc and
uncharged). So, for UDP at least, it basically doesn't work.
Here's a test program in Python:
#!/usr/bin/python3
import typing
import argparse
import socket
import sys
import dataclasses
import struct
SO_RESERVE_MEM = 73
@dataclasses.dataclass
class MemInfo:
RMEM_ALLOC : int
RCVBUF : int
WMEM_ALLOC : int
SNDBUF : int
FWD_ALLOC : int
WMEM_QUEUED : int
OPTMEM : int
BACKLOG : int
DROPS : int
def sk_meminfo(s : socket.socket) -> MemInfo:
nvars = len(dataclasses.fields(MemInfo))
buf = s.getsockopt(socket.SOL_SOCKET, 55, 4*nvars) # SO_MEMINFO
return MemInfo(*struct.unpack(f"{nvars}I", buf))
def main() -> None:
parser = argparse.ArgumentParser(description='Measure UDP socket
receive buffers')
parser.add_argument('--sendsize', type=int, default=1024,
help='Size of each individual send')
parser.add_argument('--rcvbuf', type=int,
help='SO_RCVBUF')
parser.add_argument('--reserve-mem', type=int,
help='SO_RESERVE_MEM')
args = parser.parse_args()
receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
receiver.bind(('127.0.0.1', 0))
bound_addr = receiver.getsockname()
print('Receiving on %s:%d' % bound_addr, file=sys.stderr)
sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
sender.connect(bound_addr)
if args.rcvbuf is not None:
receiver.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, args.rcvbuf)
print('SO_RCVBUF = %d' % receiver.getsockopt(socket.SOL_SOCKET,
socket.SO_RCVBUF))
if args.reserve_mem is not None:
receiver.setsockopt(socket.SOL_SOCKET, SO_RESERVE_MEM, args.reserve_mem)
print('SO_RESERVE_MEM %d succeeded' % args.reserve_mem)
print(f"{'sent':>10} {'total':>10}")
meminfo = sk_meminfo(receiver)
print(f"{'':>10} {0:>10} {meminfo}")
bytes_sent = 0
while meminfo.DROPS == 0:
bytes_now = args.sendsize
sender.send(bytes_now * b'x')
bytes_sent += bytes_now
meminfo = sk_meminfo(receiver)
print(f"{bytes_now:>10} {bytes_sent:>10} {meminfo}")
# Be polite to anyone watching using dropwatch or similar tools:
drain the socket
receiver.setblocking(False)
ndgrams = 0
while True:
try:
receiver.recv(1)
ndgrams += 1
except OSError:
break
print('Drained %d datagrams' % ndgrams)
meminfo = sk_meminfo(receiver)
print(f"{'':>10} {'':>10} {meminfo}")
if __name__ == '__main__':
main()
SO_RCVBUF = 212992
SO_RESERVE_MEM 100000 succeeded
sent total
0 MemInfo(RMEM_ALLOC=0, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=102400, WMEM_QUEUED=0,
OPTMEM=0, BACKLOG=0, DROPS=0)
1024 1024 MemInfo(RMEM_ALLOC=2304, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=100096, WMEM_QUEUED=0,
OPTMEM=0, BACKLOG=0, DROPS=0)
1024 2048 MemInfo(RMEM_ALLOC=4608, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=97792, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 3072 MemInfo(RMEM_ALLOC=6912, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=95488, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 4096 MemInfo(RMEM_ALLOC=9216, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=93184, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 5120 MemInfo(RMEM_ALLOC=11520, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=90880, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 6144 MemInfo(RMEM_ALLOC=13824, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=88576, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 7168 MemInfo(RMEM_ALLOC=16128, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=86272, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 8192 MemInfo(RMEM_ALLOC=18432, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=83968, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 9216 MemInfo(RMEM_ALLOC=20736, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=81664, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 10240 MemInfo(RMEM_ALLOC=23040, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=79360, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 11264 MemInfo(RMEM_ALLOC=25344, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=77056, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 12288 MemInfo(RMEM_ALLOC=27648, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=74752, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 13312 MemInfo(RMEM_ALLOC=29952, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=72448, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 14336 MemInfo(RMEM_ALLOC=32256, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=70144, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
<-- Okay, looks like it's working as expected.
1024 94208 MemInfo(RMEM_ALLOC=211968, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=1024, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
1024 95232 MemInfo(RMEM_ALLOC=214272, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=2816, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=0)
<-- And now we're out of space.
1024 96256 MemInfo(RMEM_ALLOC=214272, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=2816, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=1)
<-- And we drop the next datagram, as expected
Drained 93 datagrams
MemInfo(RMEM_ALLOC=0, RCVBUF=212992,
WMEM_ALLOC=0, SNDBUF=212992, FWD_ALLOC=4096, WMEM_QUEUED=0, OPTMEM=0,
BACKLOG=0, DROPS=1)
<-- Now read all the queued data. Whoops, sk_forward_alloc == 4096.
Where'd the reservation go?
Am I right that this is a bug? What's the right way to fix it?
--Andy
Powered by blists - more mailing lists