diff options
author | otto <> | 2009-11-27 20:11:01 +0000 |
---|---|---|
committer | otto <> | 2009-11-27 20:11:01 +0000 |
commit | 3d3d1405a7100bc32d8e0a7ca73cb68cf4219ca7 (patch) | |
tree | 4867d3c5308815e824306c492a7c70f75b5ae4d7 /src | |
parent | 40a957c6c401170c2ef2c75c1a3353742dad7322 (diff) | |
download | openbsd-3d3d1405a7100bc32d8e0a7ca73cb68cf4219ca7.tar.gz openbsd-3d3d1405a7100bc32d8e0a7ca73cb68cf4219ca7.tar.bz2 openbsd-3d3d1405a7100bc32d8e0a7ca73cb68cf4219ca7.zip |
Switch the chunk_info lists to doubly-linked lists and use the queue
macros for them. Avoids walking the lists and greatly enhances speed
of freeing chunks in reverse or random order at the cost of a little
space. Suggested by Fabien Romano and Jonathan Armani; ok djm@
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libc/stdlib/malloc.c | 85 |
1 files changed, 34 insertions, 51 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index f3f0a97c3a..b364a0c28b 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: malloc.c,v 1.120 2009/11/27 20:05:03 otto Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.121 2009/11/27 20:11:01 otto Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> | 3 | * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> |
4 | * | 4 | * |
@@ -32,6 +32,7 @@ | |||
32 | 32 | ||
33 | #include <sys/types.h> | 33 | #include <sys/types.h> |
34 | #include <sys/param.h> | 34 | #include <sys/param.h> |
35 | #include <sys/queue.h> | ||
35 | #include <sys/mman.h> | 36 | #include <sys/mman.h> |
36 | #include <sys/uio.h> | 37 | #include <sys/uio.h> |
37 | #include <errno.h> | 38 | #include <errno.h> |
@@ -93,6 +94,8 @@ struct region_info { | |||
93 | uintptr_t size; /* size for pages, or chunk_info pointer */ | 94 | uintptr_t size; /* size for pages, or chunk_info pointer */ |
94 | }; | 95 | }; |
95 | 96 | ||
97 | LIST_HEAD(chunk_head, chunk_info); | ||
98 | |||
96 | struct dir_info { | 99 | struct dir_info { |
97 | u_int32_t canary1; | 100 | u_int32_t canary1; |
98 | struct region_info *r; /* region slots */ | 101 | struct region_info *r; /* region slots */ |
@@ -100,9 +103,9 @@ struct dir_info { | |||
100 | size_t regions_bits; /* log2 of total */ | 103 | size_t regions_bits; /* log2 of total */ |
101 | size_t regions_free; /* number of free slots */ | 104 | size_t regions_free; /* number of free slots */ |
102 | /* list of free chunk info structs */ | 105 | /* list of free chunk info structs */ |
103 | struct chunk_info *chunk_info_list; | 106 | struct chunk_head chunk_info_list; |
104 | /* lists of chunks with free slots */ | 107 | /* lists of chunks with free slots */ |
105 | struct chunk_info *chunk_dir[MALLOC_MAXSHIFT]; | 108 | struct chunk_head chunk_dir[MALLOC_MAXSHIFT]; |
106 | size_t free_regions_size; /* free pages cached */ | 109 | size_t free_regions_size; /* free pages cached */ |
107 | /* free pages cache */ | 110 | /* free pages cache */ |
108 | struct region_info free_regions[MALLOC_MAXCACHE]; | 111 | struct region_info free_regions[MALLOC_MAXCACHE]; |
@@ -135,7 +138,7 @@ struct dir_info { | |||
135 | */ | 138 | */ |
136 | #define MALLOC_BITS (NBBY * sizeof(u_long)) | 139 | #define MALLOC_BITS (NBBY * sizeof(u_long)) |
137 | struct chunk_info { | 140 | struct chunk_info { |
138 | struct chunk_info *next; /* next on the free list */ | 141 | LIST_ENTRY(chunk_info) entries; |
139 | void *page; /* pointer to the page */ | 142 | void *page; /* pointer to the page */ |
140 | u_int32_t canary; | 143 | u_int32_t canary; |
141 | u_short size; /* size of this page's chunks */ | 144 | u_short size; /* size of this page's chunks */ |
@@ -217,13 +220,13 @@ dump_chunk(int fd, struct chunk_info *p, int fromfreelist) | |||
217 | { | 220 | { |
218 | char buf[64]; | 221 | char buf[64]; |
219 | 222 | ||
220 | while (p) { | 223 | while (p != NULL) { |
221 | snprintf(buf, sizeof(buf), "chunk %d %d/%d %p\n", p->size, | 224 | snprintf(buf, sizeof(buf), "chunk %d %d/%d %p\n", p->size, |
222 | p->free, p->total, p->page); | 225 | p->free, p->total, p->page); |
223 | write(fd, buf, strlen(buf)); | 226 | write(fd, buf, strlen(buf)); |
224 | if (!fromfreelist) | 227 | if (!fromfreelist) |
225 | break; | 228 | break; |
226 | p = p->next; | 229 | p = LIST_NEXT(p, entries); |
227 | if (p != NULL) { | 230 | if (p != NULL) { |
228 | snprintf(buf, sizeof(buf), " "); | 231 | snprintf(buf, sizeof(buf), " "); |
229 | write(fd, buf, strlen(buf)); | 232 | write(fd, buf, strlen(buf)); |
@@ -240,7 +243,7 @@ dump_free_chunk_info(int fd, struct dir_info *d) | |||
240 | snprintf(buf, sizeof(buf), "Free chunk structs:\n"); | 243 | snprintf(buf, sizeof(buf), "Free chunk structs:\n"); |
241 | write(fd, buf, strlen(buf)); | 244 | write(fd, buf, strlen(buf)); |
242 | for (i = 0; i < MALLOC_MAXSHIFT; i++) { | 245 | for (i = 0; i < MALLOC_MAXSHIFT; i++) { |
243 | struct chunk_info *p = d->chunk_dir[i]; | 246 | struct chunk_info *p = LIST_FIRST(&d->chunk_dir[i]); |
244 | if (p != NULL) { | 247 | if (p != NULL) { |
245 | snprintf(buf, sizeof(buf), "%2d) ", i); | 248 | snprintf(buf, sizeof(buf), "%2d) ", i); |
246 | write(fd, buf, strlen(buf)); | 249 | write(fd, buf, strlen(buf)); |
@@ -716,6 +719,9 @@ omalloc_init(struct dir_info **dp) | |||
716 | d->regions_total = 0; | 719 | d->regions_total = 0; |
717 | return 1; | 720 | return 1; |
718 | } | 721 | } |
722 | LIST_INIT(&d->chunk_info_list); | ||
723 | for (i = 0; i < MALLOC_MAXSHIFT; i++) | ||
724 | LIST_INIT(&d->chunk_dir[i]); | ||
719 | malloc_used += regioninfo_size; | 725 | malloc_used += regioninfo_size; |
720 | d->canary1 = mopts.malloc_canary ^ (u_int32_t)(uintptr_t)d; | 726 | d->canary1 = mopts.malloc_canary ^ (u_int32_t)(uintptr_t)d; |
721 | d->canary2 = ~d->canary1; | 727 | d->canary2 = ~d->canary1; |
@@ -788,31 +794,21 @@ alloc_chunk_info(struct dir_info *d) | |||
788 | struct chunk_info *p; | 794 | struct chunk_info *p; |
789 | int i; | 795 | int i; |
790 | 796 | ||
791 | if (d->chunk_info_list == NULL) { | 797 | if (LIST_EMPTY(&d->chunk_info_list)) { |
792 | p = MMAP(MALLOC_PAGESIZE); | 798 | p = MMAP(MALLOC_PAGESIZE); |
793 | if (p == MAP_FAILED) | 799 | if (p == MAP_FAILED) |
794 | return NULL; | 800 | return NULL; |
795 | malloc_used += MALLOC_PAGESIZE; | 801 | malloc_used += MALLOC_PAGESIZE; |
796 | for (i = 0; i < MALLOC_PAGESIZE / sizeof(*p); i++) { | 802 | for (i = 0; i < MALLOC_PAGESIZE / sizeof(*p); i++) |
797 | p[i].next = d->chunk_info_list; | 803 | LIST_INSERT_HEAD(&d->chunk_info_list, &p[i], entries); |
798 | d->chunk_info_list = &p[i]; | ||
799 | } | ||
800 | } | 804 | } |
801 | p = d->chunk_info_list; | 805 | p = LIST_FIRST(&d->chunk_info_list); |
802 | d->chunk_info_list = p->next; | 806 | LIST_REMOVE(p, entries); |
803 | memset(p, 0, sizeof *p); | 807 | memset(p, 0, sizeof *p); |
804 | p->canary = d->canary1; | 808 | p->canary = d->canary1; |
805 | return p; | 809 | return p; |
806 | } | 810 | } |
807 | 811 | ||
808 | |||
809 | static void | ||
810 | put_chunk_info(struct dir_info *d, struct chunk_info *p) | ||
811 | { | ||
812 | p->next = d->chunk_info_list; | ||
813 | d->chunk_info_list = p; | ||
814 | } | ||
815 | |||
816 | static int | 812 | static int |
817 | insert(struct dir_info *d, void *p, size_t sz) | 813 | insert(struct dir_info *d, void *p, size_t sz) |
818 | { | 814 | { |
@@ -930,7 +926,7 @@ omalloc_make_chunks(struct dir_info *d, int bits) | |||
930 | k = mprotect(pp, MALLOC_PAGESIZE, PROT_NONE); | 926 | k = mprotect(pp, MALLOC_PAGESIZE, PROT_NONE); |
931 | if (k < 0) { | 927 | if (k < 0) { |
932 | unmap(d, pp, MALLOC_PAGESIZE); | 928 | unmap(d, pp, MALLOC_PAGESIZE); |
933 | put_chunk_info(d, bp); | 929 | LIST_INSERT_HEAD(&d->chunk_info_list, bp, entries); |
934 | return NULL; | 930 | return NULL; |
935 | } | 931 | } |
936 | } else { | 932 | } else { |
@@ -951,8 +947,7 @@ omalloc_make_chunks(struct dir_info *d, int bits) | |||
951 | for (; i < k; i++) | 947 | for (; i < k; i++) |
952 | bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); | 948 | bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); |
953 | 949 | ||
954 | bp->next = d->chunk_dir[bits]; | 950 | LIST_INSERT_HEAD(&d->chunk_dir[bits], bp, entries); |
955 | d->chunk_dir[bits] = bp; | ||
956 | 951 | ||
957 | bits++; | 952 | bits++; |
958 | if ((uintptr_t)pp & bits) | 953 | if ((uintptr_t)pp & bits) |
@@ -993,9 +988,12 @@ malloc_bytes(struct dir_info *d, size_t size) | |||
993 | } | 988 | } |
994 | 989 | ||
995 | /* If it's empty, make a page more of that size chunks */ | 990 | /* If it's empty, make a page more of that size chunks */ |
996 | bp = d->chunk_dir[j]; | 991 | if (LIST_EMPTY(&d->chunk_dir[j])) { |
997 | if (bp == NULL && (bp = omalloc_make_chunks(d, j)) == NULL) | 992 | bp = omalloc_make_chunks(d, j); |
998 | return NULL; | 993 | if (bp == NULL) |
994 | return NULL; | ||
995 | } else | ||
996 | bp = LIST_FIRST(&d->chunk_dir[j]); | ||
999 | 997 | ||
1000 | if (bp->canary != d->canary1) | 998 | if (bp->canary != d->canary1) |
1001 | wrterror("chunk info corrupted"); | 999 | wrterror("chunk info corrupted"); |
@@ -1033,10 +1031,9 @@ malloc_bytes(struct dir_info *d, size_t size) | |||
1033 | *lp ^= u; | 1031 | *lp ^= u; |
1034 | 1032 | ||
1035 | /* If there are no more free, remove from free-list */ | 1033 | /* If there are no more free, remove from free-list */ |
1036 | if (!--bp->free) { | 1034 | if (!--bp->free) |
1037 | d->chunk_dir[j] = bp->next; | 1035 | LIST_REMOVE(bp, entries); |
1038 | bp->next = NULL; | 1036 | |
1039 | } | ||
1040 | /* Adjust to the real offset of that chunk */ | 1037 | /* Adjust to the real offset of that chunk */ |
1041 | k += (lp - bp->bits) * MALLOC_BITS; | 1038 | k += (lp - bp->bits) * MALLOC_BITS; |
1042 | k <<= bp->shift; | 1039 | k <<= bp->shift; |
@@ -1053,7 +1050,8 @@ malloc_bytes(struct dir_info *d, size_t size) | |||
1053 | static void | 1050 | static void |
1054 | free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | 1051 | free_bytes(struct dir_info *d, struct region_info *r, void *ptr) |
1055 | { | 1052 | { |
1056 | struct chunk_info *info, **mp; | 1053 | struct chunk_head *mp; |
1054 | struct chunk_info *info; | ||
1057 | long i; | 1055 | long i; |
1058 | 1056 | ||
1059 | info = (struct chunk_info *)r->size; | 1057 | info = (struct chunk_info *)r->size; |
@@ -1082,35 +1080,20 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | |||
1082 | 1080 | ||
1083 | if (info->free == 1) { | 1081 | if (info->free == 1) { |
1084 | /* Page became non-full */ | 1082 | /* Page became non-full */ |
1085 | 1083 | LIST_INSERT_HEAD(mp, info, entries); | |
1086 | /* Insert in address order */ | ||
1087 | while (*mp != NULL && (*mp)->next != NULL && | ||
1088 | (*mp)->next->page < info->page) | ||
1089 | mp = &(*mp)->next; | ||
1090 | info->next = *mp; | ||
1091 | *mp = info; | ||
1092 | return; | 1084 | return; |
1093 | } | 1085 | } |
1094 | if (info->free != info->total) | 1086 | if (info->free != info->total) |
1095 | return; | 1087 | return; |
1096 | 1088 | ||
1097 | /* Find & remove this page in the queue */ | 1089 | LIST_REMOVE(info, entries); |
1098 | while (*mp != info) { | ||
1099 | mp = &((*mp)->next); | ||
1100 | if (!*mp) { | ||
1101 | wrterror("not on queue"); | ||
1102 | errno = EFAULT; | ||
1103 | return; | ||
1104 | } | ||
1105 | } | ||
1106 | *mp = info->next; | ||
1107 | 1090 | ||
1108 | if (info->size == 0 && !mopts.malloc_freeprot) | 1091 | if (info->size == 0 && !mopts.malloc_freeprot) |
1109 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); | 1092 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); |
1110 | unmap(d, info->page, MALLOC_PAGESIZE); | 1093 | unmap(d, info->page, MALLOC_PAGESIZE); |
1111 | 1094 | ||
1112 | delete(d, r); | 1095 | delete(d, r); |
1113 | put_chunk_info(d, info); | 1096 | LIST_INSERT_HEAD(&d->chunk_info_list, info, entries); |
1114 | } | 1097 | } |
1115 | 1098 | ||
1116 | 1099 | ||