为什么 Linux 原始套接字的 RX 环大小限制为 4GB?

Why is the RX ring of a Linux raw socket limited to 4GB in size?

背景

我正在尝试 mmap() 我的 64 位 Linux 应用程序中原始套接字的 RX 环形缓冲区。我的戒指由 4096 个块组成,每个块大小为 1MB,总共 4GB。 (注意每个 1MB 块中可以有很多帧。See this doc for background if you're curious.

问题

不幸的是,在使用 setsockopt() 和选项 PACKET_RX_RING 配置时,RX 环形缓冲区的大小似乎有 4GB 的限制。这对我来说意味着我不能再增加我的块大小或环大小。不过,我的申请会从增加中受益。

此限制至少在 Linux 内核的现代版本中强制执行。 See the source here.

(请注意 mmap() 大于 4GB 没有问题。我最初偶然发现 this question。)

问题

为什么RX环形缓冲区有4GB的限制?如果在从 32 位迁移内核的这一部分时出现错误,那会很酷并且可能很容易打补丁。但是,如果有更根本的原因,我很想知道那可能是什么。

这是部分答案。让我们检查一下 PACKET_MMAP 环形缓冲区结构及其缓冲区是如何分配的。这是在 line 4270 by calling alloc_pg_vec. The structure itself is just an array of pointers and is allocated using kcalloc, which imposes an upper limit of 131,072 bytes. If each pointer is 8 bytes in size, then there can be at most 131,072/8 = 16,384 = 214 blocks. Each block is allocated using alloc_one_pg_vec_page. Note that each block must be contiguous in memory and its size must be a multiple of the page size (4096 bytes). The order parameter passed to alloc_pg_vec represents the number of pages to be allocated as a power of two for each buffer. There is a limit on the order defined in mmzone.h 处完成的,即 MAX_ORDER = 11。因此,最大块大小为 4096 * 211 = 223 = 8 MiB。 这意味着整个环限制为 237 字节或 128 GiB.

我不知道为什么要执行检查req->tp_block_size > UINT_MAX / req->tp_block_nr。这可能是因为 Linux 的旧版本支持较少数量的缓冲区和缓冲区大小(MAX_ORDER 在 Linux 2.4 中为 10)。但也许通过删除检查,您可以分配最大 128 GiB 的环。

这是一个错误。我提交了一个补丁来解决这个问题(现已合并): https://github.com/torvalds/linux/commit/fc62814d690cf62189854464f4bd07457d5e9e50