diff options
author | Matthew Dempsky <matthew@cvs.openbsd.org> | 2012-06-18 17:03:53 +0000 |
---|---|---|
committer | Matthew Dempsky <matthew@cvs.openbsd.org> | 2012-06-18 17:03:53 +0000 |
commit | c68a2702b1752fe6c03e138edb72fee4f93d215a (patch) | |
tree | f45a801997441e06da3238e23468a682c862fd5f | |
parent | 120e6b0ec214f1b5d3fc4373cfb944692ffff7fa (diff) |
Support larger-than-page-alignment requests in posix_memalign() by
overallocating and then releasing unneeded memory pages.
ok otto
-rw-r--r-- | lib/libc/stdlib/malloc.c | 147 | ||||
-rw-r--r-- | lib/libc/stdlib/posix_memalign.3 | 6 |
2 files changed, 130 insertions, 23 deletions
diff --git a/lib/libc/stdlib/malloc.c b/lib/libc/stdlib/malloc.c index 6aba00e4a0b..6f646934b22 100644 --- a/lib/libc/stdlib/malloc.c +++ b/lib/libc/stdlib/malloc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: malloc.c,v 1.141 2012/02/29 08:44:14 otto Exp $ */ +/* $OpenBSD: malloc.c,v 1.142 2012/06/18 17:03:51 matthew Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> * @@ -389,7 +389,7 @@ map(struct dir_info *d, size_t sz, int zero_fill) wrterror("internal struct corrupt", NULL); if (sz != PAGEROUND(sz)) { wrterror("map round", NULL); - return NULL; + return MAP_FAILED; } if (psz > d->free_regions_size) { p = MMAP(sz); @@ -1295,8 +1295,10 @@ orealloc(void *p, size_t newsz, void *f) STATS_SETF(r, f); STATS_INC(g_pool->cheap_reallocs); return p; - } else if (q != MAP_FAILED) - munmap(q, rnewsz - roldsz); + } else if (q != MAP_FAILED) { + if (munmap(q, rnewsz - roldsz)) + wrterror("munmap", q); + } } } else if (rnewsz < roldsz) { if (mopts.malloc_guard) { @@ -1413,29 +1415,136 @@ calloc(size_t nmemb, size_t size) return r; } +static void * +mapalign(struct dir_info *d, size_t alignment, size_t sz, int zero_fill) +{ + void *p, *q; + + if (alignment < MALLOC_PAGESIZE || alignment & (alignment - 1) != 0) { + wrterror("mapalign bad alignment", NULL); + return MAP_FAILED; + } + if (sz != PAGEROUND(sz)) { + wrterror("mapalign round", NULL); + return MAP_FAILED; + } + + /* Allocate sz + alignment bytes of memory, which must include a + * subrange of size bytes that is properly aligned. Unmap the + * other bytes, and then return that subrange. + */ + + /* We need sz + alignment to fit into a size_t. */ + if (alignment > SIZE_MAX - sz) + return MAP_FAILED; + + p = map(d, sz + alignment, zero_fill); + if (p == MAP_FAILED) + return MAP_FAILED; + q = (void *)(((uintptr_t)p + alignment - 1) & ~(alignment - 1)); + if (q != p) { + if (munmap(p, q - p)) + wrterror("munmap", p); + } + if (munmap(q + sz, alignment - (q - p))) + wrterror("munmap", q + sz); + malloc_used -= alignment; + + return q; +} + +static void * +omemalign(size_t alignment, size_t sz, int zero_fill, void *f) +{ + size_t psz; + void *p; + + if (alignment <= MALLOC_PAGESIZE) { + /* + * max(size, alignment) is enough to assure the requested alignment, + * since the allocator always allocates power-of-two blocks. + */ + if (sz < alignment) + sz = alignment; + return omalloc(sz, zero_fill, f); + } + + if (sz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { + errno = ENOMEM; + return NULL; + } + + sz += mopts.malloc_guard; + psz = PAGEROUND(sz); + + p = mapalign(g_pool, alignment, psz, zero_fill); + if (p == NULL) { + errno = ENOMEM; + return NULL; + } + + if (insert(g_pool, p, sz, f)) { + unmap(g_pool, p, psz); + errno = ENOMEM; + return NULL; + } + + if (mopts.malloc_guard) { + if (mprotect((char *)p + psz - mopts.malloc_guard, + mopts.malloc_guard, PROT_NONE)) + wrterror("mprotect", NULL); + malloc_guarded += mopts.malloc_guard; + } + + if (mopts.malloc_junk) { + if (zero_fill) + memset((char *)p + sz - mopts.malloc_guard, + SOME_JUNK, psz - sz); + else + memset(p, SOME_JUNK, psz - mopts.malloc_guard); + } + + return p; +} + int posix_memalign(void **memptr, size_t alignment, size_t size) { - void *result; + int res, saved_errno = errno; + void *r; /* Make sure that alignment is a large enough power of 2. */ - if (((alignment - 1) & alignment) != 0 || alignment < sizeof(void *) || - alignment > MALLOC_PAGESIZE) + if (((alignment - 1) & alignment) != 0 || alignment < sizeof(void *)) return EINVAL; - /* - * max(size, alignment) is enough to assure the requested alignment, - * since the allocator always allocates power-of-two blocks. - */ - if (size < alignment) - size = alignment; - result = malloc(size); - - if (result == NULL) - return ENOMEM; - - *memptr = result; + _MALLOC_LOCK(); + malloc_func = " in posix_memalign():"; + if (g_pool == NULL) { + if (malloc_init() != 0) + goto err; + } + if (malloc_active++) { + malloc_recurse(); + goto err; + } + r = omemalign(alignment, size, mopts.malloc_zero, CALLER); + malloc_active--; + _MALLOC_UNLOCK(); + if (r == NULL) { + if (mopts.malloc_xmalloc) { + wrterror("out of memory", NULL); + errno = ENOMEM; + } + goto err; + } + errno = saved_errno; + *memptr = r; return 0; + +err: + res = errno; + errno = saved_errno; + return res; } #ifdef MALLOC_STATS diff --git a/lib/libc/stdlib/posix_memalign.3 b/lib/libc/stdlib/posix_memalign.3 index 46b74af7bef..05ec1b9d148 100644 --- a/lib/libc/stdlib/posix_memalign.3 +++ b/lib/libc/stdlib/posix_memalign.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: posix_memalign.3,v 1.2 2010/05/19 06:32:43 jmc Exp $ +.\" $OpenBSD: posix_memalign.3,v 1.3 2012/06/18 17:03:52 matthew Exp $ .\" Copyright (C) 2006 Jason Evans <jasone@FreeBSD.org>. .\" All rights reserved. .\" @@ -28,7 +28,7 @@ .\" .\" $FreeBSD: src/lib/libc/stdlib/posix_memalign.3,v 1.3 2007/03/28 04:32:51 jasone Exp $ .\" -.Dd $Mdocdate: May 19 2010 $ +.Dd $Mdocdate: June 18 2012 $ .Dt POSIX_MEMALIGN 3 .Os .Sh NAME @@ -90,5 +90,3 @@ The .Fn posix_memalign function first appeared in .Ox 4.8 . -.Sh BUGS -Only alignments up to the page size can be specified. |