xref: /illumos-gate/usr/src/lib/libumem/common/vmem_stand.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Standalone-specific vmem routines
31  *
32  * The standalone allocator operates on a pre-existing blob of memory, the
33  * location and dimensions of which are set using vmem_stand_setsize().  We
34  * then hand out CHUNKSIZE-sized pieces of this blob, until we run out.
35  */
36 
37 #define	DEF_CHUNKSIZE	(64 * 1024)	/* 64K */
38 
39 #define	DEF_NREGIONS	2
40 
41 #include <errno.h>
42 #include <limits.h>
43 #include <sys/sysmacros.h>
44 #include <sys/mman.h>
45 #include <unistd.h>
46 #include <strings.h>
47 
48 #include "vmem_base.h"
49 #include "misc.h"
50 
51 static vmem_t *stand_heap;
52 
53 static size_t stand_chunksize;
54 
55 typedef struct stand_region {
56 	caddr_t sr_base;
57 	caddr_t sr_curtop;
58 	size_t sr_left;
59 } stand_region_t;
60 
61 static stand_region_t stand_regions[DEF_NREGIONS];
62 static int stand_nregions;
63 
64 extern void membar_producer(void);
65 
66 void
67 vmem_stand_init(void)
68 {
69 	stand_chunksize = MAX(DEF_CHUNKSIZE, pagesize);
70 
71 	stand_nregions = 0;
72 }
73 
74 int
75 vmem_stand_add(caddr_t base, size_t len)
76 {
77 	stand_region_t *sr = &stand_regions[stand_nregions];
78 
79 	ASSERT(pagesize != 0);
80 
81 	if (stand_nregions == DEF_NREGIONS) {
82 		errno = ENOSPC;
83 		return (-1); /* we don't have room -- throw it back */
84 	}
85 
86 	/*
87 	 * We guarantee that only one call to `vmem_stand_add' will be
88 	 * active at a time, but we can't ensure that the allocator won't be
89 	 * in use while this function is being called.  As such, we have to
90 	 * ensure that sr is populated and visible to other processors before
91 	 * allowing the allocator to access the new region.
92 	 */
93 	sr->sr_base = base;
94 	sr->sr_curtop = (caddr_t)P2ROUNDUP((ulong_t)base, stand_chunksize);
95 	sr->sr_left = P2ALIGN(len - (size_t)(sr->sr_curtop - sr->sr_base),
96 	    stand_chunksize);
97 	membar_producer();
98 
99 	stand_nregions++;
100 
101 	return (0);
102 }
103 
104 static void *
105 stand_parent_alloc(vmem_t *src, size_t size, int vmflags)
106 {
107 	int old_errno = errno;
108 	stand_region_t *sr;
109 	size_t chksize;
110 	void *ret;
111 	int i;
112 
113 	if ((ret = vmem_alloc(src, size, VM_NOSLEEP)) != NULL) {
114 		errno = old_errno;
115 		return (ret);
116 	}
117 
118 	/* We need to allocate another chunk */
119 	chksize = roundup(size, stand_chunksize);
120 
121 	for (sr = stand_regions, i = 0; i < stand_nregions; i++, sr++) {
122 		if (sr->sr_left >= chksize)
123 			break;
124 	}
125 
126 	if (i == stand_nregions) {
127 		/*
128 		 * We don't have enough in any of our regions to satisfy the
129 		 * request.
130 		 */
131 		errno = old_errno;
132 		return (NULL);
133 	}
134 
135 	if ((ret = _vmem_extend_alloc(src, sr->sr_curtop, chksize, size,
136 	    vmflags)) == NULL) {
137 		errno = old_errno;
138 		return (NULL);
139 	}
140 
141 	bzero(sr->sr_curtop, chksize);
142 
143 	sr->sr_curtop += chksize;
144 	sr->sr_left -= chksize;
145 
146 	return (ret);
147 }
148 
149 vmem_t *
150 vmem_stand_arena(vmem_alloc_t **a_out, vmem_free_t **f_out)
151 {
152 	ASSERT(stand_nregions == 1);
153 
154 	stand_heap = vmem_init("stand_parent", stand_chunksize,
155 	    stand_parent_alloc, vmem_free,
156 	    "stand_heap", NULL, 0, pagesize, vmem_alloc, vmem_free);
157 
158 	if (a_out != NULL)
159 		*a_out = vmem_alloc;
160 	if (f_out != NULL)
161 		*f_out = vmem_free;
162 
163 	return (stand_heap);
164 }
165