xref: /dragonfly/lib/libc/upmap/upmap.c (revision 17183580)
1 /*
2  * Copyright (c) 2014,2019 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "namespace.h"
36 #include <sys/cdefs.h>
37 #include <sys/types.h>
38 #include <sys/syscall.h>
39 #include <sys/upmap.h>
40 #include <sys/time.h>
41 #include <sys/mman.h>
42 #include <sys/fcntl.h>
43 #include <errno.h>
44 #include <stdio.h>
45 #include <unistd.h>
46 #include <time.h>
47 #include <pthread.h>
48 #include "un-namespace.h"
49 #include "libc_private.h"
50 #include "upmap.h"
51 
52 /*
53  * kpmap - Global user/kernel shared map (RO)
54  * upmap - Per-process user/kernel shared map (RW)
55  * lpmap - Per-thread user/kernel shared map (RW)
56  */
57 static pthread_mutex_t ukpmap_lock;
58 static ukpheader_t *__kpmap_headers;
59 static ukpheader_t *__upmap_headers;
60 __thread ukpheader_t *__lpmap_headers TLS_ATTRIBUTE;
61 __thread uint32_t *__lpmap_blockallsigs TLS_ATTRIBUTE;
62 
63 static __thread int lpmap_ok;
64 static int kpmap_ok;
65 static int upmap_ok;
66 static pthread_once_t upmap_once = PTHREAD_ONCE_INIT;
67 
68 
69 /*
70  * Map the requested data item from the user-kernel global shared mmap
71  *
72  * *state is set to -1 on failure, else it is left alone.
73  * *datap is set to a pointer to the item on success, else it is left alone.
74  * If type == 0 this function finalizes state, setting it to 1 if it is 0.
75  */
76 void
__kpmap_map(void * datap,int * state,uint16_t type)77 __kpmap_map(void *datap, int *state, uint16_t type)
78 {
79 	ukpheader_t *head;
80 
81 	if (__isthreaded)
82 		_pthread_mutex_lock(&ukpmap_lock);
83 
84 	if (kpmap_ok <= 0) {
85 		int fd;
86 
87 		if (kpmap_ok < 0)
88 			goto failed;
89 		fd = _open("/dev/kpmap", O_RDONLY);
90 		if (fd < 0) {
91 			kpmap_ok = -1;
92 			goto failed;
93 		}
94 		__kpmap_headers = mmap(NULL, KPMAP_MAPSIZE,
95 				       PROT_READ, MAP_SHARED | MAP_FILE,
96 				       fd, 0);
97 		_close(fd);
98 		if ((void *)__kpmap_headers == MAP_FAILED) {
99 			kpmap_ok = -1;
100 			goto failed;
101 		}
102 		kpmap_ok = 1;
103 	}
104 
105 	/*
106 	 * Special case to finalize state
107 	 */
108 	if (type == 0) {
109 		if (*state == 0)
110 			*state = 1;
111 		if (__isthreaded)
112 			_pthread_mutex_unlock(&ukpmap_lock);
113 		return;
114 	}
115 
116 	/*
117 	 * Look for type.
118 	 */
119 	for (head = __kpmap_headers; head->type; ++head) {
120 		if (head->type == type) {
121 			*(void **)datap = (char *)__kpmap_headers +
122 					  head->offset;
123 			if (__isthreaded)
124 				_pthread_mutex_unlock(&ukpmap_lock);
125 			return;
126 		}
127 	}
128 failed:
129 	*state = -1;
130 	if (__isthreaded)
131 		_pthread_mutex_unlock(&ukpmap_lock);
132 }
133 
134 /*
135  * Map the requested data item from the user-kernel per-process shared mmap
136  *
137  * *state is set to -1 on failure, else it is left alone.
138  * *datap is set to a pointer to the item on success, else it is left alone.
139  * If type == 0 this function finalizes state, setting it to 1 if it is 0.
140  */
141 void
__upmap_map(void * datap,int * state,uint16_t type)142 __upmap_map(void *datap, int *state, uint16_t type)
143 {
144 	ukpheader_t *head;
145 
146 	if (__isthreaded)
147 		_pthread_mutex_lock(&ukpmap_lock);
148 
149 	if (upmap_ok <= 0) {
150 		int fd;
151 
152 		if (upmap_ok < 0)
153 			goto failed;
154 		fd = _open("/dev/upmap", O_RDWR);
155 		if (fd < 0) {
156 			upmap_ok = -1;
157 			goto failed;
158 		}
159 		__upmap_headers = mmap(NULL, UPMAP_MAPSIZE,
160 				       PROT_READ | PROT_WRITE,
161 				       MAP_SHARED | MAP_FILE,
162 				       fd, 0);
163 		_close(fd);
164 		if ((void *)__upmap_headers == MAP_FAILED) {
165 			upmap_ok = -1;
166 			goto failed;
167 		}
168 		upmap_ok = 1;
169 	}
170 
171 	/*
172 	 * Special case to finalize state
173 	 */
174 	if (type == 0) {
175 		if (*state == 0)
176 			*state = 1;
177 		if (__isthreaded)
178 			_pthread_mutex_unlock(&ukpmap_lock);
179 		return;
180 	}
181 
182 	/*
183 	 * Look for type.
184 	 */
185 	for (head = __upmap_headers; head->type; ++head) {
186 		if (head->type == type) {
187 			*(void **)datap = (char *)__upmap_headers +
188 					  head->offset;
189 			if (__isthreaded)
190 				_pthread_mutex_unlock(&ukpmap_lock);
191 			return;
192 		}
193 	}
194 failed:
195 	*state = -1;
196 	if (__isthreaded)
197 		_pthread_mutex_unlock(&ukpmap_lock);
198 }
199 
200 /*
201  * Map the requested data item from the user-kernel per-thread shared mmap
202  *
203  * *state is set to -1 on failure, else it is left alone.
204  * *datap is set to a pointer to the item on success, else it is left alone.
205  * If type == 0 this function finalizes state, setting it to 1 if it is 0.
206  *
207  * WARNING!  This code is used all over pthreads and must NOT make any
208  *	     reentrant pthreads calls until after the mapping has been
209  *	     set up.
210  */
211 static pthread_key_t lpmap_key;
212 
213 static void lpmap_unmap(void **datap);
214 
215 void
__lpmap_map(void * datap,int * state,uint16_t type)216 __lpmap_map(void *datap, int *state, uint16_t type)
217 {
218 	ukpheader_t *head;
219 
220 	if (lpmap_ok <= 0) {
221 		int fd;
222 
223 		if (lpmap_ok < 0)
224 			goto failed;
225 		fd = _open("/dev/lpmap", O_RDWR);
226 		if (fd < 0) {
227 			lpmap_ok = -1;
228 			goto failed;
229 		}
230 		__lpmap_headers = mmap(NULL, LPMAP_MAPSIZE,
231 				       PROT_READ | PROT_WRITE,
232 				       MAP_SHARED | MAP_FILE,
233 				       fd, 0);
234 		_close(fd);
235 		if ((void *)__lpmap_headers == MAP_FAILED) {
236 			lpmap_ok = -1;
237 			goto failed;
238 		}
239 		lpmap_ok = 1;
240 		_pthread_setspecific(lpmap_key, &__lpmap_headers);
241 	}
242 
243 	/*
244 	 * Special case to finalize state
245 	 */
246 	if (type == 0) {
247 		if (*state == 0)
248 			*state = 1;
249 		return;
250 	}
251 
252 	/*
253 	 * Look for type.
254 	 */
255 	for (head = __lpmap_headers; head->type; ++head) {
256 		if (head->type == type) {
257 			*(void **)datap = (char *)__lpmap_headers +
258 					  head->offset;
259 			return;
260 		}
261 	}
262 failed:
263 	*state = -1;
264 }
265 
266 /*
267  * Cleanup thread state
268  */
269 static void
lpmap_unmap(void ** datap)270 lpmap_unmap(void **datap)
271 {
272 	ukpheader_t *lpmap = *datap;
273 
274 	lpmap_ok = -1;
275 	if (lpmap) {
276 		__lpmap_blockallsigs = NULL;
277 		*datap = NULL;
278 		munmap(lpmap, LPMAP_MAPSIZE);
279 	}
280 }
281 
282 /*
283  * upmap initialization code, _upmap_thr_init() is called for the initial
284  * main thread by libc or pthreads, and on every thread create.  We need
285  * the __lpmap_blockallsigs pointer ASAP because it is used everywhere in
286  * pthreads.
287  *
288  * If pthreads is not linked in, _pthread_once() still runs via a stub in
289  * libc, and _pthread_key_create() is a NOP.
290  *
291  * NOTE: These pthreads calls are stubs when pthreads is not linked in.
292  *	 The once routine will still be run once regardless.
293  */
294 static
295 void
_upmap_init_once(void)296 _upmap_init_once(void)
297 {
298 	/* ignore error from stub if not threaded */
299 	_pthread_key_create(&lpmap_key, (void (*)(void *))lpmap_unmap);
300 }
301 
302 void
_upmap_thr_init(void)303 _upmap_thr_init(void)
304 {
305 	int dummy_state = 0;
306 
307         _pthread_once(&upmap_once, _upmap_init_once);
308 	__lpmap_map(&__lpmap_blockallsigs, &dummy_state, LPTYPE_BLOCKALLSIGS);
309 }
310