root/trunk/vmem_sbrk.c

Revision 59, 7.8 kB (checked in by wez, 4 years ago)

Sync with the current sources from onnv-gate.

The solaris sources use a non-portable create-thread-suspended flag when
spawning the update thread; I've thrown together a pthreads portable
equivalent. This has not had any real level of testing.

These changes include a lock around the underlying brk() call; the lack of lock
in earlier revisions of this repo may be the reason that
UMEM_OPTIONS=backend=sbrk was flaky.

  • Property svn:eol-style set to native
Line 
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * Portions Copyright 2006-2008 Message Systems, Inc.
27  */
28
29 /* #pragma ident        "@(#)vmem_sbrk.c        1.4     05/06/08 SMI" */
30
31 /*
32  * The structure of the sbrk backend:
33  *
34  * +-----------+
35  * | sbrk_top  |
36  * +-----------+
37  *      | (vmem_sbrk_alloc(), vmem_free())
38  *      |
39  * +-----------+
40  * | sbrk_heap |
41  * +-----------+
42  *   | | ... |  (vmem_alloc(), vmem_free())
43  * <other arenas>
44  *
45  * The sbrk_top arena holds all controlled memory.  vmem_sbrk_alloc() handles
46  * allocations from it, including growing the heap when we run low.
47  *
48  * Growing the heap is complicated by the fact that we have to extend the
49  * sbrk_top arena (using _vmem_extend_alloc()), and that can fail.  Since
50  * other threads may be actively allocating, we can't return the memory.
51  *
52  * Instead, we put it on a doubly-linked list, sbrk_fails, which we search
53  * before calling sbrk().
54  */
55
56 #include "config.h"
57 /* #include "mtlib.h" */
58 #include <errno.h>
59 #include <limits.h>
60 #ifdef HAVE_SYS_SYSMACROS_H
61 #include <sys/sysmacros.h>
62 #endif
63 #include <sys/mman.h>
64 #include <unistd.h>
65
66 #include "vmem_base.h"
67
68 #include "misc.h"
69
70 size_t vmem_sbrk_pagesize = 0; /* the preferred page size of the heap */
71
72 #define VMEM_SBRK_MINALLOC      (64 * 1024)
73 size_t vmem_sbrk_minalloc = VMEM_SBRK_MINALLOC; /* minimum allocation */
74
75 static size_t real_pagesize;
76 static vmem_t *sbrk_heap;
77
78 typedef struct sbrk_fail {
79         struct sbrk_fail *sf_next;
80         struct sbrk_fail *sf_prev;
81         void *sf_base;                  /* == the sbrk_fail's address */
82         size_t sf_size;                 /* the size of this buffer */
83 } sbrk_fail_t;
84
85 static sbrk_fail_t sbrk_fails = {
86         &sbrk_fails,
87         &sbrk_fails,
88         NULL,
89         0
90 };
91
92 static mutex_t sbrk_faillock = DEFAULTMUTEX;
93 static mutex_t sbrk_lock = DEFAULTMUTEX;
94
95  /*
96  * _sbrk_grow_aligned() aligns the old break to a low_align boundry,
97  * adds min_size, aligns to a high_align boundry, and calls _brk_unlocked()
98  * to set the new break.  The low_aligned-aligned value is returned, and
99  * the actual space allocated is returned through actual_size.
100  *
101  * Unlike sbrk(2), _sbrk_grow_aligned takes an unsigned size, and does
102  * not allow shrinking the heap.
103  */
104 static void *
105 _sbrk_grow_aligned(size_t min_size, size_t low_align, size_t high_align,
106     size_t *actual_size)
107 {
108         uintptr_t old_brk;
109         uintptr_t ret_brk;
110         uintptr_t high_brk;
111         uintptr_t new_brk;
112         int brk_result;
113
114 #define ALIGNSZ   16
115 #define BRKALIGN(x) (caddr_t)P2ROUNDUP((uintptr_t)(x), ALIGNSZ)
116
117         if ((low_align & (low_align - 1)) != 0 ||
118                         (high_align & (high_align - 1)) != 0) {
119                 errno = EINVAL;
120                 return ((void *)-1);
121         }
122         low_align = MAX(low_align, ALIGNSZ);
123         high_align = MAX(high_align, ALIGNSZ);
124
125         mutex_lock(&sbrk_lock);
126
127         old_brk = (uintptr_t)BRKALIGN(sbrk(0));
128         ret_brk = P2ROUNDUP(old_brk, low_align);
129         high_brk = ret_brk + min_size;
130         new_brk = P2ROUNDUP(high_brk, high_align);
131
132         /*
133          * Check for overflow
134          */
135         if (ret_brk < old_brk || high_brk < ret_brk || new_brk < high_brk) {
136                 mutex_unlock(&sbrk_lock);
137                 errno = ENOMEM;
138                 return ((void *)-1);
139         }
140
141         brk_result = brk((void *)new_brk);
142         mutex_unlock(&sbrk_lock);
143
144         if (brk_result != 0)
145                 return ((void *)-1);
146
147         if (actual_size != NULL)
148                 *actual_size = (new_brk - ret_brk);
149         return ((void *)ret_brk);
150 }
151
152 /*
153  * Try to extend src with [pos, pos + size).
154  *
155  * If it fails, add the block to the sbrk_fails list.
156  */
157 static void *
158 vmem_sbrk_extend_alloc(vmem_t *src, void *pos, size_t size, size_t alloc,
159     int vmflags)
160 {
161         sbrk_fail_t *fnext, *fprev, *fp;
162         void *ret;
163
164         ret = _vmem_extend_alloc(src, pos, size, alloc, vmflags);
165         if (ret != NULL)
166                 return (ret);
167
168         fp = (sbrk_fail_t *)pos;
169
170         ASSERT(sizeof (sbrk_fail_t) <= size);
171
172         fp->sf_base = pos;
173         fp->sf_size = size;
174
175         (void) mutex_lock(&sbrk_faillock);
176         fp->sf_next = fnext = &sbrk_fails;
177         fp->sf_prev = fprev = sbrk_fails.sf_prev;
178         fnext->sf_prev = fp;
179         fprev->sf_next = fp;
180         (void) mutex_unlock(&sbrk_faillock);
181
182         return (NULL);
183 }
184
185 /*
186  * Try to add at least size bytes to src, using the sbrk_fails list
187  */
188 static void *
189 vmem_sbrk_tryfail(vmem_t *src, size_t size, int vmflags)
190 {
191         sbrk_fail_t *fp;
192
193         (void) mutex_lock(&sbrk_faillock);
194         for (fp = sbrk_fails.sf_next; fp != &sbrk_fails; fp = fp->sf_next) {
195                 if (fp->sf_size >= size) {
196                         fp->sf_next->sf_prev = fp->sf_prev;
197                         fp->sf_prev->sf_next = fp->sf_next;
198                         fp->sf_next = fp->sf_prev = NULL;
199                         break;
200                 }
201         }
202         (void) mutex_unlock(&sbrk_faillock);
203
204         if (fp != &sbrk_fails) {
205                 ASSERT(fp->sf_base == (void *)fp);
206                 return (vmem_sbrk_extend_alloc(src, fp, fp->sf_size, size,
207                     vmflags));
208         }
209         /*
210          * nothing of the right size on the freelist
211          */
212         return (NULL);
213 }
214
215 static void *
216 vmem_sbrk_alloc(vmem_t *src, size_t size, int vmflags)
217 {
218         void *ret;
219         void *buf;
220         size_t buf_size;
221
222         int old_errno = errno;
223
224         ret = vmem_alloc(src, size, VM_NOSLEEP);
225         if (ret != NULL) {
226                 errno = old_errno;
227                 return (ret);
228         }
229
230         /*
231          * The allocation failed.  We need to grow the heap.
232          *
233          * First, try to use any buffers which failed earlier.
234          */
235         if (sbrk_fails.sf_next != &sbrk_fails &&
236             (ret = vmem_sbrk_tryfail(src, size, vmflags)) != NULL)
237                 return (ret);
238
239         buf_size = MAX(size, vmem_sbrk_minalloc);
240
241         /*
242          * buf_size gets overwritten with the actual allocated size
243          */
244         buf = _sbrk_grow_aligned(buf_size, real_pagesize, vmem_sbrk_pagesize,
245             &buf_size);
246
247         if (buf != MAP_FAILED) {
248                 ret = vmem_sbrk_extend_alloc(src, buf, buf_size, size, vmflags);
249                 if (ret != NULL) {
250                         errno = old_errno;
251                         return (ret);
252                 }
253         }
254
255         /*
256          * Growing the heap failed. The vmem_alloc() above called umem_reap().
257          */
258         ASSERT((vmflags & VM_NOSLEEP) == VM_NOSLEEP);
259
260         errno = old_errno;
261         return (NULL);
262 }
263
264 /*
265  * fork1() support
266  */
267 void
268 vmem_sbrk_lockup(void)
269 {
270         (void) mutex_lock(&sbrk_faillock);
271 }
272
273 void
274 vmem_sbrk_release(void)
275 {
276         (void) mutex_unlock(&sbrk_faillock);
277 }
278
279 vmem_t *
280 vmem_sbrk_arena(vmem_alloc_t **a_out, vmem_free_t **f_out)
281 {
282         if (sbrk_heap == NULL) {
283                 size_t heap_size;
284
285                 real_pagesize = sysconf(_SC_PAGESIZE);
286
287                 heap_size = vmem_sbrk_pagesize;
288
289                 if (issetugid()) {
290                         heap_size = 0;
291                 } else if (heap_size != 0 && !ISP2(heap_size)) {
292                         heap_size = 0;
293                         log_message("ignoring bad pagesize: 0x%p\n", heap_size);
294                 }
295                 if (heap_size <= real_pagesize) {
296                         heap_size = real_pagesize;
297                 } else {
298 #ifdef MHA_MAPSIZE_BSSBRK
299                         struct memcntl_mha mha;
300                         mha.mha_cmd = MHA_MAPSIZE_BSSBRK;
301                         mha.mha_flags = 0;
302                         mha.mha_pagesize = heap_size;
303
304                         if (memcntl(NULL, 0, MC_HAT_ADVISE, (char *)&mha, 0, 0)
305                             == -1) {
306                                 log_message("unable to set MAPSIZE_BSSBRK to "
307                                     "0x%p\n", heap_size);
308                                 heap_size = real_pagesize;
309                         }
310 #else
311                         heap_size = real_pagesize;
312 #endif
313                 }
314                 vmem_sbrk_pagesize = heap_size;
315
316                 /* validate vmem_sbrk_minalloc */
317                 if (vmem_sbrk_minalloc < VMEM_SBRK_MINALLOC)
318                         vmem_sbrk_minalloc = VMEM_SBRK_MINALLOC;
319                 vmem_sbrk_minalloc = P2ROUNDUP(vmem_sbrk_minalloc, heap_size);
320
321                 sbrk_heap = vmem_init("sbrk_top", real_pagesize,
322                     vmem_sbrk_alloc, vmem_free,
323                     "sbrk_heap", NULL, 0, real_pagesize,
324                     vmem_alloc, vmem_free);
325         }
326
327         if (a_out != NULL)
328                 *a_out = vmem_alloc;
329         if (f_out != NULL)
330                 *f_out = vmem_free;
331
332         return (sbrk_heap);
333 }
Note: See TracBrowser for help on using the browser.