If you try to grow a 'large' (at least half a page) allocation with
realloc(3), it'll try to extend the memory mapping for it and if that
works it won't need to move it.
Currently, it does that by using mquery(2) to see if that area is
available and if so then trying to mmap it, munmaping it if mmap(2) didn't
return the desired address (perhaps due to a race with another thread).
Instead of doing mquery/mmap/munmap, this path can use the MAP_FIXED and
__MAP_NOREPLACE flags to directly request the specific address but not
change anything if it's not available.
(We still use mquery in ld.so on i386 as a performance optimization, but
that use case differs in needing to find a base address for *multiple*
mappings, where unrolling partial hits grows to be more expensive than
probing with mquery and only trying to do all the mapping on a successful
set of probes: recall that on x86 munmap() is more expensive than mmap()
due to TLB shootdowns. This case in realloc() only has a single mapping to
probe/extend so it avoids those costs. Indeed, this diff eliminates the
current "munmap on failed attempt" path/cost.)
The regress/lib/libc/malloc tests continue to pass on my amd64 box, with
ktrace confirming the change in calls.
oks?
Philip
Index: stdlib/malloc.c
===================================================================
RCS file: /data/src/openbsd/src/lib/libc/stdlib/malloc.c,v
retrieving revision 1.273
diff -u -p -r1.273 malloc.c
--- stdlib/malloc.c 26 Feb 2022 16:14:42 -0000 1.273
+++ stdlib/malloc.c 14 May 2022 02:35:04 -0000
@@ -100,9 +100,6 @@
#define MMAPA(a,sz,f) mmap((a), (sz), PROT_READ | PROT_WRITE, \
MAP_ANON | MAP_PRIVATE | (f), -1, 0)
-#define MQUERY(a,sz,f) mquery((a), (sz), PROT_READ | PROT_WRITE, \
- MAP_ANON | MAP_PRIVATE | MAP_FIXED | (f), -1, 0)
-
struct region_info {
void *p; /* page; low bits used to mark chunks */
uintptr_t size; /* size for pages, or chunk_info pointer */
@@ -1687,11 +1684,7 @@ orealloc(struct dir_info **argpool, void
size_t needed = rnewsz - roldsz;
STATS_INC(pool->cheap_realloc_tries);
- q = MQUERY(hint, needed, pool->mmap_flag);
- if (q == hint)
- q = MMAPA(hint, needed,
pool->mmap_flag);
- else
- q = MAP_FAILED;
+ q = MMAPA(hint, needed, MAP_FIXED |
__MAP_NOREPLACE | pool->mmap_flag);
if (q == hint) {
STATS_ADD(pool->malloc_used, needed);
if (pool->malloc_junk == 2)
@@ -1709,9 +1702,6 @@ orealloc(struct dir_info **argpool, void
STATS_INC(pool->cheap_reallocs);
ret = p;
goto done;
- } else if (q != MAP_FAILED) {
- if (munmap(q, needed))
- wrterror(pool, "munmap %p", q);
}
}
} else if (rnewsz < roldsz) {