Temporarily move taskq+util to libzpool until that directory is broken in to lib...
[zfs.git] / zfs / lib / libumem / malloc.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 (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 /*
23  * Copyright 2008 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 #include <unistd.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/sysmacros.h>
33 #include "umem_base.h"
34 #include "misc.h"
35
36 /*
37  * malloc_data_t is an 8-byte structure which is located "before" the pointer
38  * returned from {m,c,re}alloc and memalign.  The first four bytes give
39  * information about the buffer, and the second four bytes are a status byte.
40  *
41  * See umem_impl.h for the various magic numbers used, and the size
42  * encode/decode macros.
43  *
44  * The 'size' of the buffer includes the tags.  That is, we encode the
45  * argument to umem_alloc(), not the argument to malloc().
46  */
47
48 typedef struct malloc_data {
49         uint32_t malloc_size;
50         uint32_t malloc_stat; /* = UMEM_MALLOC_ENCODE(state, malloc_size) */
51 } malloc_data_t;
52
53 void *
54 malloc(size_t size_arg)
55 {
56 #ifdef _LP64
57         uint32_t high_size = 0;
58 #endif
59         size_t size;
60
61         malloc_data_t *ret;
62         size = size_arg + sizeof (malloc_data_t);
63
64 #ifdef _LP64
65         if (size > UMEM_SECOND_ALIGN) {
66                 size += sizeof (malloc_data_t);
67                 high_size = (size >> 32);
68         }
69 #endif
70         if (size < size_arg) {
71                 errno = ENOMEM;                 /* overflow */
72                 return (NULL);
73         }
74         ret = (malloc_data_t *)_umem_alloc(size, UMEM_DEFAULT);
75         if (ret == NULL) {
76                 if (size <= UMEM_MAXBUF)
77                         errno = EAGAIN;
78                 else
79                         errno = ENOMEM;
80                 return (NULL);
81 #ifdef _LP64
82         } else if (high_size > 0) {
83                 uint32_t low_size = (uint32_t)size;
84
85                 /*
86                  * uses different magic numbers to make it harder to
87                  * undetectably corrupt
88                  */
89                 ret->malloc_size = high_size;
90                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, high_size);
91                 ret++;
92
93                 ret->malloc_size = low_size;
94                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_OVERSIZE_MAGIC,
95                     low_size);
96                 ret++;
97         } else if (size > UMEM_SECOND_ALIGN) {
98                 uint32_t low_size = (uint32_t)size;
99
100                 ret++; /* leave the first 8 bytes alone */
101
102                 ret->malloc_size = low_size;
103                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_SECOND_MAGIC,
104                     low_size);
105                 ret++;
106 #endif
107         } else {
108                 ret->malloc_size = size;
109                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, size);
110                 ret++;
111         }
112         return ((void *)ret);
113 }
114
115 void *
116 calloc(size_t nelem, size_t elsize)
117 {
118         size_t size = nelem * elsize;
119         void *retval;
120
121         if (nelem > 0 && elsize > 0 && size/nelem != elsize) {
122                 errno = ENOMEM;                         /* overflow */
123                 return (NULL);
124         }
125
126         retval = malloc(size);
127         if (retval == NULL)
128                 return (NULL);
129
130         (void) memset(retval, 0, size);
131         return (retval);
132 }
133
134 /*
135  * memalign uses vmem_xalloc to do its work.
136  *
137  * in 64-bit, the memaligned buffer always has two tags.  This simplifies the
138  * code.
139  */
140
141 void *
142 memalign(size_t align, size_t size_arg)
143 {
144         size_t size;
145         uintptr_t phase;
146
147         void *buf;
148         malloc_data_t *ret;
149
150         size_t overhead;
151
152         if (size_arg == 0 || align == 0 || (align & (align - 1)) != 0) {
153                 errno = EINVAL;
154                 return (NULL);
155         }
156
157         /*
158          * if malloc provides the required alignment, use it.
159          */
160         if (align <= UMEM_ALIGN ||
161             (align <= UMEM_SECOND_ALIGN && size_arg >= UMEM_SECOND_ALIGN))
162                 return (malloc(size_arg));
163
164 #ifdef _LP64
165         overhead = 2 * sizeof (malloc_data_t);
166 #else
167         overhead = sizeof (malloc_data_t);
168 #endif
169
170         ASSERT(overhead <= align);
171
172         size = size_arg + overhead;
173         phase = align - overhead;
174
175         if (umem_memalign_arena == NULL && umem_init() == 0) {
176                 errno = ENOMEM;
177                 return (NULL);
178         }
179
180         if (size < size_arg) {
181                 errno = ENOMEM;                 /* overflow */
182                 return (NULL);
183         }
184
185         buf = vmem_xalloc(umem_memalign_arena, size, align, phase,
186             0, NULL, NULL, VM_NOSLEEP);
187
188         if (buf == NULL) {
189                 if ((size_arg + align) <= UMEM_MAXBUF)
190                         errno = EAGAIN;
191                 else
192                         errno = ENOMEM;
193
194                 return (NULL);
195         }
196
197         ret = (malloc_data_t *)buf;
198         {
199                 uint32_t low_size = (uint32_t)size;
200
201 #ifdef _LP64
202                 uint32_t high_size = (uint32_t)(size >> 32);
203
204                 ret->malloc_size = high_size;
205                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC,
206                     high_size);
207                 ret++;
208 #endif
209
210                 ret->malloc_size = low_size;
211                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC, low_size);
212                 ret++;
213         }
214
215         ASSERT(P2PHASE((uintptr_t)ret, align) == 0);
216         ASSERT((void *)((uintptr_t)ret - overhead) == buf);
217
218         return ((void *)ret);
219 }
220
221 void *
222 valloc(size_t size)
223 {
224         return (memalign(pagesize, size));
225 }
226
227 /*
228  * process_free:
229  *
230  * Pulls information out of a buffer pointer, and optionally free it.
231  * This is used by free() and realloc() to process buffers.
232  *
233  * On failure, calls umem_err_recoverable() with an appropriate message
234  * On success, returns the data size through *data_size_arg, if (!is_free).
235  *
236  * Preserves errno, since free()'s semantics require it.
237  */
238
239 static int
240 process_free(void *buf_arg,
241     int do_free,                /* free the buffer, or just get its size? */
242     size_t *data_size_arg)      /* output: bytes of data in buf_arg */
243 {
244         malloc_data_t *buf;
245
246         void *base;
247         size_t size;
248         size_t data_size;
249
250         const char *message;
251         int old_errno = errno;
252
253         buf = (malloc_data_t *)buf_arg;
254
255         buf--;
256         size = buf->malloc_size;
257
258         switch (UMEM_MALLOC_DECODE(buf->malloc_stat, size)) {
259
260         case MALLOC_MAGIC:
261                 base = (void *)buf;
262                 data_size = size - sizeof (malloc_data_t);
263
264                 if (do_free)
265                         buf->malloc_stat = UMEM_FREE_PATTERN_32;
266
267                 goto process_malloc;
268
269 #ifdef _LP64
270         case MALLOC_SECOND_MAGIC:
271                 base = (void *)(buf - 1);
272                 data_size = size - 2 * sizeof (malloc_data_t);
273
274                 if (do_free)
275                         buf->malloc_stat = UMEM_FREE_PATTERN_32;
276
277                 goto process_malloc;
278
279         case MALLOC_OVERSIZE_MAGIC: {
280                 size_t high_size;
281
282                 buf--;
283                 high_size = buf->malloc_size;
284
285                 if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) !=
286                     MALLOC_MAGIC) {
287                         message = "invalid or corrupted buffer";
288                         break;
289                 }
290
291                 size += high_size << 32;
292
293                 base = (void *)buf;
294                 data_size = size - 2 * sizeof (malloc_data_t);
295
296                 if (do_free) {
297                         buf->malloc_stat = UMEM_FREE_PATTERN_32;
298                         (buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32;
299                 }
300
301                 goto process_malloc;
302         }
303 #endif
304
305         case MEMALIGN_MAGIC: {
306                 size_t overhead = sizeof (malloc_data_t);
307
308 #ifdef _LP64
309                 size_t high_size;
310
311                 overhead += sizeof (malloc_data_t);
312
313                 buf--;
314                 high_size = buf->malloc_size;
315
316                 if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) !=
317                     MEMALIGN_MAGIC) {
318                         message = "invalid or corrupted buffer";
319                         break;
320                 }
321                 size += high_size << 32;
322
323                 /*
324                  * destroy the main tag's malloc_stat
325                  */
326                 if (do_free)
327                         (buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32;
328 #endif
329
330                 base = (void *)buf;
331                 data_size = size - overhead;
332
333                 if (do_free)
334                         buf->malloc_stat = UMEM_FREE_PATTERN_32;
335
336                 goto process_memalign;
337         }
338         default:
339                 if (buf->malloc_stat == UMEM_FREE_PATTERN_32)
340                         message = "double-free or invalid buffer";
341                 else
342                         message = "invalid or corrupted buffer";
343                 break;
344         }
345
346         umem_err_recoverable("%s(%p): %s\n",
347             do_free? "free" : "realloc", buf_arg, message);
348
349         errno = old_errno;
350         return (0);
351
352 process_malloc:
353         if (do_free)
354                 _umem_free(base, size);
355         else
356                 *data_size_arg = data_size;
357
358         errno = old_errno;
359         return (1);
360
361 process_memalign:
362         if (do_free)
363                 vmem_xfree(umem_memalign_arena, base, size);
364         else
365                 *data_size_arg = data_size;
366
367         errno = old_errno;
368         return (1);
369 }
370
371 void
372 free(void *buf)
373 {
374         if (buf == NULL)
375                 return;
376
377         /*
378          * Process buf, freeing it if it is not corrupt.
379          */
380         (void) process_free(buf, 1, NULL);
381 }
382
383 void *
384 realloc(void *buf_arg, size_t newsize)
385 {
386         size_t oldsize;
387         void *buf;
388
389         if (buf_arg == NULL)
390                 return (malloc(newsize));
391
392         if (newsize == 0) {
393                 free(buf_arg);
394                 return (NULL);
395         }
396
397         /*
398          * get the old data size without freeing the buffer
399          */
400         if (process_free(buf_arg, 0, &oldsize) == 0) {
401                 errno = EINVAL;
402                 return (NULL);
403         }
404
405         if (newsize == oldsize)         /* size didn't change */
406                 return (buf_arg);
407
408         buf = malloc(newsize);
409         if (buf == NULL)
410                 return (NULL);
411
412         (void) memcpy(buf, buf_arg, MIN(newsize, oldsize));
413         free(buf_arg);
414         return (buf);
415 }