xseg_segdev user driver: close fd on deallocate
[archipelago] / xseg / drivers / user / xseg_posix.c
1 /*
2  * Copyright 2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *   2. Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials
14  *      provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * The views and conclusions contained in the software and
30  * documentation are those of the authors and should not be
31  * interpreted as representing official policies, either expressed
32  * or implied, of GRNET S.A.
33  */
34
35 #define _GNU_SOURCE
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/mman.h>
42 #include <sys/syscall.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <signal.h>
47 #include <sys/util.h>
48 #include <xseg/xseg.h>
49 #include <xtypes/xobj.h>
50 #include <drivers/xseg_posix.h>
51 #define ERRSIZE 512
52 char errbuf[ERRSIZE];
53
54 static long posix_allocate(const char *name, uint64_t size)
55 {
56         int fd, r;
57         fd = shm_open(name, O_RDWR | O_CREAT, 0770);
58         if (fd < 0) {
59                 XSEGLOG("Cannot create shared segment: %s\n",
60                         strerror_r(errno, errbuf, ERRSIZE));
61                 return fd;
62         }
63
64         r = lseek(fd, size -1, SEEK_SET);
65         if (r < 0) {
66                 close(fd);
67                 XSEGLOG("Cannot seek into segment file: %s\n",
68                         strerror_r(errno, errbuf, ERRSIZE));
69                 return r;
70         }
71
72         errbuf[0] = 0;
73         r = write(fd, errbuf, 1);
74         if (r != 1) {
75                 close(fd);
76                 XSEGLOG("Failed to set segment size: %s\n",
77                         strerror_r(errno, errbuf, ERRSIZE));
78                 return r;
79         }
80
81         close(fd);
82         return 0;
83 }
84
85 static long posix_deallocate(const char *name)
86 {
87         return shm_unlink(name);
88 }
89
90 static void *posix_map(const char *name, uint64_t size, struct xseg *seg)
91 {
92         struct xseg *xseg;
93         int fd;
94
95         if (seg)
96                 XSEGLOG("struct xseg * is not NULL. Ignoring...\n");
97
98         fd = shm_open(name, O_RDWR, 0000);
99         if (fd < 0) {
100                 XSEGLOG("Failed to open '%s' for mapping: %s\n",
101                         name, strerror_r(errno, errbuf, ERRSIZE));
102                 return NULL;
103         }
104
105         xseg = mmap (   XSEG_BASE_AS_PTR,
106                         size,
107                         PROT_READ | PROT_WRITE,
108                         MAP_SHARED | MAP_FIXED /* | MAP_LOCKED */,
109                         fd, 0   );
110
111         if (xseg == MAP_FAILED) {
112                 XSEGLOG("Could not map segment: %s\n",
113                         strerror_r(errno, errbuf, ERRSIZE));
114                 return NULL;
115         }
116
117         close(fd);
118         return xseg;
119 }
120
121 static void posix_unmap(void *ptr, uint64_t size)
122 {
123         struct xseg *xseg = ptr;
124         (void)munmap(xseg, xseg->segment_size);
125 }
126
127
128 static void handler(int signum)
129 {
130         static unsigned long counter;
131         printf("%lu: signal %d: this shouldn't have happened.\n", counter, signum);
132         counter ++;
133 }
134
135 static sigset_t savedset, set;
136 static pid_t pid;
137
138 static int posix_local_signal_init(struct xseg *xseg, xport portno)
139 {
140         void (*h)(int);
141         int r;
142         h = signal(SIGIO, handler);
143         if (h == SIG_ERR)
144                 return -1;
145
146         sigemptyset(&set);
147         sigaddset(&set, SIGIO);
148
149         r = sigprocmask(SIG_BLOCK, &set, &savedset);
150         if (r < 0)
151                 return -1;
152
153         pid = syscall(SYS_gettid);
154         return 0;
155 }
156
157 static void posix_local_signal_quit(struct xseg *xseg, xport portno)
158 {
159         pid = 0;
160         signal(SIGIO, SIG_DFL);
161         sigprocmask(SIG_SETMASK, &savedset, NULL);
162 }
163
164 static int posix_remote_signal_init(void)
165 {
166         return 0;
167 }
168
169 static void posix_remote_signal_quit(void)
170 {
171         return;
172 }
173
174 static int posix_prepare_wait(struct xseg *xseg, uint32_t portno)
175 {
176         struct xseg_port *port = xseg_get_port(xseg, portno);
177         if (!port) 
178                 return -1;
179         struct posix_signal_desc *psd = xseg_get_signal_desc(xseg, port);
180         if (!psd)
181                 return -1;
182         psd->waitcue = pid;
183         return 0;
184 }
185
186 static int posix_cancel_wait(struct xseg *xseg, uint32_t portno)
187 {
188         struct xseg_port *port = xseg_get_port(xseg, portno);
189         if (!port) 
190                 return -1;
191         struct posix_signal_desc *psd = xseg_get_signal_desc(xseg, port);
192         if (!psd)
193                 return -1;
194         psd->waitcue = 0;
195         return 0;
196 }
197
198 static int posix_wait_signal(struct xseg *xseg, uint32_t usec_timeout)
199 {
200         int r;
201         siginfo_t siginfo;
202         struct timespec ts;
203
204         ts.tv_sec = usec_timeout / 1000000;
205         ts.tv_nsec = 1000 * (usec_timeout - ts.tv_sec * 1000000);
206
207         /* FIXME: Now that posix signaling is fixed, we could get rid of the timeout
208          * and use a NULL timespec linux-specific)
209          */
210         r = sigtimedwait(&set, &siginfo, &ts);
211         if (r < 0)
212                 return r;
213
214         return siginfo.si_signo;
215 }
216
217 static int posix_signal(struct xseg *xseg, uint32_t portno)
218 {
219         struct xseg_port *port = xseg_get_port(xseg, portno);
220         if (!port) 
221                 return -1;
222         struct posix_signal_desc *psd = xseg_get_signal_desc(xseg, port);
223         if (!psd)
224                 return -1;
225         pid_t cue = (pid_t)psd->waitcue;
226         if (!cue)
227                 return 0;
228
229         /* FIXME: Make calls to xseg_signal() check for errors */
230         return syscall(SYS_tkill, cue, SIGIO);
231 }
232
233 static void *posix_malloc(uint64_t size)
234 {
235         return malloc((size_t)size);
236 }
237
238 static void *posix_realloc(void *mem, uint64_t size)
239 {
240         return realloc(mem, (size_t)size);
241 }
242
243 static void posix_mfree(void *mem)
244 {
245         free(mem);
246 }
247
248
249 int posix_init_signal_desc(struct xseg *xseg, void *sd)
250 {
251         struct posix_signal_desc *psd = sd;
252         if (!psd)
253                 return -1;
254         psd->waitcue = 0;
255         return 0;
256 }
257
258 void posix_quit_signal_desc(struct xseg *xseg, void *sd)
259 {
260         return;
261 }
262
263 void * posix_alloc_data(struct xseg *xseg)
264 {
265         struct xobject_h *sd_h = xseg_get_objh(xseg, MAGIC_POSIX_SD, 
266                         sizeof(struct posix_signal_desc));
267         return sd_h;
268 }
269
270 void posix_free_data(struct xseg *xseg, void *data)
271 {
272         if (data)
273                 xseg_put_objh(xseg, (struct xobject_h *)data);
274 }
275
276 void *posix_alloc_signal_desc(struct xseg *xseg, void *data)
277 {
278         struct xobject_h *sd_h = (struct xobject_h *) data;
279         if (!sd_h)
280                 return NULL;
281         struct posix_signal_desc *psd = xobj_get_obj(sd_h, X_ALLOC);
282         if (!psd)
283                 return NULL;
284         psd->waitcue = 0;
285         return psd;
286
287 }
288
289 void posix_free_signal_desc(struct xseg *xseg, void *data, void *sd)
290 {
291         struct xobject_h *sd_h = (struct xobject_h *) data;
292         if (!sd_h)
293                 return;
294         if (sd)
295                 xobj_put_obj(sd_h, sd);
296         return;
297 }
298
299 static struct xseg_type xseg_posix = {
300         /* xseg_operations */
301         {
302                 .mfree          = posix_mfree,
303                 .allocate       = posix_allocate,
304                 .deallocate     = posix_deallocate,
305                 .map            = posix_map,
306                 .unmap          = posix_unmap,
307         },
308         /* name */
309         "posix"
310 };
311
312 static struct xseg_peer xseg_peer_posix = {
313         /* xseg_peer_operations */
314         {
315                 .init_signal_desc   = posix_init_signal_desc,
316                 .quit_signal_desc   = posix_quit_signal_desc,
317                 .alloc_data         = posix_alloc_data,
318                 .free_data          = posix_free_data,
319                 .alloc_signal_desc  = posix_alloc_signal_desc,
320                 .free_signal_desc   = posix_free_signal_desc,
321                 .local_signal_init  = posix_local_signal_init,
322                 .local_signal_quit  = posix_local_signal_quit,
323                 .remote_signal_init = posix_remote_signal_init,
324                 .remote_signal_quit = posix_remote_signal_quit,
325                 .prepare_wait       = posix_prepare_wait,
326                 .cancel_wait        = posix_cancel_wait,
327                 .wait_signal        = posix_wait_signal,
328                 .signal             = posix_signal,
329                 .malloc             = posix_malloc,
330                 .realloc            = posix_realloc,
331                 .mfree              = posix_mfree,
332         },
333         /* name */
334         "posix"
335 };
336
337 void xseg_posix_init(void)
338 {
339         xseg_register_type(&xseg_posix);
340         xseg_register_peer(&xseg_peer_posix);
341 }
342