1 | /* $NetBSD: uipc_sem.c,v 1.47 2016/10/31 15:08:45 maxv Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2011 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Mindaugas Rasiukevicius. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org> |
34 | * All rights reserved. |
35 | * |
36 | * Redistribution and use in source and binary forms, with or without |
37 | * modification, are permitted provided that the following conditions |
38 | * are met: |
39 | * 1. Redistributions of source code must retain the above copyright |
40 | * notice, this list of conditions and the following disclaimer. |
41 | * 2. Redistributions in binary form must reproduce the above copyright |
42 | * notice, this list of conditions and the following disclaimer in the |
43 | * documentation and/or other materials provided with the distribution. |
44 | * |
45 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
55 | * SUCH DAMAGE. |
56 | */ |
57 | |
58 | /* |
59 | * Implementation of POSIX semaphore. |
60 | */ |
61 | |
62 | #include <sys/cdefs.h> |
63 | __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.47 2016/10/31 15:08:45 maxv Exp $" ); |
64 | |
65 | #include <sys/param.h> |
66 | #include <sys/kernel.h> |
67 | |
68 | #include <sys/atomic.h> |
69 | #include <sys/proc.h> |
70 | #include <sys/ksem.h> |
71 | #include <sys/syscall.h> |
72 | #include <sys/stat.h> |
73 | #include <sys/kmem.h> |
74 | #include <sys/fcntl.h> |
75 | #include <sys/file.h> |
76 | #include <sys/filedesc.h> |
77 | #include <sys/kauth.h> |
78 | #include <sys/module.h> |
79 | #include <sys/mount.h> |
80 | #include <sys/semaphore.h> |
81 | #include <sys/syscall.h> |
82 | #include <sys/syscallargs.h> |
83 | #include <sys/syscallvar.h> |
84 | #include <sys/sysctl.h> |
85 | |
86 | MODULE(MODULE_CLASS_MISC, ksem, NULL); |
87 | |
88 | #define SEM_MAX_NAMELEN 14 |
89 | |
90 | #define SEM_NSEMS_MAX 256 |
91 | #define KS_UNLINKED 0x01 |
92 | |
93 | static kmutex_t ksem_lock __cacheline_aligned; |
94 | static LIST_HEAD(,ksem) ksem_head __cacheline_aligned; |
95 | static u_int nsems_total __cacheline_aligned; |
96 | static u_int nsems __cacheline_aligned; |
97 | |
98 | static kauth_listener_t ksem_listener; |
99 | |
100 | static int ksem_sysinit(void); |
101 | static int ksem_sysfini(bool); |
102 | static int ksem_modcmd(modcmd_t, void *); |
103 | static int ksem_close_fop(file_t *); |
104 | static int ksem_stat_fop(file_t *, struct stat *); |
105 | static int ksem_read_fop(file_t *, off_t *, struct uio *, |
106 | kauth_cred_t, int); |
107 | |
108 | static const struct fileops semops = { |
109 | .fo_read = ksem_read_fop, |
110 | .fo_write = fbadop_write, |
111 | .fo_ioctl = fbadop_ioctl, |
112 | .fo_fcntl = fnullop_fcntl, |
113 | .fo_poll = fnullop_poll, |
114 | .fo_stat = ksem_stat_fop, |
115 | .fo_close = ksem_close_fop, |
116 | .fo_kqfilter = fnullop_kqfilter, |
117 | .fo_restart = fnullop_restart, |
118 | }; |
119 | |
120 | static const struct syscall_package ksem_syscalls[] = { |
121 | { SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init }, |
122 | { SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open }, |
123 | { SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink }, |
124 | { SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close }, |
125 | { SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post }, |
126 | { SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait }, |
127 | { SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait }, |
128 | { SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue }, |
129 | { SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy }, |
130 | { SYS__ksem_timedwait, 0, (sy_call_t *)sys__ksem_timedwait }, |
131 | { 0, 0, NULL }, |
132 | }; |
133 | |
134 | struct sysctllog *ksem_clog; |
135 | int ksem_max; |
136 | |
137 | static int |
138 | ksem_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, |
139 | void *arg0, void *arg1, void *arg2, void *arg3) |
140 | { |
141 | ksem_t *ks; |
142 | mode_t mode; |
143 | |
144 | if (action != KAUTH_SYSTEM_SEMAPHORE) |
145 | return KAUTH_RESULT_DEFER; |
146 | |
147 | ks = arg1; |
148 | mode = ks->ks_mode; |
149 | |
150 | if ((kauth_cred_geteuid(cred) == ks->ks_uid && (mode & S_IWUSR) != 0) || |
151 | (kauth_cred_getegid(cred) == ks->ks_gid && (mode & S_IWGRP) != 0) || |
152 | (mode & S_IWOTH) != 0) |
153 | return KAUTH_RESULT_ALLOW; |
154 | |
155 | return KAUTH_RESULT_DEFER; |
156 | } |
157 | |
158 | static int |
159 | ksem_sysinit(void) |
160 | { |
161 | int error; |
162 | const struct sysctlnode *rnode; |
163 | |
164 | mutex_init(&ksem_lock, MUTEX_DEFAULT, IPL_NONE); |
165 | LIST_INIT(&ksem_head); |
166 | nsems_total = 0; |
167 | nsems = 0; |
168 | |
169 | error = syscall_establish(NULL, ksem_syscalls); |
170 | if (error) { |
171 | (void)ksem_sysfini(false); |
172 | } |
173 | |
174 | ksem_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, |
175 | ksem_listener_cb, NULL); |
176 | |
177 | /* Define module-specific sysctl tree */ |
178 | |
179 | ksem_max = KSEM_MAX; |
180 | ksem_clog = NULL; |
181 | |
182 | sysctl_createv(&ksem_clog, 0, NULL, &rnode, |
183 | CTLFLAG_PERMANENT, |
184 | CTLTYPE_NODE, "posix" , |
185 | SYSCTL_DESCR("POSIX options" ), |
186 | NULL, 0, NULL, 0, |
187 | CTL_KERN, CTL_CREATE, CTL_EOL); |
188 | sysctl_createv(&ksem_clog, 0, &rnode, NULL, |
189 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, |
190 | CTLTYPE_INT, "semmax" , |
191 | SYSCTL_DESCR("Maximal number of semaphores" ), |
192 | NULL, 0, &ksem_max, 0, |
193 | CTL_CREATE, CTL_EOL); |
194 | sysctl_createv(&ksem_clog, 0, &rnode, NULL, |
195 | CTLFLAG_PERMANENT | CTLFLAG_READONLY, |
196 | CTLTYPE_INT, "semcnt" , |
197 | SYSCTL_DESCR("Current number of semaphores" ), |
198 | NULL, 0, &nsems, 0, |
199 | CTL_CREATE, CTL_EOL); |
200 | |
201 | return error; |
202 | } |
203 | |
204 | static int |
205 | ksem_sysfini(bool interface) |
206 | { |
207 | int error; |
208 | |
209 | if (interface) { |
210 | error = syscall_disestablish(NULL, ksem_syscalls); |
211 | if (error != 0) { |
212 | return error; |
213 | } |
214 | /* |
215 | * Make sure that no semaphores are in use. Note: semops |
216 | * must be unused at this point. |
217 | */ |
218 | if (nsems_total) { |
219 | error = syscall_establish(NULL, ksem_syscalls); |
220 | KASSERT(error == 0); |
221 | return EBUSY; |
222 | } |
223 | } |
224 | kauth_unlisten_scope(ksem_listener); |
225 | mutex_destroy(&ksem_lock); |
226 | sysctl_teardown(&ksem_clog); |
227 | return 0; |
228 | } |
229 | |
230 | static int |
231 | ksem_modcmd(modcmd_t cmd, void *arg) |
232 | { |
233 | |
234 | switch (cmd) { |
235 | case MODULE_CMD_INIT: |
236 | return ksem_sysinit(); |
237 | |
238 | case MODULE_CMD_FINI: |
239 | return ksem_sysfini(true); |
240 | |
241 | default: |
242 | return ENOTTY; |
243 | } |
244 | } |
245 | |
246 | static ksem_t * |
247 | ksem_lookup(const char *name) |
248 | { |
249 | ksem_t *ks; |
250 | |
251 | KASSERT(mutex_owned(&ksem_lock)); |
252 | |
253 | LIST_FOREACH(ks, &ksem_head, ks_entry) { |
254 | if (strcmp(ks->ks_name, name) == 0) { |
255 | mutex_enter(&ks->ks_lock); |
256 | return ks; |
257 | } |
258 | } |
259 | return NULL; |
260 | } |
261 | |
262 | static int |
263 | ksem_perm(lwp_t *l, ksem_t *ks) |
264 | { |
265 | kauth_cred_t uc = l->l_cred; |
266 | |
267 | KASSERT(mutex_owned(&ks->ks_lock)); |
268 | |
269 | if (kauth_authorize_system(uc, KAUTH_SYSTEM_SEMAPHORE, 0, ks, NULL, NULL) != 0) |
270 | return EACCES; |
271 | |
272 | return 0; |
273 | } |
274 | |
275 | /* |
276 | * ksem_get: get the semaphore from the descriptor. |
277 | * |
278 | * => locks the semaphore, if found. |
279 | * => holds a reference on the file descriptor. |
280 | */ |
281 | static int |
282 | ksem_get(int fd, ksem_t **ksret) |
283 | { |
284 | ksem_t *ks; |
285 | file_t *fp; |
286 | |
287 | fp = fd_getfile(fd); |
288 | if (__predict_false(fp == NULL)) |
289 | return EINVAL; |
290 | if (__predict_false(fp->f_type != DTYPE_SEM)) { |
291 | fd_putfile(fd); |
292 | return EINVAL; |
293 | } |
294 | ks = fp->f_ksem; |
295 | mutex_enter(&ks->ks_lock); |
296 | |
297 | *ksret = ks; |
298 | return 0; |
299 | } |
300 | |
301 | /* |
302 | * ksem_create: allocate and setup a new semaphore structure. |
303 | */ |
304 | static int |
305 | ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val) |
306 | { |
307 | ksem_t *ks; |
308 | kauth_cred_t uc; |
309 | char *kname; |
310 | size_t len; |
311 | |
312 | /* Pre-check for the limit. */ |
313 | if (nsems >= ksem_max) { |
314 | return ENFILE; |
315 | } |
316 | |
317 | if (val > SEM_VALUE_MAX) { |
318 | return EINVAL; |
319 | } |
320 | |
321 | if (name != NULL) { |
322 | len = strlen(name); |
323 | if (len > SEM_MAX_NAMELEN) { |
324 | return ENAMETOOLONG; |
325 | } |
326 | /* Name must start with a '/' but not contain one. */ |
327 | if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) { |
328 | return EINVAL; |
329 | } |
330 | kname = kmem_alloc(++len, KM_SLEEP); |
331 | strlcpy(kname, name, len); |
332 | } else { |
333 | kname = NULL; |
334 | len = 0; |
335 | } |
336 | |
337 | if (atomic_inc_uint_nv(&l->l_proc->p_nsems) > SEM_NSEMS_MAX) { |
338 | atomic_dec_uint(&l->l_proc->p_nsems); |
339 | if (kname != NULL) |
340 | kmem_free(kname, len); |
341 | return -1; |
342 | } |
343 | |
344 | ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP); |
345 | mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE); |
346 | cv_init(&ks->ks_cv, "psem" ); |
347 | ks->ks_name = kname; |
348 | ks->ks_namelen = len; |
349 | ks->ks_mode = mode; |
350 | ks->ks_value = val; |
351 | ks->ks_ref = 1; |
352 | |
353 | uc = l->l_cred; |
354 | ks->ks_uid = kauth_cred_geteuid(uc); |
355 | ks->ks_gid = kauth_cred_getegid(uc); |
356 | |
357 | atomic_inc_uint(&nsems_total); |
358 | *ksret = ks; |
359 | return 0; |
360 | } |
361 | |
362 | static void |
363 | ksem_free(ksem_t *ks) |
364 | { |
365 | |
366 | KASSERT(!cv_has_waiters(&ks->ks_cv)); |
367 | |
368 | if (ks->ks_name) { |
369 | KASSERT(ks->ks_namelen > 0); |
370 | kmem_free(ks->ks_name, ks->ks_namelen); |
371 | } |
372 | mutex_destroy(&ks->ks_lock); |
373 | cv_destroy(&ks->ks_cv); |
374 | kmem_free(ks, sizeof(ksem_t)); |
375 | |
376 | atomic_dec_uint(&nsems_total); |
377 | atomic_dec_uint(&curproc->p_nsems); |
378 | } |
379 | |
380 | int |
381 | sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap, |
382 | register_t *retval) |
383 | { |
384 | /* { |
385 | unsigned int value; |
386 | intptr_t *idp; |
387 | } */ |
388 | |
389 | return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout); |
390 | } |
391 | |
392 | int |
393 | do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout) |
394 | { |
395 | proc_t *p = l->l_proc; |
396 | ksem_t *ks; |
397 | file_t *fp; |
398 | intptr_t id; |
399 | int fd, error; |
400 | |
401 | error = fd_allocfile(&fp, &fd); |
402 | if (error) { |
403 | return error; |
404 | } |
405 | fp->f_type = DTYPE_SEM; |
406 | fp->f_flag = FREAD | FWRITE; |
407 | fp->f_ops = &semops; |
408 | |
409 | id = (intptr_t)fd; |
410 | error = (*docopyout)(&id, idp, sizeof(*idp)); |
411 | if (error) { |
412 | fd_abort(p, fp, fd); |
413 | return error; |
414 | } |
415 | |
416 | /* Note the mode does not matter for anonymous semaphores. */ |
417 | error = ksem_create(l, NULL, &ks, 0, val); |
418 | if (error) { |
419 | fd_abort(p, fp, fd); |
420 | return error; |
421 | } |
422 | fp->f_ksem = ks; |
423 | fd_affix(p, fp, fd); |
424 | return error; |
425 | } |
426 | |
427 | int |
428 | sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap, |
429 | register_t *retval) |
430 | { |
431 | /* { |
432 | const char *name; |
433 | int oflag; |
434 | mode_t mode; |
435 | unsigned int value; |
436 | intptr_t *idp; |
437 | } */ |
438 | |
439 | return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag), |
440 | SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout); |
441 | } |
442 | |
443 | int |
444 | do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode, |
445 | unsigned int value, intptr_t *idp, copyout_t docopyout) |
446 | { |
447 | char name[SEM_MAX_NAMELEN + 1]; |
448 | proc_t *p = l->l_proc; |
449 | ksem_t *ksnew = NULL, *ks; |
450 | file_t *fp; |
451 | intptr_t id; |
452 | int fd, error; |
453 | |
454 | error = copyinstr(semname, name, sizeof(name), NULL); |
455 | if (error) { |
456 | return error; |
457 | } |
458 | error = fd_allocfile(&fp, &fd); |
459 | if (error) { |
460 | return error; |
461 | } |
462 | fp->f_type = DTYPE_SEM; |
463 | fp->f_flag = FREAD | FWRITE; |
464 | fp->f_ops = &semops; |
465 | |
466 | /* |
467 | * The ID (file descriptor number) can be stored early. |
468 | * Note that zero is a special value for libpthread. |
469 | */ |
470 | id = (intptr_t)fd; |
471 | error = (*docopyout)(&id, idp, sizeof(*idp)); |
472 | if (error) { |
473 | goto err; |
474 | } |
475 | |
476 | if (oflag & O_CREAT) { |
477 | /* Create a new semaphore. */ |
478 | error = ksem_create(l, name, &ksnew, mode, value); |
479 | if (error) { |
480 | goto err; |
481 | } |
482 | KASSERT(ksnew != NULL); |
483 | } |
484 | |
485 | /* Lookup for a semaphore with such name. */ |
486 | mutex_enter(&ksem_lock); |
487 | ks = ksem_lookup(name); |
488 | if (ks) { |
489 | KASSERT(mutex_owned(&ks->ks_lock)); |
490 | mutex_exit(&ksem_lock); |
491 | |
492 | /* Check for exclusive create. */ |
493 | if (oflag & O_EXCL) { |
494 | mutex_exit(&ks->ks_lock); |
495 | error = EEXIST; |
496 | goto err; |
497 | } |
498 | /* |
499 | * Verify permissions. If we can access it, |
500 | * add the reference of this thread. |
501 | */ |
502 | error = ksem_perm(l, ks); |
503 | if (error == 0) { |
504 | ks->ks_ref++; |
505 | } |
506 | mutex_exit(&ks->ks_lock); |
507 | if (error) { |
508 | goto err; |
509 | } |
510 | } else { |
511 | /* Fail if not found and not creating. */ |
512 | if ((oflag & O_CREAT) == 0) { |
513 | mutex_exit(&ksem_lock); |
514 | KASSERT(ksnew == NULL); |
515 | error = ENOENT; |
516 | goto err; |
517 | } |
518 | |
519 | /* Check for the limit locked. */ |
520 | if (nsems >= ksem_max) { |
521 | mutex_exit(&ksem_lock); |
522 | error = ENFILE; |
523 | goto err; |
524 | } |
525 | |
526 | /* |
527 | * Finally, insert semaphore into the list. |
528 | * Note: it already has the initial reference. |
529 | */ |
530 | ks = ksnew; |
531 | LIST_INSERT_HEAD(&ksem_head, ks, ks_entry); |
532 | nsems++; |
533 | mutex_exit(&ksem_lock); |
534 | |
535 | ksnew = NULL; |
536 | } |
537 | KASSERT(ks != NULL); |
538 | fp->f_ksem = ks; |
539 | fd_affix(p, fp, fd); |
540 | err: |
541 | if (error) { |
542 | fd_abort(p, fp, fd); |
543 | } |
544 | if (ksnew) { |
545 | ksem_free(ksnew); |
546 | } |
547 | return error; |
548 | } |
549 | |
550 | int |
551 | sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap, |
552 | register_t *retval) |
553 | { |
554 | /* { |
555 | intptr_t id; |
556 | } */ |
557 | int fd = (int)SCARG(uap, id); |
558 | |
559 | if (fd_getfile(fd) == NULL) { |
560 | return EBADF; |
561 | } |
562 | return fd_close(fd); |
563 | } |
564 | |
565 | static int |
566 | ksem_read_fop(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred, |
567 | int flags) |
568 | { |
569 | size_t len; |
570 | char *name; |
571 | ksem_t *ks = fp->f_ksem; |
572 | |
573 | mutex_enter(&ks->ks_lock); |
574 | len = ks->ks_namelen; |
575 | name = ks->ks_name; |
576 | mutex_exit(&ks->ks_lock); |
577 | if (name == NULL || len == 0) |
578 | return 0; |
579 | return uiomove(name, len, uio); |
580 | } |
581 | |
582 | static int |
583 | ksem_stat_fop(file_t *fp, struct stat *ub) |
584 | { |
585 | ksem_t *ks = fp->f_ksem; |
586 | |
587 | mutex_enter(&ks->ks_lock); |
588 | |
589 | memset(ub, 0, sizeof(*ub)); |
590 | |
591 | ub->st_mode = ks->ks_mode | ((ks->ks_name && ks->ks_namelen) |
592 | ? _S_IFLNK : _S_IFREG); |
593 | ub->st_uid = ks->ks_uid; |
594 | ub->st_gid = ks->ks_gid; |
595 | ub->st_size = ks->ks_value; |
596 | ub->st_blocks = (ub->st_size) ? 1 : 0; |
597 | ub->st_nlink = ks->ks_ref; |
598 | ub->st_blksize = 4096; |
599 | |
600 | nanotime(&ub->st_atimespec); |
601 | ub->st_mtimespec = ub->st_ctimespec = ub->st_birthtimespec = |
602 | ub->st_atimespec; |
603 | |
604 | /* |
605 | * Left as 0: st_dev, st_ino, st_rdev, st_flags, st_gen. |
606 | * XXX (st_dev, st_ino) should be unique. |
607 | */ |
608 | mutex_exit(&ks->ks_lock); |
609 | return 0; |
610 | } |
611 | |
612 | static int |
613 | ksem_close_fop(file_t *fp) |
614 | { |
615 | ksem_t *ks = fp->f_ksem; |
616 | bool destroy = false; |
617 | |
618 | mutex_enter(&ks->ks_lock); |
619 | KASSERT(ks->ks_ref > 0); |
620 | if (--ks->ks_ref == 0) { |
621 | /* |
622 | * Destroy if the last reference and semaphore is unnamed, |
623 | * or unlinked (for named semaphore). |
624 | */ |
625 | destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL); |
626 | } |
627 | mutex_exit(&ks->ks_lock); |
628 | |
629 | if (destroy) { |
630 | ksem_free(ks); |
631 | } |
632 | return 0; |
633 | } |
634 | |
635 | int |
636 | sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap, |
637 | register_t *retval) |
638 | { |
639 | /* { |
640 | const char *name; |
641 | } */ |
642 | char name[SEM_MAX_NAMELEN + 1]; |
643 | ksem_t *ks; |
644 | u_int refcnt; |
645 | int error; |
646 | |
647 | error = copyinstr(SCARG(uap, name), name, sizeof(name), NULL); |
648 | if (error) |
649 | return error; |
650 | |
651 | mutex_enter(&ksem_lock); |
652 | ks = ksem_lookup(name); |
653 | if (ks == NULL) { |
654 | mutex_exit(&ksem_lock); |
655 | return ENOENT; |
656 | } |
657 | KASSERT(mutex_owned(&ks->ks_lock)); |
658 | |
659 | /* Verify permissions. */ |
660 | error = ksem_perm(l, ks); |
661 | if (error) { |
662 | mutex_exit(&ks->ks_lock); |
663 | mutex_exit(&ksem_lock); |
664 | return error; |
665 | } |
666 | |
667 | /* Remove from the global list. */ |
668 | LIST_REMOVE(ks, ks_entry); |
669 | nsems--; |
670 | mutex_exit(&ksem_lock); |
671 | |
672 | refcnt = ks->ks_ref; |
673 | if (refcnt) { |
674 | /* Mark as unlinked, if there are references. */ |
675 | ks->ks_flags |= KS_UNLINKED; |
676 | } |
677 | mutex_exit(&ks->ks_lock); |
678 | |
679 | if (refcnt == 0) { |
680 | ksem_free(ks); |
681 | } |
682 | return 0; |
683 | } |
684 | |
685 | int |
686 | sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap, |
687 | register_t *retval) |
688 | { |
689 | /* { |
690 | intptr_t id; |
691 | } */ |
692 | int fd = (int)SCARG(uap, id), error; |
693 | ksem_t *ks; |
694 | |
695 | error = ksem_get(fd, &ks); |
696 | if (error) { |
697 | return error; |
698 | } |
699 | KASSERT(mutex_owned(&ks->ks_lock)); |
700 | if (ks->ks_value == SEM_VALUE_MAX) { |
701 | error = EOVERFLOW; |
702 | goto out; |
703 | } |
704 | ks->ks_value++; |
705 | if (ks->ks_waiters) { |
706 | cv_broadcast(&ks->ks_cv); |
707 | } |
708 | out: |
709 | mutex_exit(&ks->ks_lock); |
710 | fd_putfile(fd); |
711 | return error; |
712 | } |
713 | |
714 | int |
715 | do_ksem_wait(lwp_t *l, intptr_t id, bool try_p, struct timespec *abstime) |
716 | { |
717 | int fd = (int)id, error, timeo; |
718 | ksem_t *ks; |
719 | |
720 | error = ksem_get(fd, &ks); |
721 | if (error) { |
722 | return error; |
723 | } |
724 | KASSERT(mutex_owned(&ks->ks_lock)); |
725 | while (ks->ks_value == 0) { |
726 | ks->ks_waiters++; |
727 | if (!try_p && abstime != NULL) { |
728 | error = ts2timo(CLOCK_REALTIME, TIMER_ABSTIME, abstime, |
729 | &timeo, NULL); |
730 | if (error != 0) |
731 | goto out; |
732 | } else { |
733 | timeo = 0; |
734 | } |
735 | error = try_p ? EAGAIN : cv_timedwait_sig(&ks->ks_cv, |
736 | &ks->ks_lock, timeo); |
737 | ks->ks_waiters--; |
738 | if (error) |
739 | goto out; |
740 | } |
741 | ks->ks_value--; |
742 | out: |
743 | mutex_exit(&ks->ks_lock); |
744 | fd_putfile(fd); |
745 | return error; |
746 | } |
747 | |
748 | int |
749 | sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap, |
750 | register_t *retval) |
751 | { |
752 | /* { |
753 | intptr_t id; |
754 | } */ |
755 | |
756 | return do_ksem_wait(l, SCARG(uap, id), false, NULL); |
757 | } |
758 | |
759 | int |
760 | sys__ksem_timedwait(struct lwp *l, const struct sys__ksem_timedwait_args *uap, |
761 | register_t *retval) |
762 | { |
763 | /* { |
764 | intptr_t id; |
765 | const struct timespec *abstime; |
766 | } */ |
767 | struct timespec ts; |
768 | int error; |
769 | |
770 | error = copyin(SCARG(uap, abstime), &ts, sizeof(ts)); |
771 | if (error != 0) |
772 | return error; |
773 | |
774 | if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000) |
775 | return EINVAL; |
776 | |
777 | error = do_ksem_wait(l, SCARG(uap, id), false, &ts); |
778 | if (error == EWOULDBLOCK) |
779 | error = ETIMEDOUT; |
780 | return error; |
781 | } |
782 | |
783 | int |
784 | sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap, |
785 | register_t *retval) |
786 | { |
787 | /* { |
788 | intptr_t id; |
789 | } */ |
790 | |
791 | return do_ksem_wait(l, SCARG(uap, id), true, NULL); |
792 | } |
793 | |
794 | int |
795 | sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap, |
796 | register_t *retval) |
797 | { |
798 | /* { |
799 | intptr_t id; |
800 | unsigned int *value; |
801 | } */ |
802 | int fd = (int)SCARG(uap, id), error; |
803 | ksem_t *ks; |
804 | unsigned int val; |
805 | |
806 | error = ksem_get(fd, &ks); |
807 | if (error) { |
808 | return error; |
809 | } |
810 | KASSERT(mutex_owned(&ks->ks_lock)); |
811 | val = ks->ks_value; |
812 | mutex_exit(&ks->ks_lock); |
813 | fd_putfile(fd); |
814 | |
815 | return copyout(&val, SCARG(uap, value), sizeof(val)); |
816 | } |
817 | |
818 | int |
819 | sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap, |
820 | register_t *retval) |
821 | { |
822 | /* { |
823 | intptr_t id; |
824 | } */ |
825 | int fd = (int)SCARG(uap, id), error; |
826 | ksem_t *ks; |
827 | |
828 | error = ksem_get(fd, &ks); |
829 | if (error) { |
830 | return error; |
831 | } |
832 | KASSERT(mutex_owned(&ks->ks_lock)); |
833 | |
834 | /* Operation is only for unnamed semaphores. */ |
835 | if (ks->ks_name != NULL) { |
836 | error = EINVAL; |
837 | goto out; |
838 | } |
839 | /* Cannot destroy if there are waiters. */ |
840 | if (ks->ks_waiters) { |
841 | error = EBUSY; |
842 | goto out; |
843 | } |
844 | out: |
845 | mutex_exit(&ks->ks_lock); |
846 | if (error) { |
847 | fd_putfile(fd); |
848 | return error; |
849 | } |
850 | return fd_close(fd); |
851 | } |
852 | |