Initial Linux ZFS GIT Repo
[zfs.git] / zfs / lib / libumem / vmem_mmap.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 2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * Portions Copyright 2006 OmniTI, Inc.
28  */
29
30 /* #pragma ident        "@(#)vmem_mmap.c        1.2     05/06/08 SMI" */
31
32 #include "config.h"
33 #include <errno.h>
34
35 #if HAVE_SYS_MMAN_H
36 #include <sys/mman.h>
37 #endif
38
39 #ifdef HAVE_SYS_SYSMACROS_H
40 #include <sys/sysmacros.h>
41 #endif
42
43 #include <unistd.h>
44
45 #include "vmem_base.h"
46
47 #define ALLOC_PROT      PROT_READ | PROT_WRITE | PROT_EXEC
48 #define FREE_PROT       PROT_NONE
49
50 #define ALLOC_FLAGS     MAP_PRIVATE | MAP_ANON
51 #define FREE_FLAGS      MAP_PRIVATE | MAP_ANON | MAP_NORESERVE
52
53 #ifdef MAP_ALIGN
54 #define CHUNKSIZE       (64*1024)       /* 64 kilobytes */
55 #else
56 static size_t CHUNKSIZE;
57 #endif
58
59 static vmem_t *mmap_heap;
60
61 static void *
62 vmem_mmap_alloc(vmem_t *src, size_t size, int vmflags)
63 {
64         void *ret;
65         int old_errno = errno;
66
67         ret = vmem_alloc(src, size, vmflags);
68 #ifndef _WIN32
69         if (ret != NULL
70                 &&
71             mmap(ret, size, ALLOC_PROT, ALLOC_FLAGS | MAP_FIXED, -1, 0) ==
72             MAP_FAILED
73                 ) {
74                 vmem_free(src, ret, size);
75                 vmem_reap();
76
77                 ASSERT((vmflags & VM_NOSLEEP) == VM_NOSLEEP);
78                 errno = old_errno;
79                 return (NULL);
80         }
81 #endif
82
83         errno = old_errno;
84         return (ret);
85 }
86
87 static void
88 vmem_mmap_free(vmem_t *src, void *addr, size_t size)
89 {
90         int old_errno = errno;
91 #ifdef _WIN32
92         VirtualFree(addr, size, MEM_RELEASE);
93 #else
94         (void) mmap(addr, size, FREE_PROT, FREE_FLAGS | MAP_FIXED, -1, 0);
95 #endif
96         vmem_free(src, addr, size);
97         errno = old_errno;
98 }
99
100 static void *
101 vmem_mmap_top_alloc(vmem_t *src, size_t size, int vmflags)
102 {
103         void *ret;
104         void *buf;
105         int old_errno = errno;
106
107         ret = vmem_alloc(src, size, VM_NOSLEEP);
108
109         if (ret) {
110                 errno = old_errno;
111                 return (ret);
112         }
113         /*
114          * Need to grow the heap
115          */
116 #ifdef _WIN32
117         buf = VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
118         if (buf == NULL) buf = MAP_FAILED;
119 #else
120         buf = mmap(
121 #ifdef MAP_ALIGN
122                         (void *)CHUNKSIZE,
123 #else
124                         0,
125 #endif
126                         size, FREE_PROT, FREE_FLAGS
127 #ifdef MAP_ALIGN
128                         | MAP_ALIGN
129 #endif
130                         , -1, 0);
131 #endif
132
133         if (buf != MAP_FAILED) {
134                 ret = _vmem_extend_alloc(src, buf, size, size, vmflags);
135                 if (ret != NULL)
136                         return (ret);
137                 else {
138                         (void) munmap(buf, size);
139                         errno = old_errno;
140                         return (NULL);
141                 }
142         } else {
143                 /*
144                  * Growing the heap failed.  The allocation above will
145                  * already have called umem_reap().
146                  */
147                 ASSERT((vmflags & VM_NOSLEEP) == VM_NOSLEEP);
148
149                 errno = old_errno;
150                 return (NULL);
151         }
152 }
153
154 vmem_t *
155 vmem_mmap_arena(vmem_alloc_t **a_out, vmem_free_t **f_out)
156 {
157 #ifdef _WIN32
158         SYSTEM_INFO info;
159         size_t pagesize;
160 #else
161         size_t pagesize = _sysconf(_SC_PAGESIZE);
162 #endif
163         
164 #ifdef _WIN32
165         GetSystemInfo(&info);
166         pagesize = info.dwPageSize;
167         CHUNKSIZE = info.dwAllocationGranularity;
168 #elif !defined(MAP_ALIGN)
169         CHUNKSIZE = pagesize;
170 #endif
171         
172         if (mmap_heap == NULL) {
173                 mmap_heap = vmem_init("mmap_top", 
174                         CHUNKSIZE,
175                     vmem_mmap_top_alloc, vmem_free,
176                     "mmap_heap", NULL, 0, pagesize,
177                     vmem_mmap_alloc, vmem_mmap_free);
178         }
179
180         if (a_out != NULL)
181                 *a_out = vmem_mmap_alloc;
182         if (f_out != NULL)
183                 *f_out = vmem_mmap_free;
184
185         return (mmap_heap);
186 }