1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * CDDL HEADER START
4 *
5 * The contents of this file are subject to the terms of the
6 * Common Development and Distribution License, Version 1.0 only
7 * (the "License"). You may not use this file except in compliance
8 * with the License.
9 *
10 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11 * or https://opensource.org/licenses/CDDL-1.0.
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23 /*
24 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 #ifndef _LIBSPL_UMEM_H
29 #define _LIBSPL_UMEM_H
30
31 /*
32 * XXX: We should use the real portable umem library if it is detected
33 * at configure time. However, if the library is not available, we can
34 * use a trivial malloc based implementation. This obviously impacts
35 * performance, but unless you are using a full userspace build of zpool for
36 * something other than ztest, you are likely not going to notice or care.
37 *
38 * https://labs.omniti.com/trac/portableumem
39 */
40 #include <sys/debug.h>
41
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
46
47 #ifdef __cplusplus
48 extern "C" {
49 #endif
50
51 typedef void vmem_t;
52
53 /*
54 * Flags for umem_alloc/umem_free
55 */
56 #define UMEM_DEFAULT 0x0000 /* normal -- may fail */
57 #define UMEM_NOFAIL 0x0100 /* Never fails */
58
59 /*
60 * Flags for umem_cache_create()
61 */
62 #define UMC_NODEBUG 0x00020000
63
64 #define UMEM_CACHE_NAMELEN 31
65
66 typedef int umem_nofail_callback_t(void);
67 typedef int umem_constructor_t(void *, void *, int);
68 typedef void umem_destructor_t(void *, void *);
69 typedef void umem_reclaim_t(void *);
70
71 typedef struct umem_cache {
72 char cache_name[UMEM_CACHE_NAMELEN + 1];
73 size_t cache_bufsize;
74 size_t cache_align;
75 umem_constructor_t *cache_constructor;
76 umem_destructor_t *cache_destructor;
77 umem_reclaim_t *cache_reclaim;
78 void *cache_private;
79 void *cache_arena;
80 int cache_cflags;
81 } umem_cache_t;
82
83 /* Prototypes for functions to provide defaults for umem envvars */
84 const char *_umem_debug_init(void);
85 const char *_umem_options_init(void);
86 const char *_umem_logging_init(void);
87
88 __attribute__((malloc, alloc_size(1)))
89 static inline void *
umem_alloc(size_t size,int flags)90 umem_alloc(size_t size, int flags)
91 {
92 void *ptr = NULL;
93
94 do {
95 ptr = malloc(size);
96 } while (ptr == NULL && (flags & UMEM_NOFAIL));
97
98 return (ptr);
99 }
100
101 __attribute__((malloc, alloc_size(1)))
102 static inline void *
umem_alloc_aligned(size_t size,size_t align,int flags)103 umem_alloc_aligned(size_t size, size_t align, int flags)
104 {
105 void *ptr = NULL;
106 int rc;
107
108 do {
109 rc = posix_memalign(&ptr, align, size);
110 } while (rc == ENOMEM && (flags & UMEM_NOFAIL));
111
112 if (rc == EINVAL) {
113 fprintf(stderr, "%s: invalid memory alignment (%zd)\n",
114 __func__, align);
115 if (flags & UMEM_NOFAIL)
116 abort();
117 return (NULL);
118 }
119
120 return (ptr);
121 }
122
123 __attribute__((malloc, alloc_size(1)))
124 static inline void *
umem_zalloc(size_t size,int flags)125 umem_zalloc(size_t size, int flags)
126 {
127 void *ptr = NULL;
128
129 ptr = umem_alloc(size, flags);
130 if (ptr)
131 memset(ptr, 0, size);
132
133 return (ptr);
134 }
135
136 static inline void
umem_free(const void * ptr,size_t size __maybe_unused)137 umem_free(const void *ptr, size_t size __maybe_unused)
138 {
139 free((void *)ptr);
140 }
141
142 /*
143 * umem_free_aligned was added for supporting portability
144 * with non-POSIX platforms that require a different free
145 * to be used with aligned allocations.
146 */
147 static inline void
umem_free_aligned(void * ptr,size_t size __maybe_unused)148 umem_free_aligned(void *ptr, size_t size __maybe_unused)
149 {
150 #ifndef _WIN32
151 free((void *)ptr);
152 #else
153 _aligned_free(ptr);
154 #endif
155 }
156
157 static inline void
umem_nofail_callback(umem_nofail_callback_t * cb __maybe_unused)158 umem_nofail_callback(umem_nofail_callback_t *cb __maybe_unused)
159 {}
160
161 static inline umem_cache_t *
umem_cache_create(const char * name,size_t bufsize,size_t align,umem_constructor_t * constructor,umem_destructor_t * destructor,umem_reclaim_t * reclaim,void * priv,void * vmp,int cflags)162 umem_cache_create(
163 const char *name, size_t bufsize, size_t align,
164 umem_constructor_t *constructor,
165 umem_destructor_t *destructor,
166 umem_reclaim_t *reclaim,
167 void *priv, void *vmp, int cflags)
168 {
169 umem_cache_t *cp;
170
171 cp = (umem_cache_t *)umem_alloc(sizeof (umem_cache_t), UMEM_DEFAULT);
172 if (cp) {
173 strlcpy(cp->cache_name, name, UMEM_CACHE_NAMELEN);
174 cp->cache_bufsize = bufsize;
175 cp->cache_align = align;
176 cp->cache_constructor = constructor;
177 cp->cache_destructor = destructor;
178 cp->cache_reclaim = reclaim;
179 cp->cache_private = priv;
180 cp->cache_arena = vmp;
181 cp->cache_cflags = cflags;
182 }
183
184 return (cp);
185 }
186
187 static inline void
umem_cache_destroy(umem_cache_t * cp)188 umem_cache_destroy(umem_cache_t *cp)
189 {
190 umem_free(cp, sizeof (umem_cache_t));
191 }
192
193 __attribute__((malloc))
194 static inline void *
umem_cache_alloc(umem_cache_t * cp,int flags)195 umem_cache_alloc(umem_cache_t *cp, int flags)
196 {
197 void *ptr = NULL;
198
199 if (cp->cache_align != 0)
200 ptr = umem_alloc_aligned(
201 cp->cache_bufsize, cp->cache_align, flags);
202 else
203 ptr = umem_alloc(cp->cache_bufsize, flags);
204
205 if (ptr && cp->cache_constructor)
206 cp->cache_constructor(ptr, cp->cache_private, UMEM_DEFAULT);
207
208 return (ptr);
209 }
210
211 static inline void
umem_cache_free(umem_cache_t * cp,void * ptr)212 umem_cache_free(umem_cache_t *cp, void *ptr)
213 {
214 if (cp->cache_destructor)
215 cp->cache_destructor(ptr, cp->cache_private);
216
217 if (cp->cache_align != 0)
218 umem_free_aligned(ptr, cp->cache_bufsize);
219 else
220 umem_free(ptr, cp->cache_bufsize);
221 }
222
223 static inline void
umem_cache_reap_now(umem_cache_t * cp __maybe_unused)224 umem_cache_reap_now(umem_cache_t *cp __maybe_unused)
225 {
226 }
227
228 #ifdef __cplusplus
229 }
230 #endif
231
232 #endif
233