diff options
author | otto <> | 2016-09-01 10:41:02 +0000 |
---|---|---|
committer | otto <> | 2016-09-01 10:41:02 +0000 |
commit | b1956fe5420530b5f79ae749a4a0dc7637d37ca1 (patch) | |
tree | c68052597a52801dd4d6ba434fc8b86434a1ce5b /src | |
parent | f4ceb868e6233874d753365a18b835f8cb2e1876 (diff) | |
download | openbsd-b1956fe5420530b5f79ae749a4a0dc7637d37ca1.tar.gz openbsd-b1956fe5420530b5f79ae749a4a0dc7637d37ca1.tar.bz2 openbsd-b1956fe5420530b5f79ae749a4a0dc7637d37ca1.zip |
Less lock contention by using more pools for mult-threaded programs.
tested by many (thanks!) ok tedu, guenther@
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libc/include/thread_private.h | 21 | ||||
-rw-r--r-- | src/lib/libc/stdlib/malloc.c | 263 |
2 files changed, 190 insertions, 94 deletions
diff --git a/src/lib/libc/include/thread_private.h b/src/lib/libc/include/thread_private.h index c2e0cf0b3f..81caaaae68 100644 --- a/src/lib/libc/include/thread_private.h +++ b/src/lib/libc/include/thread_private.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: thread_private.h,v 1.27 2016/05/07 19:05:22 guenther Exp $ */ | 1 | /* $OpenBSD: thread_private.h,v 1.28 2016/09/01 10:41:02 otto Exp $ */ |
2 | 2 | ||
3 | /* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */ | 3 | /* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */ |
4 | 4 | ||
@@ -7,6 +7,9 @@ | |||
7 | 7 | ||
8 | #include <stdio.h> /* for FILE and __isthreaded */ | 8 | #include <stdio.h> /* for FILE and __isthreaded */ |
9 | 9 | ||
10 | #define _MALLOC_MUTEXES 4 | ||
11 | void _malloc_init(int); | ||
12 | |||
10 | /* | 13 | /* |
11 | * The callbacks needed by libc to handle the threaded case. | 14 | * The callbacks needed by libc to handle the threaded case. |
12 | * NOTE: Bump the version when you change the struct contents! | 15 | * NOTE: Bump the version when you change the struct contents! |
@@ -72,8 +75,8 @@ struct thread_callbacks { | |||
72 | void (*tc_flockfile)(FILE *); | 75 | void (*tc_flockfile)(FILE *); |
73 | int (*tc_ftrylockfile)(FILE *); | 76 | int (*tc_ftrylockfile)(FILE *); |
74 | void (*tc_funlockfile)(FILE *); | 77 | void (*tc_funlockfile)(FILE *); |
75 | void (*tc_malloc_lock)(void); | 78 | void (*tc_malloc_lock)(int); |
76 | void (*tc_malloc_unlock)(void); | 79 | void (*tc_malloc_unlock)(int); |
77 | void (*tc_atexit_lock)(void); | 80 | void (*tc_atexit_lock)(void); |
78 | void (*tc_atexit_unlock)(void); | 81 | void (*tc_atexit_unlock)(void); |
79 | void (*tc_atfork_lock)(void); | 82 | void (*tc_atfork_lock)(void); |
@@ -137,8 +140,8 @@ extern void *__THREAD_NAME(serv_mutex); | |||
137 | #define _MUTEX_LOCK(mutex) do {} while (0) | 140 | #define _MUTEX_LOCK(mutex) do {} while (0) |
138 | #define _MUTEX_UNLOCK(mutex) do {} while (0) | 141 | #define _MUTEX_UNLOCK(mutex) do {} while (0) |
139 | #define _MUTEX_DESTROY(mutex) do {} while (0) | 142 | #define _MUTEX_DESTROY(mutex) do {} while (0) |
140 | #define _MALLOC_LOCK() do {} while (0) | 143 | #define _MALLOC_LOCK(n) do {} while (0) |
141 | #define _MALLOC_UNLOCK() do {} while (0) | 144 | #define _MALLOC_UNLOCK(n) do {} while (0) |
142 | #define _ATEXIT_LOCK() do {} while (0) | 145 | #define _ATEXIT_LOCK() do {} while (0) |
143 | #define _ATEXIT_UNLOCK() do {} while (0) | 146 | #define _ATEXIT_UNLOCK() do {} while (0) |
144 | #define _ATFORK_LOCK() do {} while (0) | 147 | #define _ATFORK_LOCK() do {} while (0) |
@@ -184,15 +187,15 @@ extern void *__THREAD_NAME(serv_mutex); | |||
184 | /* | 187 | /* |
185 | * malloc lock/unlock prototypes and definitions | 188 | * malloc lock/unlock prototypes and definitions |
186 | */ | 189 | */ |
187 | #define _MALLOC_LOCK() \ | 190 | #define _MALLOC_LOCK(n) \ |
188 | do { \ | 191 | do { \ |
189 | if (__isthreaded) \ | 192 | if (__isthreaded) \ |
190 | _thread_cb.tc_malloc_lock(); \ | 193 | _thread_cb.tc_malloc_lock(n); \ |
191 | } while (0) | 194 | } while (0) |
192 | #define _MALLOC_UNLOCK() \ | 195 | #define _MALLOC_UNLOCK(n) \ |
193 | do { \ | 196 | do { \ |
194 | if (__isthreaded) \ | 197 | if (__isthreaded) \ |
195 | _thread_cb.tc_malloc_unlock(); \ | 198 | _thread_cb.tc_malloc_unlock(n); \ |
196 | } while (0) | 199 | } while (0) |
197 | 200 | ||
198 | #define _ATEXIT_LOCK() \ | 201 | #define _ATEXIT_LOCK() \ |
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index c84b7c8ba3..ce869412f5 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* $OpenBSD: malloc.c,v 1.194 2016/09/01 10:20:22 tedu Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.195 2016/09/01 10:41:02 otto Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2008, 2010, 2011 Otto Moerbeek <otto@drijf.net> | 3 | * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> |
4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> | 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> |
5 | * Copyright (c) 2008 Damien Miller <djm@openbsd.org> | 5 | * Copyright (c) 2008 Damien Miller <djm@openbsd.org> |
6 | * Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org> | 6 | * Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org> |
@@ -43,6 +43,7 @@ | |||
43 | #endif | 43 | #endif |
44 | 44 | ||
45 | #include "thread_private.h" | 45 | #include "thread_private.h" |
46 | #include <tib.h> | ||
46 | 47 | ||
47 | #if defined(__mips64__) | 48 | #if defined(__mips64__) |
48 | #define MALLOC_PAGESHIFT (14U) | 49 | #define MALLOC_PAGESHIFT (14U) |
@@ -118,6 +119,7 @@ struct dir_info { | |||
118 | void *delayed_chunks[MALLOC_DELAYED_CHUNK_MASK + 1]; | 119 | void *delayed_chunks[MALLOC_DELAYED_CHUNK_MASK + 1]; |
119 | size_t rbytesused; /* random bytes used */ | 120 | size_t rbytesused; /* random bytes used */ |
120 | char *func; /* current function */ | 121 | char *func; /* current function */ |
122 | int mutex; | ||
121 | u_char rbytes[32]; /* random bytes */ | 123 | u_char rbytes[32]; /* random bytes */ |
122 | u_short chunk_start; | 124 | u_short chunk_start; |
123 | #ifdef MALLOC_STATS | 125 | #ifdef MALLOC_STATS |
@@ -167,7 +169,8 @@ struct chunk_info { | |||
167 | }; | 169 | }; |
168 | 170 | ||
169 | struct malloc_readonly { | 171 | struct malloc_readonly { |
170 | struct dir_info *malloc_pool; /* Main bookkeeping information */ | 172 | struct dir_info *malloc_pool[_MALLOC_MUTEXES]; /* Main bookkeeping information */ |
173 | int malloc_mt; /* multi-threaded mode? */ | ||
171 | int malloc_freenow; /* Free quickly - disable chunk rnd */ | 174 | int malloc_freenow; /* Free quickly - disable chunk rnd */ |
172 | int malloc_freeunmap; /* mprotect free pages PROT_NONE? */ | 175 | int malloc_freeunmap; /* mprotect free pages PROT_NONE? */ |
173 | int malloc_hint; /* call madvice on free pages? */ | 176 | int malloc_hint; /* call madvice on free pages? */ |
@@ -191,14 +194,13 @@ static union { | |||
191 | u_char _pad[MALLOC_PAGESIZE]; | 194 | u_char _pad[MALLOC_PAGESIZE]; |
192 | } malloc_readonly __attribute__((aligned(MALLOC_PAGESIZE))); | 195 | } malloc_readonly __attribute__((aligned(MALLOC_PAGESIZE))); |
193 | #define mopts malloc_readonly.mopts | 196 | #define mopts malloc_readonly.mopts |
194 | #define getpool() mopts.malloc_pool | ||
195 | 197 | ||
196 | char *malloc_options; /* compile-time options */ | 198 | char *malloc_options; /* compile-time options */ |
197 | 199 | ||
198 | static u_char getrbyte(struct dir_info *d); | 200 | static u_char getrbyte(struct dir_info *d); |
199 | 201 | ||
200 | #ifdef MALLOC_STATS | 202 | #ifdef MALLOC_STATS |
201 | void malloc_dump(int); | 203 | void malloc_dump(int, struct dir_info *); |
202 | PROTO_NORMAL(malloc_dump); | 204 | PROTO_NORMAL(malloc_dump); |
203 | static void malloc_exit(void); | 205 | static void malloc_exit(void); |
204 | #define CALLER __builtin_return_address(0) | 206 | #define CALLER __builtin_return_address(0) |
@@ -216,17 +218,17 @@ static void malloc_exit(void); | |||
216 | static inline void | 218 | static inline void |
217 | _MALLOC_LEAVE(struct dir_info *d) | 219 | _MALLOC_LEAVE(struct dir_info *d) |
218 | { | 220 | { |
219 | if (__isthreaded) { | 221 | if (mopts.malloc_mt) { |
220 | d->active--; | 222 | d->active--; |
221 | _MALLOC_UNLOCK(); | 223 | _MALLOC_UNLOCK(d->mutex); |
222 | } | 224 | } |
223 | } | 225 | } |
224 | 226 | ||
225 | static inline void | 227 | static inline void |
226 | _MALLOC_ENTER(struct dir_info *d) | 228 | _MALLOC_ENTER(struct dir_info *d) |
227 | { | 229 | { |
228 | if (__isthreaded) { | 230 | if (mopts.malloc_mt) { |
229 | _MALLOC_LOCK(); | 231 | _MALLOC_LOCK(d->mutex); |
230 | d->active++; | 232 | d->active++; |
231 | } | 233 | } |
232 | } | 234 | } |
@@ -247,14 +249,24 @@ hash(void *p) | |||
247 | return sum; | 249 | return sum; |
248 | } | 250 | } |
249 | 251 | ||
250 | __dead static void | 252 | static inline |
253 | struct dir_info *getpool(void) | ||
254 | { | ||
255 | if (!mopts.malloc_mt) | ||
256 | return mopts.malloc_pool[0]; | ||
257 | else | ||
258 | return mopts.malloc_pool[TIB_GET()->tib_tid & | ||
259 | (_MALLOC_MUTEXES - 1)]; | ||
260 | } | ||
261 | |||
262 | static __dead void | ||
251 | wrterror(struct dir_info *d, char *msg, void *p) | 263 | wrterror(struct dir_info *d, char *msg, void *p) |
252 | { | 264 | { |
253 | char *q = " error: "; | 265 | char *q = " error: "; |
254 | struct iovec iov[7]; | 266 | struct iovec iov[7]; |
255 | char pidbuf[20]; | 267 | char pidbuf[20]; |
256 | char buf[20]; | 268 | char buf[20]; |
257 | int saved_errno = errno; | 269 | int saved_errno = errno, i; |
258 | 270 | ||
259 | iov[0].iov_base = __progname; | 271 | iov[0].iov_base = __progname; |
260 | iov[0].iov_len = strlen(__progname); | 272 | iov[0].iov_len = strlen(__progname); |
@@ -285,7 +297,8 @@ wrterror(struct dir_info *d, char *msg, void *p) | |||
285 | 297 | ||
286 | #ifdef MALLOC_STATS | 298 | #ifdef MALLOC_STATS |
287 | if (mopts.malloc_stats) | 299 | if (mopts.malloc_stats) |
288 | malloc_dump(STDERR_FILENO); | 300 | for (i = 0; i < _MALLOC_MUTEXES; i++) |
301 | malloc_dump(STDERR_FILENO, mopts.malloc_pool[i]); | ||
289 | #endif /* MALLOC_STATS */ | 302 | #endif /* MALLOC_STATS */ |
290 | 303 | ||
291 | errno = saved_errno; | 304 | errno = saved_errno; |
@@ -413,6 +426,7 @@ map(struct dir_info *d, void *hint, size_t sz, int zero_fill) | |||
413 | wrterror(d, "internal struct corrupt", NULL); | 426 | wrterror(d, "internal struct corrupt", NULL); |
414 | if (sz != PAGEROUND(sz)) | 427 | if (sz != PAGEROUND(sz)) |
415 | wrterror(d, "map round", NULL); | 428 | wrterror(d, "map round", NULL); |
429 | |||
416 | if (!hint && psz > d->free_regions_size) { | 430 | if (!hint && psz > d->free_regions_size) { |
417 | _MALLOC_LEAVE(d); | 431 | _MALLOC_LEAVE(d); |
418 | p = MMAP(sz); | 432 | p = MMAP(sz); |
@@ -570,16 +584,11 @@ omalloc_parseopt(char opt) | |||
570 | } | 584 | } |
571 | } | 585 | } |
572 | 586 | ||
573 | /* | 587 | static void |
574 | * Initialize a dir_info, which should have been cleared by caller | 588 | omalloc_init(void) |
575 | */ | ||
576 | static int | ||
577 | omalloc_init(struct dir_info **dp) | ||
578 | { | 589 | { |
579 | char *p, *q, b[64]; | 590 | char *p, *q, b[64]; |
580 | int i, j; | 591 | int i, j; |
581 | size_t d_avail, regioninfo_size; | ||
582 | struct dir_info *d; | ||
583 | 592 | ||
584 | /* | 593 | /* |
585 | * Default options | 594 | * Default options |
@@ -642,6 +651,18 @@ omalloc_init(struct dir_info **dp) | |||
642 | 651 | ||
643 | arc4random_buf(&mopts.malloc_chunk_canary, | 652 | arc4random_buf(&mopts.malloc_chunk_canary, |
644 | sizeof(mopts.malloc_chunk_canary)); | 653 | sizeof(mopts.malloc_chunk_canary)); |
654 | } | ||
655 | |||
656 | /* | ||
657 | * Initialize a dir_info, which should have been cleared by caller | ||
658 | */ | ||
659 | static void | ||
660 | omalloc_poolinit(struct dir_info **dp) | ||
661 | { | ||
662 | void *p; | ||
663 | size_t d_avail, regioninfo_size; | ||
664 | struct dir_info *d; | ||
665 | int i, j; | ||
645 | 666 | ||
646 | /* | 667 | /* |
647 | * Allocate dir_info with a guard page on either side. Also | 668 | * Allocate dir_info with a guard page on either side. Also |
@@ -649,7 +670,7 @@ omalloc_init(struct dir_info **dp) | |||
649 | * lies (subject to alignment by 1 << MALLOC_MINSHIFT) | 670 | * lies (subject to alignment by 1 << MALLOC_MINSHIFT) |
650 | */ | 671 | */ |
651 | if ((p = MMAP(DIR_INFO_RSZ + (MALLOC_PAGESIZE * 2))) == MAP_FAILED) | 672 | if ((p = MMAP(DIR_INFO_RSZ + (MALLOC_PAGESIZE * 2))) == MAP_FAILED) |
652 | return -1; | 673 | wrterror(NULL, "malloc init mmap failed", NULL); |
653 | mprotect(p, MALLOC_PAGESIZE, PROT_NONE); | 674 | mprotect(p, MALLOC_PAGESIZE, PROT_NONE); |
654 | mprotect(p + MALLOC_PAGESIZE + DIR_INFO_RSZ, | 675 | mprotect(p + MALLOC_PAGESIZE + DIR_INFO_RSZ, |
655 | MALLOC_PAGESIZE, PROT_NONE); | 676 | MALLOC_PAGESIZE, PROT_NONE); |
@@ -661,9 +682,10 @@ omalloc_init(struct dir_info **dp) | |||
661 | d->regions_free = d->regions_total = MALLOC_INITIAL_REGIONS; | 682 | d->regions_free = d->regions_total = MALLOC_INITIAL_REGIONS; |
662 | regioninfo_size = d->regions_total * sizeof(struct region_info); | 683 | regioninfo_size = d->regions_total * sizeof(struct region_info); |
663 | d->r = MMAP(regioninfo_size); | 684 | d->r = MMAP(regioninfo_size); |
664 | if (d->r == MAP_FAILED) | 685 | if (d->r == MAP_FAILED) { |
686 | d->regions_total = 0; | ||
665 | wrterror(NULL, "malloc init mmap failed", NULL); | 687 | wrterror(NULL, "malloc init mmap failed", NULL); |
666 | 688 | } | |
667 | for (i = 0; i <= MALLOC_MAXSHIFT; i++) { | 689 | for (i = 0; i <= MALLOC_MAXSHIFT; i++) { |
668 | LIST_INIT(&d->chunk_info_list[i]); | 690 | LIST_INIT(&d->chunk_info_list[i]); |
669 | for (j = 0; j < MALLOC_CHUNK_LISTS; j++) | 691 | for (j = 0; j < MALLOC_CHUNK_LISTS; j++) |
@@ -674,15 +696,6 @@ omalloc_init(struct dir_info **dp) | |||
674 | d->canary2 = ~d->canary1; | 696 | d->canary2 = ~d->canary1; |
675 | 697 | ||
676 | *dp = d; | 698 | *dp = d; |
677 | |||
678 | /* | ||
679 | * Options have been set and will never be reset. | ||
680 | * Prevent further tampering with them. | ||
681 | */ | ||
682 | if (((uintptr_t)&malloc_readonly & MALLOC_PAGEMASK) == 0) | ||
683 | mprotect(&malloc_readonly, sizeof(malloc_readonly), PROT_READ); | ||
684 | |||
685 | return 0; | ||
686 | } | 699 | } |
687 | 700 | ||
688 | static int | 701 | static int |
@@ -1172,21 +1185,46 @@ malloc_recurse(struct dir_info *d) | |||
1172 | wrterror(d, "recursive call", NULL); | 1185 | wrterror(d, "recursive call", NULL); |
1173 | } | 1186 | } |
1174 | d->active--; | 1187 | d->active--; |
1175 | _MALLOC_UNLOCK(); | 1188 | _MALLOC_UNLOCK(d->mutex); |
1176 | errno = EDEADLK; | 1189 | errno = EDEADLK; |
1177 | } | 1190 | } |
1178 | 1191 | ||
1179 | static int | 1192 | void |
1180 | malloc_init(void) | 1193 | _malloc_init(int from_rthreads) |
1181 | { | 1194 | { |
1182 | if (omalloc_init(&mopts.malloc_pool)) { | 1195 | int i, max; |
1183 | _MALLOC_UNLOCK(); | 1196 | struct dir_info *d; |
1184 | if (mopts.malloc_xmalloc) | 1197 | |
1185 | wrterror(NULL, "out of memory", NULL); | 1198 | _MALLOC_LOCK(0); |
1186 | errno = ENOMEM; | 1199 | if (!from_rthreads && mopts.malloc_pool[0]) { |
1187 | return -1; | 1200 | _MALLOC_UNLOCK(0); |
1201 | return; | ||
1188 | } | 1202 | } |
1189 | return 0; | 1203 | if (!mopts.malloc_canary) |
1204 | omalloc_init(); | ||
1205 | |||
1206 | max = from_rthreads ? _MALLOC_MUTEXES : 1; | ||
1207 | if (((uintptr_t)&malloc_readonly & MALLOC_PAGEMASK) == 0) | ||
1208 | mprotect(&malloc_readonly, sizeof(malloc_readonly), | ||
1209 | PROT_READ | PROT_WRITE); | ||
1210 | for (i = 0; i < max; i++) { | ||
1211 | if (mopts.malloc_pool[i]) | ||
1212 | continue; | ||
1213 | omalloc_poolinit(&d); | ||
1214 | d->mutex = i; | ||
1215 | mopts.malloc_pool[i] = d; | ||
1216 | } | ||
1217 | |||
1218 | if (from_rthreads) | ||
1219 | mopts.malloc_mt = 1; | ||
1220 | |||
1221 | /* | ||
1222 | * Options have been set and will never be reset. | ||
1223 | * Prevent further tampering with them. | ||
1224 | */ | ||
1225 | if (((uintptr_t)&malloc_readonly & MALLOC_PAGEMASK) == 0) | ||
1226 | mprotect(&malloc_readonly, sizeof(malloc_readonly), PROT_READ); | ||
1227 | _MALLOC_UNLOCK(0); | ||
1190 | } | 1228 | } |
1191 | 1229 | ||
1192 | void * | 1230 | void * |
@@ -1196,13 +1234,12 @@ malloc(size_t size) | |||
1196 | struct dir_info *d; | 1234 | struct dir_info *d; |
1197 | int saved_errno = errno; | 1235 | int saved_errno = errno; |
1198 | 1236 | ||
1199 | _MALLOC_LOCK(); | ||
1200 | d = getpool(); | 1237 | d = getpool(); |
1201 | if (d == NULL) { | 1238 | if (d == NULL) { |
1202 | if (malloc_init() != 0) | 1239 | _malloc_init(0); |
1203 | return NULL; | ||
1204 | d = getpool(); | 1240 | d = getpool(); |
1205 | } | 1241 | } |
1242 | _MALLOC_LOCK(d->mutex); | ||
1206 | d->func = "malloc():"; | 1243 | d->func = "malloc():"; |
1207 | 1244 | ||
1208 | if (d->active++) { | 1245 | if (d->active++) { |
@@ -1213,7 +1250,7 @@ malloc(size_t size) | |||
1213 | size += mopts.malloc_canaries; | 1250 | size += mopts.malloc_canaries; |
1214 | r = omalloc(d, size, 0, CALLER); | 1251 | r = omalloc(d, size, 0, CALLER); |
1215 | d->active--; | 1252 | d->active--; |
1216 | _MALLOC_UNLOCK(); | 1253 | _MALLOC_UNLOCK(d->mutex); |
1217 | if (r == NULL && mopts.malloc_xmalloc) | 1254 | if (r == NULL && mopts.malloc_xmalloc) |
1218 | wrterror(d, "out of memory", NULL); | 1255 | wrterror(d, "out of memory", NULL); |
1219 | if (r != NULL) | 1256 | if (r != NULL) |
@@ -1244,14 +1281,34 @@ validate_junk(struct dir_info *pool, void *p) { | |||
1244 | } | 1281 | } |
1245 | 1282 | ||
1246 | static void | 1283 | static void |
1247 | ofree(struct dir_info *pool, void *p) | 1284 | ofree(struct dir_info *argpool, void *p) |
1248 | { | 1285 | { |
1286 | struct dir_info *pool; | ||
1249 | struct region_info *r; | 1287 | struct region_info *r; |
1250 | size_t sz; | 1288 | size_t sz; |
1289 | int i; | ||
1251 | 1290 | ||
1291 | pool = argpool; | ||
1252 | r = find(pool, p); | 1292 | r = find(pool, p); |
1253 | if (r == NULL) | 1293 | if (r == NULL) { |
1254 | wrterror(pool, "bogus pointer (double free?)", p); | 1294 | if (mopts.malloc_mt) { |
1295 | for (i = 0; i < _MALLOC_MUTEXES; i++) { | ||
1296 | if (i == argpool->mutex) | ||
1297 | continue; | ||
1298 | pool->active--; | ||
1299 | _MALLOC_UNLOCK(pool->mutex); | ||
1300 | pool = mopts.malloc_pool[i]; | ||
1301 | _MALLOC_LOCK(pool->mutex); | ||
1302 | pool->active++; | ||
1303 | r = find(pool, p); | ||
1304 | if (r != NULL) | ||
1305 | break; | ||
1306 | } | ||
1307 | } | ||
1308 | if (r == NULL) | ||
1309 | wrterror(pool, "bogus pointer (double free?)", p); | ||
1310 | } | ||
1311 | |||
1255 | REALSIZE(sz, r); | 1312 | REALSIZE(sz, r); |
1256 | if (sz > MALLOC_MAXCHUNK) { | 1313 | if (sz > MALLOC_MAXCHUNK) { |
1257 | if (sz - mopts.malloc_guard >= MALLOC_PAGESIZE - | 1314 | if (sz - mopts.malloc_guard >= MALLOC_PAGESIZE - |
@@ -1294,7 +1351,7 @@ ofree(struct dir_info *pool, void *p) | |||
1294 | memset(p, SOME_FREEJUNK, sz - mopts.malloc_canaries); | 1351 | memset(p, SOME_FREEJUNK, sz - mopts.malloc_canaries); |
1295 | if (!mopts.malloc_freenow) { | 1352 | if (!mopts.malloc_freenow) { |
1296 | if (find_chunknum(pool, r, p) == -1) | 1353 | if (find_chunknum(pool, r, p) == -1) |
1297 | return; | 1354 | goto done; |
1298 | i = getrbyte(pool) & MALLOC_DELAYED_CHUNK_MASK; | 1355 | i = getrbyte(pool) & MALLOC_DELAYED_CHUNK_MASK; |
1299 | tmp = p; | 1356 | tmp = p; |
1300 | p = pool->delayed_chunks[i]; | 1357 | p = pool->delayed_chunks[i]; |
@@ -1311,6 +1368,13 @@ ofree(struct dir_info *pool, void *p) | |||
1311 | free_bytes(pool, r, p); | 1368 | free_bytes(pool, r, p); |
1312 | } | 1369 | } |
1313 | } | 1370 | } |
1371 | done: | ||
1372 | if (argpool != pool) { | ||
1373 | pool->active--; | ||
1374 | _MALLOC_UNLOCK(pool->mutex); | ||
1375 | _MALLOC_LOCK(argpool->mutex); | ||
1376 | argpool->active++; | ||
1377 | } | ||
1314 | } | 1378 | } |
1315 | 1379 | ||
1316 | void | 1380 | void |
@@ -1323,12 +1387,10 @@ free(void *ptr) | |||
1323 | if (ptr == NULL) | 1387 | if (ptr == NULL) |
1324 | return; | 1388 | return; |
1325 | 1389 | ||
1326 | _MALLOC_LOCK(); | ||
1327 | d = getpool(); | 1390 | d = getpool(); |
1328 | if (d == NULL) { | 1391 | if (d == NULL) |
1329 | _MALLOC_UNLOCK(); | ||
1330 | wrterror(d, "free() called before allocation", NULL); | 1392 | wrterror(d, "free() called before allocation", NULL); |
1331 | } | 1393 | _MALLOC_LOCK(d->mutex); |
1332 | d->func = "free():"; | 1394 | d->func = "free():"; |
1333 | if (d->active++) { | 1395 | if (d->active++) { |
1334 | malloc_recurse(d); | 1396 | malloc_recurse(d); |
@@ -1336,28 +1398,49 @@ free(void *ptr) | |||
1336 | } | 1398 | } |
1337 | ofree(d, ptr); | 1399 | ofree(d, ptr); |
1338 | d->active--; | 1400 | d->active--; |
1339 | _MALLOC_UNLOCK(); | 1401 | _MALLOC_UNLOCK(d->mutex); |
1340 | errno = saved_errno; | 1402 | errno = saved_errno; |
1341 | } | 1403 | } |
1342 | /*DEF_STRONG(free);*/ | 1404 | /*DEF_STRONG(free);*/ |
1343 | 1405 | ||
1344 | 1406 | ||
1345 | static void * | 1407 | static void * |
1346 | orealloc(struct dir_info *pool, void *p, size_t newsz, void *f) | 1408 | orealloc(struct dir_info *argpool, void *p, size_t newsz, void *f) |
1347 | { | 1409 | { |
1410 | struct dir_info *pool; | ||
1348 | struct region_info *r; | 1411 | struct region_info *r; |
1349 | size_t oldsz, goldsz, gnewsz; | 1412 | size_t oldsz, goldsz, gnewsz; |
1350 | void *q; | 1413 | void *q, *ret; |
1414 | int i; | ||
1415 | |||
1416 | pool = argpool; | ||
1351 | 1417 | ||
1352 | if (p == NULL) | 1418 | if (p == NULL) |
1353 | return omalloc(pool, newsz, 0, f); | 1419 | return omalloc(pool, newsz, 0, f); |
1354 | 1420 | ||
1355 | r = find(pool, p); | 1421 | r = find(pool, p); |
1356 | if (r == NULL) | 1422 | if (r == NULL) { |
1357 | wrterror(pool, "bogus pointer (double free?)", p); | 1423 | if (mopts.malloc_mt) { |
1424 | for (i = 0; i < _MALLOC_MUTEXES; i++) { | ||
1425 | if (i == argpool->mutex) | ||
1426 | continue; | ||
1427 | pool->active--; | ||
1428 | _MALLOC_UNLOCK(pool->mutex); | ||
1429 | pool = mopts.malloc_pool[i]; | ||
1430 | _MALLOC_LOCK(pool->mutex); | ||
1431 | pool->active++; | ||
1432 | r = find(pool, p); | ||
1433 | if (r != NULL) | ||
1434 | break; | ||
1435 | } | ||
1436 | } | ||
1437 | if (r == NULL) | ||
1438 | wrterror(pool, "bogus pointer (double free?)", p); | ||
1439 | } | ||
1358 | if (newsz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { | 1440 | if (newsz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { |
1359 | errno = ENOMEM; | 1441 | errno = ENOMEM; |
1360 | return NULL; | 1442 | ret = NULL; |
1443 | goto done; | ||
1361 | } | 1444 | } |
1362 | 1445 | ||
1363 | REALSIZE(oldsz, r); | 1446 | REALSIZE(oldsz, r); |
@@ -1400,7 +1483,8 @@ gotit: | |||
1400 | r->size = newsz; | 1483 | r->size = newsz; |
1401 | STATS_SETF(r, f); | 1484 | STATS_SETF(r, f); |
1402 | STATS_INC(pool->cheap_reallocs); | 1485 | STATS_INC(pool->cheap_reallocs); |
1403 | return p; | 1486 | ret = p; |
1487 | goto done; | ||
1404 | } else if (q != MAP_FAILED) { | 1488 | } else if (q != MAP_FAILED) { |
1405 | if (munmap(q, needed)) | 1489 | if (munmap(q, needed)) |
1406 | wrterror(pool, "munmap", q); | 1490 | wrterror(pool, "munmap", q); |
@@ -1420,14 +1504,16 @@ gotit: | |||
1420 | unmap(pool, (char *)p + rnewsz, roldsz - rnewsz); | 1504 | unmap(pool, (char *)p + rnewsz, roldsz - rnewsz); |
1421 | r->size = gnewsz; | 1505 | r->size = gnewsz; |
1422 | STATS_SETF(r, f); | 1506 | STATS_SETF(r, f); |
1423 | return p; | 1507 | ret = p; |
1508 | goto done; | ||
1424 | } else { | 1509 | } else { |
1425 | if (newsz > oldsz && mopts.malloc_junk == 2) | 1510 | if (newsz > oldsz && mopts.malloc_junk == 2) |
1426 | memset((char *)p + newsz, SOME_JUNK, | 1511 | memset((char *)p + newsz, SOME_JUNK, |
1427 | rnewsz - mopts.malloc_guard - newsz); | 1512 | rnewsz - mopts.malloc_guard - newsz); |
1428 | r->size = gnewsz; | 1513 | r->size = gnewsz; |
1429 | STATS_SETF(r, f); | 1514 | STATS_SETF(r, f); |
1430 | return p; | 1515 | ret = p; |
1516 | goto done; | ||
1431 | } | 1517 | } |
1432 | } | 1518 | } |
1433 | if (newsz <= oldsz && newsz > oldsz / 2 && !mopts.malloc_realloc) { | 1519 | if (newsz <= oldsz && newsz > oldsz / 2 && !mopts.malloc_realloc) { |
@@ -1439,11 +1525,13 @@ gotit: | |||
1439 | memset((char *)p + newsz, SOME_JUNK, usable_oldsz - newsz); | 1525 | memset((char *)p + newsz, SOME_JUNK, usable_oldsz - newsz); |
1440 | } | 1526 | } |
1441 | STATS_SETF(r, f); | 1527 | STATS_SETF(r, f); |
1442 | return p; | 1528 | ret = p; |
1443 | } else if (newsz != oldsz || mopts.malloc_realloc) { | 1529 | } else if (newsz != oldsz || mopts.malloc_realloc) { |
1444 | q = omalloc(pool, newsz, 0, f); | 1530 | q = omalloc(pool, newsz, 0, f); |
1445 | if (q == NULL) | 1531 | if (q == NULL) { |
1446 | return NULL; | 1532 | ret = NULL; |
1533 | goto done; | ||
1534 | } | ||
1447 | if (newsz != 0 && oldsz != 0) { | 1535 | if (newsz != 0 && oldsz != 0) { |
1448 | size_t copysz = oldsz < newsz ? oldsz : newsz; | 1536 | size_t copysz = oldsz < newsz ? oldsz : newsz; |
1449 | if (copysz <= MALLOC_MAXCHUNK) | 1537 | if (copysz <= MALLOC_MAXCHUNK) |
@@ -1451,11 +1539,19 @@ gotit: | |||
1451 | memcpy(q, p, copysz); | 1539 | memcpy(q, p, copysz); |
1452 | } | 1540 | } |
1453 | ofree(pool, p); | 1541 | ofree(pool, p); |
1454 | return q; | 1542 | ret = q; |
1455 | } else { | 1543 | } else { |
1456 | STATS_SETF(r, f); | 1544 | STATS_SETF(r, f); |
1457 | return p; | 1545 | ret = p; |
1546 | } | ||
1547 | done: | ||
1548 | if (argpool != pool) { | ||
1549 | pool->active--; | ||
1550 | _MALLOC_UNLOCK(pool->mutex); | ||
1551 | _MALLOC_LOCK(argpool->mutex); | ||
1552 | argpool->active++; | ||
1458 | } | 1553 | } |
1554 | return ret; | ||
1459 | } | 1555 | } |
1460 | 1556 | ||
1461 | void * | 1557 | void * |
@@ -1465,13 +1561,12 @@ realloc(void *ptr, size_t size) | |||
1465 | void *r; | 1561 | void *r; |
1466 | int saved_errno = errno; | 1562 | int saved_errno = errno; |
1467 | 1563 | ||
1468 | _MALLOC_LOCK(); | ||
1469 | d = getpool(); | 1564 | d = getpool(); |
1470 | if (d == NULL) { | 1565 | if (d == NULL) { |
1471 | if (malloc_init() != 0) | 1566 | _malloc_init(0); |
1472 | return NULL; | ||
1473 | d = getpool(); | 1567 | d = getpool(); |
1474 | } | 1568 | } |
1569 | _MALLOC_LOCK(d->mutex); | ||
1475 | d->func = "realloc():"; | 1570 | d->func = "realloc():"; |
1476 | if (d->active++) { | 1571 | if (d->active++) { |
1477 | malloc_recurse(d); | 1572 | malloc_recurse(d); |
@@ -1482,7 +1577,7 @@ realloc(void *ptr, size_t size) | |||
1482 | r = orealloc(d, ptr, size, CALLER); | 1577 | r = orealloc(d, ptr, size, CALLER); |
1483 | 1578 | ||
1484 | d->active--; | 1579 | d->active--; |
1485 | _MALLOC_UNLOCK(); | 1580 | _MALLOC_UNLOCK(d->mutex); |
1486 | if (r == NULL && mopts.malloc_xmalloc) | 1581 | if (r == NULL && mopts.malloc_xmalloc) |
1487 | wrterror(d, "out of memory", NULL); | 1582 | wrterror(d, "out of memory", NULL); |
1488 | if (r != NULL) | 1583 | if (r != NULL) |
@@ -1505,17 +1600,16 @@ calloc(size_t nmemb, size_t size) | |||
1505 | void *r; | 1600 | void *r; |
1506 | int saved_errno = errno; | 1601 | int saved_errno = errno; |
1507 | 1602 | ||
1508 | _MALLOC_LOCK(); | ||
1509 | d = getpool(); | 1603 | d = getpool(); |
1510 | if (d == NULL) { | 1604 | if (d == NULL) { |
1511 | if (malloc_init() != 0) | 1605 | _malloc_init(0); |
1512 | return NULL; | ||
1513 | d = getpool(); | 1606 | d = getpool(); |
1514 | } | 1607 | } |
1608 | _MALLOC_LOCK(d->mutex); | ||
1515 | d->func = "calloc():"; | 1609 | d->func = "calloc():"; |
1516 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | 1610 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && |
1517 | nmemb > 0 && SIZE_MAX / nmemb < size) { | 1611 | nmemb > 0 && SIZE_MAX / nmemb < size) { |
1518 | _MALLOC_UNLOCK(); | 1612 | _MALLOC_UNLOCK(d->mutex); |
1519 | if (mopts.malloc_xmalloc) | 1613 | if (mopts.malloc_xmalloc) |
1520 | wrterror(d, "out of memory", NULL); | 1614 | wrterror(d, "out of memory", NULL); |
1521 | errno = ENOMEM; | 1615 | errno = ENOMEM; |
@@ -1533,7 +1627,7 @@ calloc(size_t nmemb, size_t size) | |||
1533 | r = omalloc(d, size, 1, CALLER); | 1627 | r = omalloc(d, size, 1, CALLER); |
1534 | 1628 | ||
1535 | d->active--; | 1629 | d->active--; |
1536 | _MALLOC_UNLOCK(); | 1630 | _MALLOC_UNLOCK(d->mutex); |
1537 | if (r == NULL && mopts.malloc_xmalloc) | 1631 | if (r == NULL && mopts.malloc_xmalloc) |
1538 | wrterror(d, "out of memory", NULL); | 1632 | wrterror(d, "out of memory", NULL); |
1539 | if (r != NULL) | 1633 | if (r != NULL) |
@@ -1641,13 +1735,12 @@ posix_memalign(void **memptr, size_t alignment, size_t size) | |||
1641 | if (((alignment - 1) & alignment) != 0 || alignment < sizeof(void *)) | 1735 | if (((alignment - 1) & alignment) != 0 || alignment < sizeof(void *)) |
1642 | return EINVAL; | 1736 | return EINVAL; |
1643 | 1737 | ||
1644 | _MALLOC_LOCK(); | ||
1645 | d = getpool(); | 1738 | d = getpool(); |
1646 | if (d == NULL) { | 1739 | if (d == NULL) { |
1647 | if (malloc_init() != 0) | 1740 | _malloc_init(0); |
1648 | goto err; | ||
1649 | d = getpool(); | 1741 | d = getpool(); |
1650 | } | 1742 | } |
1743 | _MALLOC_LOCK(d->mutex); | ||
1651 | d->func = "posix_memalign():"; | 1744 | d->func = "posix_memalign():"; |
1652 | if (d->active++) { | 1745 | if (d->active++) { |
1653 | malloc_recurse(d); | 1746 | malloc_recurse(d); |
@@ -1657,7 +1750,7 @@ posix_memalign(void **memptr, size_t alignment, size_t size) | |||
1657 | size += mopts.malloc_canaries; | 1750 | size += mopts.malloc_canaries; |
1658 | r = omemalign(d, alignment, size, 0, CALLER); | 1751 | r = omemalign(d, alignment, size, 0, CALLER); |
1659 | d->active--; | 1752 | d->active--; |
1660 | _MALLOC_UNLOCK(); | 1753 | _MALLOC_UNLOCK(d->mutex); |
1661 | if (r == NULL) { | 1754 | if (r == NULL) { |
1662 | if (mopts.malloc_xmalloc) | 1755 | if (mopts.malloc_xmalloc) |
1663 | wrterror(d, "out of memory", NULL); | 1756 | wrterror(d, "out of memory", NULL); |
@@ -1703,7 +1796,7 @@ putleakinfo(void *f, size_t sz, int cnt) | |||
1703 | static struct leaknode *page; | 1796 | static struct leaknode *page; |
1704 | static int used; | 1797 | static int used; |
1705 | 1798 | ||
1706 | if (cnt == 0) | 1799 | if (cnt == 0 || page == MAP_FAILED) |
1707 | return; | 1800 | return; |
1708 | 1801 | ||
1709 | key.d.f = f; | 1802 | key.d.f = f; |
@@ -1894,9 +1987,8 @@ malloc_dump1(int fd, struct dir_info *d) | |||
1894 | } | 1987 | } |
1895 | 1988 | ||
1896 | void | 1989 | void |
1897 | malloc_dump(int fd) | 1990 | malloc_dump(int fd, struct dir_info *pool) |
1898 | { | 1991 | { |
1899 | struct dir_info *pool = getpool(); | ||
1900 | int i; | 1992 | int i; |
1901 | void *p; | 1993 | void *p; |
1902 | struct region_info *r; | 1994 | struct region_info *r; |
@@ -1925,11 +2017,12 @@ static void | |||
1925 | malloc_exit(void) | 2017 | malloc_exit(void) |
1926 | { | 2018 | { |
1927 | static const char q[] = "malloc() warning: Couldn't dump stats\n"; | 2019 | static const char q[] = "malloc() warning: Couldn't dump stats\n"; |
1928 | int save_errno = errno, fd; | 2020 | int save_errno = errno, fd, i; |
1929 | 2021 | ||
1930 | fd = open("malloc.out", O_RDWR|O_APPEND); | 2022 | fd = open("malloc.out", O_RDWR|O_APPEND); |
1931 | if (fd != -1) { | 2023 | if (fd != -1) { |
1932 | malloc_dump(fd); | 2024 | for (i = 0; i < _MALLOC_MUTEXES; i++) |
2025 | malloc_dump(fd, mopts.malloc_pool[i]); | ||
1933 | close(fd); | 2026 | close(fd); |
1934 | } else | 2027 | } else |
1935 | write(STDERR_FILENO, q, sizeof(q) - 1); | 2028 | write(STDERR_FILENO, q, sizeof(q) - 1); |