Temporarily move taskq+util to libzpool until that directory is broken in to lib...
[zfs.git] / zfs / lib / libumem / vmem_stand.c
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 }