[PATCH v2] setup: dynamically detect default huge page size

Prateek <[email protected]>
Newsgroups org.kernel.vger.io-uring
Message-ID <[email protected]>
    Replaces the hardcoded 2MB huge page size with dynamic detection by
    parsing /proc/meminfo. This fixes no-mmap allocation failures on
    architectures with different default huge page sizes (like ARM64
    which often uses 512MB) or x86 systems configured for 1GB pages.

    - Safely parses /proc/meminfo without allocating memory.
    - Adds a __uring_memcmp shim for CONFIG_NOLIBC builds, allowing
      setup.c to use standard memcmp for the Hugepagesize: match.
    - Drops the MAP_HUGE_2MB mmap flag to allow the kernel to correctly
      apply the system's default huge page size.
    - Falls back safely to 2MB if /proc/meminfo is unreadable.

Signed-off-by: Prateek <[email protected]>
---
Changes in v2:
- Initialized hps explicitly to 0.
- Replaced the char-by-char Hugepagesize comparison with a new __uring_memcmp helper.
- Removed the redundant ret variable and simplified the fallback assignment using a ternary operator.

 src/lib.h    |  2 ++
 src/nolibc.c | 19 +++++++++++++
 src/setup.c  | 75 +++++++++++++++++++++++++++++++++++++++++-----------
 3 files changed, 80 insertions(+), 16 deletions(-)

diff --git a/src/lib.h b/src/lib.h
index 4d32d3e1..463dd4b5 100644
--- a/src/lib.h
+++ b/src/lib.h
@@ -41,10 +41,12 @@
 void *__uring_memset(void *s, int c, size_t n);
 void *__uring_malloc(size_t len);
 void __uring_free(void *p);
+int __uring_memcmp(const void *s1, const void *s2, size_t n);
 
 #define malloc(LEN)		__uring_malloc(LEN)
 #define free(PTR)		__uring_free(PTR)
 #define memset(PTR, C, LEN)	__uring_memset(PTR, C, LEN)
+#define memcmp(S1, S2, LEN)	__uring_memcmp(S1, S2, LEN)
 #endif
 
 #endif /* #ifndef LIBURING_LIB_H */
diff --git a/src/nolibc.c b/src/nolibc.c
index 88b1494a..14ede500 100644
--- a/src/nolibc.c
+++ b/src/nolibc.c
@@ -25,6 +25,25 @@ void *__uring_memset(void *s, int c, size_t n)
 	return s;
 }
 
+int __uring_memcmp(const void *s1, const void *s2, size_t n)
+{
+	size_t i;
+	const unsigned char *p1 = s1, *p2 = s2;
+
+	for (i = 0; i < n; i++) {
+		if (p1[i] != p2[i])
+			return p1[i] - p2[i];
+
+		/*
+		 * An empty inline ASM to avoid auto-vectorization
+		 * because it's too bloated for liburing.
+		 */
+		__asm__ volatile ("");
+	}
+
+	return 0;
+}
+
 struct uring_heap {
 	size_t		len;
 	char		user_p[] __attribute__((__aligned__));
diff --git a/src/setup.c b/src/setup.c
index ea6f11fd..88f86784 100644
--- a/src/setup.c
+++ b/src/setup.c
@@ -220,15 +220,58 @@ __cold int io_uring_ring_dontfork(struct io_uring *ring)
 	return 0;
 }
 
-#ifndef MAP_HUGE_SHIFT
-#define MAP_HUGE_SHIFT	26
-#endif
-#ifndef MAP_HUGE_2MB
-#define MAP_HUGE_2MB	(21U << MAP_HUGE_SHIFT)
-#endif
 
-/* FIXME */
-static size_t huge_page_size = 2 * 1024 * 1024;
+static size_t get_huge_page_size(void)
+{
+	static size_t hps = 0;
+	char buf[4096];
+	char *p, *end;
+	unsigned long val = 0;
+	ssize_t n;
+	int fd;
+
+	if (hps)
+		return hps;
+
+	fd = __sys_open("/proc/meminfo", O_RDONLY, 0);
+	if (fd < 0)
+		goto out;
+
+	n = __sys_read(fd, buf, sizeof(buf) - 1);
+	__sys_close(fd);
+	if (n <= 0)
+		goto out;
+	buf[n] = '\0';
+
+	/*
+	 * Scan line-by-line for "Hugepagesize:".
+	 */
+	p = buf;
+	end = buf + n;
+	while (p < end) {
+		/* Check if this line starts with "Hugepagesize:" (13 chars) */
+		if (p + 13 <= end && !memcmp(p, "Hugepagesize:", 13)) {
+			p += 13;
+			while (p < end && (*p == ' ' || *p == '\t'))
+				p++;
+			val = 0;
+			while (p < end && *p >= '0' && *p <= '9') {
+				val = val * 10 + (*p - '0');
+				p++;
+			}
+			break;
+		}
+		/* Advance to next line */
+		while (p < end && *p != '\n')
+			p++;
+		if (p < end)
+			p++;
+	}
+out:
+	hps = val ? val * 1024 : 2 * 1024 * 1024;
+	return hps;
+}
+
 
 #define KRING_SIZE	64
 
@@ -261,13 +304,13 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
 	mem_used = (mem_used + page_size - 1) & ~(page_size - 1);
 
 	/*
-	 * A maxed-out number of CQ entries with IORING_SETUP_CQE32 fills a 2MB
-	 * huge page by itself, so the SQ entries won't fit in the same huge
-	 * page. For SQEs, that shouldn't be possible given KERN_MAX_ENTRIES,
+	 * A maxed-out number of CQ entries with IORING_SETUP_CQE32 can fill a
+	 * single huge page by itself, so the SQ entries won't fit in the same
+	 * huge page. For SQEs, that shouldn't be possible given KERN_MAX_ENTRIES,
 	 * but check that too to future-proof (e.g. against different huge page
 	 * sizes). Bail out early so we don't overrun.
 	 */
-	if (!buf && (sqes_mem > huge_page_size || ring_mem > huge_page_size))
+	if (!buf && (sqes_mem > get_huge_page_size() || ring_mem > get_huge_page_size()))
 		return -ENOMEM;
 
 	if (buf) {
@@ -279,8 +322,8 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
 		if (sqes_mem <= page_size)
 			buf_size = page_size;
 		else {
-			buf_size = huge_page_size;
-			map_hugetlb = MAP_HUGETLB | MAP_HUGE_2MB;
+			buf_size = get_huge_page_size();
+			map_hugetlb = MAP_HUGETLB;
 		}
 		sqes_size = buf_size;
 		ptr = __sys_mmap(NULL, sqes_size, PROT_READ|PROT_WRITE,
@@ -302,8 +345,8 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
 		if (ring_mem <= page_size)
 			buf_size = page_size;
 		else {
-			buf_size = huge_page_size;
-			map_hugetlb = MAP_HUGETLB | MAP_HUGE_2MB;
+			buf_size = get_huge_page_size();
+			map_hugetlb = MAP_HUGETLB;
 		}
 		ptr = __sys_mmap(NULL, buf_size, PROT_READ|PROT_WRITE,
 					MAP_SHARED|MAP_ANONYMOUS|map_hugetlb,
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.