1 1.62 riastrad /* $NetBSD: uipc_sem.c,v 1.62 2024/12/06 18:44:00 riastradh Exp $ */ 2 1.3 thorpej 3 1.3 thorpej /*- 4 1.52 thorpej * Copyright (c) 2011, 2019 The NetBSD Foundation, Inc. 5 1.3 thorpej * All rights reserved. 6 1.3 thorpej * 7 1.3 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.52 thorpej * by Mindaugas Rasiukevicius and Jason R. Thorpe. 9 1.3 thorpej * 10 1.3 thorpej * Redistribution and use in source and binary forms, with or without 11 1.3 thorpej * modification, are permitted provided that the following conditions 12 1.3 thorpej * are met: 13 1.3 thorpej * 1. Redistributions of source code must retain the above copyright 14 1.3 thorpej * notice, this list of conditions and the following disclaimer. 15 1.3 thorpej * 2. Redistributions in binary form must reproduce the above copyright 16 1.3 thorpej * notice, this list of conditions and the following disclaimer in the 17 1.3 thorpej * documentation and/or other materials provided with the distribution. 18 1.3 thorpej * 19 1.3 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.3 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.3 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.3 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.3 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.3 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.3 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.3 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.3 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.3 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.3 thorpej * POSSIBILITY OF SUCH DAMAGE. 30 1.3 thorpej */ 31 1.1 christos 32 1.1 christos /* 33 1.1 christos * Copyright (c) 2002 Alfred Perlstein <alfred (at) FreeBSD.org> 34 1.1 christos * All rights reserved. 35 1.1 christos * 36 1.1 christos * Redistribution and use in source and binary forms, with or without 37 1.1 christos * modification, are permitted provided that the following conditions 38 1.1 christos * are met: 39 1.1 christos * 1. Redistributions of source code must retain the above copyright 40 1.1 christos * notice, this list of conditions and the following disclaimer. 41 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 42 1.1 christos * notice, this list of conditions and the following disclaimer in the 43 1.1 christos * documentation and/or other materials provided with the distribution. 44 1.1 christos * 45 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 1.1 christos * SUCH DAMAGE. 56 1.1 christos */ 57 1.9 lukem 58 1.30 rmind /* 59 1.30 rmind * Implementation of POSIX semaphore. 60 1.30 rmind */ 61 1.30 rmind 62 1.9 lukem #include <sys/cdefs.h> 63 1.62 riastrad __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.62 2024/12/06 18:44:00 riastradh Exp $"); 64 1.1 christos 65 1.1 christos #include <sys/param.h> 66 1.61 riastrad #include <sys/types.h> 67 1.34 rmind 68 1.34 rmind #include <sys/atomic.h> 69 1.61 riastrad #include <sys/cprng.h> 70 1.1 christos #include <sys/fcntl.h> 71 1.30 rmind #include <sys/file.h> 72 1.30 rmind #include <sys/filedesc.h> 73 1.14 elad #include <sys/kauth.h> 74 1.61 riastrad #include <sys/kernel.h> 75 1.61 riastrad #include <sys/kmem.h> 76 1.61 riastrad #include <sys/ksem.h> 77 1.61 riastrad #include <sys/lwp.h> 78 1.27 ad #include <sys/module.h> 79 1.1 christos #include <sys/mount.h> 80 1.52 thorpej #include <sys/mutex.h> 81 1.61 riastrad #include <sys/proc.h> 82 1.52 thorpej #include <sys/rwlock.h> 83 1.62 riastrad #include <sys/sdt.h> 84 1.45 dholland #include <sys/semaphore.h> 85 1.61 riastrad #include <sys/stat.h> 86 1.27 ad #include <sys/syscall.h> 87 1.1 christos #include <sys/syscallargs.h> 88 1.27 ad #include <sys/syscallvar.h> 89 1.43 pgoyette #include <sys/sysctl.h> 90 1.55 christos #include <sys/uidinfo.h> 91 1.1 christos 92 1.30 rmind MODULE(MODULE_CLASS_MISC, ksem, NULL); 93 1.30 rmind 94 1.49 christos #define SEM_MAX_NAMELEN NAME_MAX 95 1.1 christos 96 1.30 rmind #define KS_UNLINKED 0x01 97 1.4 thorpej 98 1.30 rmind static kmutex_t ksem_lock __cacheline_aligned; 99 1.30 rmind static LIST_HEAD(,ksem) ksem_head __cacheline_aligned; 100 1.34 rmind static u_int nsems_total __cacheline_aligned; 101 1.30 rmind static u_int nsems __cacheline_aligned; 102 1.30 rmind 103 1.52 thorpej static krwlock_t ksem_pshared_lock __cacheline_aligned; 104 1.52 thorpej static LIST_HEAD(, ksem) *ksem_pshared_hashtab __cacheline_aligned; 105 1.52 thorpej static u_long ksem_pshared_hashmask __read_mostly; 106 1.52 thorpej 107 1.52 thorpej #define KSEM_PSHARED_HASHSIZE 32 108 1.52 thorpej 109 1.38 elad static kauth_listener_t ksem_listener; 110 1.38 elad 111 1.30 rmind static int ksem_sysinit(void); 112 1.30 rmind static int ksem_sysfini(bool); 113 1.30 rmind static int ksem_modcmd(modcmd_t, void *); 114 1.59 riastrad static void ksem_release(ksem_t *, int); 115 1.30 rmind static int ksem_close_fop(file_t *); 116 1.39 christos static int ksem_stat_fop(file_t *, struct stat *); 117 1.39 christos static int ksem_read_fop(file_t *, off_t *, struct uio *, 118 1.39 christos kauth_cred_t, int); 119 1.30 rmind 120 1.30 rmind static const struct fileops semops = { 121 1.48 christos .fo_name = "sem", 122 1.39 christos .fo_read = ksem_read_fop, 123 1.30 rmind .fo_write = fbadop_write, 124 1.30 rmind .fo_ioctl = fbadop_ioctl, 125 1.30 rmind .fo_fcntl = fnullop_fcntl, 126 1.30 rmind .fo_poll = fnullop_poll, 127 1.39 christos .fo_stat = ksem_stat_fop, 128 1.30 rmind .fo_close = ksem_close_fop, 129 1.30 rmind .fo_kqfilter = fnullop_kqfilter, 130 1.30 rmind .fo_restart = fnullop_restart, 131 1.30 rmind }; 132 1.27 ad 133 1.27 ad static const struct syscall_package ksem_syscalls[] = { 134 1.27 ad { SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init }, 135 1.27 ad { SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open }, 136 1.27 ad { SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink }, 137 1.27 ad { SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close }, 138 1.27 ad { SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post }, 139 1.27 ad { SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait }, 140 1.27 ad { SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait }, 141 1.27 ad { SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue }, 142 1.27 ad { SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy }, 143 1.36 joerg { SYS__ksem_timedwait, 0, (sy_call_t *)sys__ksem_timedwait }, 144 1.27 ad { 0, 0, NULL }, 145 1.27 ad }; 146 1.1 christos 147 1.43 pgoyette struct sysctllog *ksem_clog; 148 1.57 ad int ksem_max = KSEM_MAX; 149 1.43 pgoyette 150 1.30 rmind static int 151 1.51 christos name_copyin(const char *uname, char **name) 152 1.51 christos { 153 1.51 christos *name = kmem_alloc(SEM_MAX_NAMELEN, KM_SLEEP); 154 1.51 christos 155 1.51 christos int error = copyinstr(uname, *name, SEM_MAX_NAMELEN, NULL); 156 1.51 christos if (error) 157 1.51 christos kmem_free(*name, SEM_MAX_NAMELEN); 158 1.51 christos 159 1.51 christos return error; 160 1.51 christos } 161 1.51 christos 162 1.51 christos static void 163 1.51 christos name_destroy(char **name) 164 1.51 christos { 165 1.51 christos if (!*name) 166 1.51 christos return; 167 1.51 christos 168 1.51 christos kmem_free(*name, SEM_MAX_NAMELEN); 169 1.51 christos *name = NULL; 170 1.51 christos } 171 1.51 christos 172 1.51 christos static int 173 1.38 elad ksem_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, 174 1.38 elad void *arg0, void *arg1, void *arg2, void *arg3) 175 1.38 elad { 176 1.38 elad ksem_t *ks; 177 1.38 elad mode_t mode; 178 1.38 elad 179 1.38 elad if (action != KAUTH_SYSTEM_SEMAPHORE) 180 1.38 elad return KAUTH_RESULT_DEFER; 181 1.38 elad 182 1.38 elad ks = arg1; 183 1.38 elad mode = ks->ks_mode; 184 1.38 elad 185 1.38 elad if ((kauth_cred_geteuid(cred) == ks->ks_uid && (mode & S_IWUSR) != 0) || 186 1.38 elad (kauth_cred_getegid(cred) == ks->ks_gid && (mode & S_IWGRP) != 0) || 187 1.38 elad (mode & S_IWOTH) != 0) 188 1.38 elad return KAUTH_RESULT_ALLOW; 189 1.38 elad 190 1.38 elad return KAUTH_RESULT_DEFER; 191 1.38 elad } 192 1.38 elad 193 1.38 elad static int 194 1.30 rmind ksem_sysinit(void) 195 1.3 thorpej { 196 1.30 rmind int error; 197 1.43 pgoyette const struct sysctlnode *rnode; 198 1.1 christos 199 1.30 rmind mutex_init(&ksem_lock, MUTEX_DEFAULT, IPL_NONE); 200 1.30 rmind LIST_INIT(&ksem_head); 201 1.34 rmind nsems_total = 0; 202 1.34 rmind nsems = 0; 203 1.20 ad 204 1.52 thorpej rw_init(&ksem_pshared_lock); 205 1.52 thorpej ksem_pshared_hashtab = hashinit(KSEM_PSHARED_HASHSIZE, HASH_LIST, 206 1.52 thorpej true, &ksem_pshared_hashmask); 207 1.52 thorpej KASSERT(ksem_pshared_hashtab != NULL); 208 1.52 thorpej 209 1.38 elad ksem_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 210 1.38 elad ksem_listener_cb, NULL); 211 1.38 elad 212 1.43 pgoyette /* Define module-specific sysctl tree */ 213 1.43 pgoyette 214 1.43 pgoyette ksem_clog = NULL; 215 1.43 pgoyette 216 1.43 pgoyette sysctl_createv(&ksem_clog, 0, NULL, &rnode, 217 1.43 pgoyette CTLFLAG_PERMANENT, 218 1.43 pgoyette CTLTYPE_NODE, "posix", 219 1.43 pgoyette SYSCTL_DESCR("POSIX options"), 220 1.43 pgoyette NULL, 0, NULL, 0, 221 1.43 pgoyette CTL_KERN, CTL_CREATE, CTL_EOL); 222 1.43 pgoyette sysctl_createv(&ksem_clog, 0, &rnode, NULL, 223 1.43 pgoyette CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 224 1.43 pgoyette CTLTYPE_INT, "semmax", 225 1.43 pgoyette SYSCTL_DESCR("Maximal number of semaphores"), 226 1.43 pgoyette NULL, 0, &ksem_max, 0, 227 1.43 pgoyette CTL_CREATE, CTL_EOL); 228 1.43 pgoyette sysctl_createv(&ksem_clog, 0, &rnode, NULL, 229 1.44 pgoyette CTLFLAG_PERMANENT | CTLFLAG_READONLY, 230 1.43 pgoyette CTLTYPE_INT, "semcnt", 231 1.43 pgoyette SYSCTL_DESCR("Current number of semaphores"), 232 1.43 pgoyette NULL, 0, &nsems, 0, 233 1.43 pgoyette CTL_CREATE, CTL_EOL); 234 1.43 pgoyette 235 1.58 ad error = syscall_establish(NULL, ksem_syscalls); 236 1.58 ad if (error) { 237 1.58 ad (void)ksem_sysfini(false); 238 1.58 ad } 239 1.58 ad 240 1.30 rmind return error; 241 1.3 thorpej } 242 1.1 christos 243 1.30 rmind static int 244 1.30 rmind ksem_sysfini(bool interface) 245 1.1 christos { 246 1.30 rmind int error; 247 1.1 christos 248 1.30 rmind if (interface) { 249 1.30 rmind error = syscall_disestablish(NULL, ksem_syscalls); 250 1.30 rmind if (error != 0) { 251 1.30 rmind return error; 252 1.30 rmind } 253 1.34 rmind /* 254 1.34 rmind * Make sure that no semaphores are in use. Note: semops 255 1.34 rmind * must be unused at this point. 256 1.34 rmind */ 257 1.34 rmind if (nsems_total) { 258 1.30 rmind error = syscall_establish(NULL, ksem_syscalls); 259 1.30 rmind KASSERT(error == 0); 260 1.62 riastrad return SET_ERROR(EBUSY); 261 1.30 rmind } 262 1.3 thorpej } 263 1.38 elad kauth_unlisten_scope(ksem_listener); 264 1.52 thorpej hashdone(ksem_pshared_hashtab, HASH_LIST, ksem_pshared_hashmask); 265 1.52 thorpej rw_destroy(&ksem_pshared_lock); 266 1.30 rmind mutex_destroy(&ksem_lock); 267 1.43 pgoyette sysctl_teardown(&ksem_clog); 268 1.30 rmind return 0; 269 1.3 thorpej } 270 1.3 thorpej 271 1.30 rmind static int 272 1.30 rmind ksem_modcmd(modcmd_t cmd, void *arg) 273 1.3 thorpej { 274 1.3 thorpej 275 1.30 rmind switch (cmd) { 276 1.30 rmind case MODULE_CMD_INIT: 277 1.30 rmind return ksem_sysinit(); 278 1.3 thorpej 279 1.30 rmind case MODULE_CMD_FINI: 280 1.30 rmind return ksem_sysfini(true); 281 1.1 christos 282 1.30 rmind default: 283 1.62 riastrad return SET_ERROR(ENOTTY); 284 1.16 thorpej } 285 1.16 thorpej } 286 1.16 thorpej 287 1.30 rmind static ksem_t * 288 1.30 rmind ksem_lookup(const char *name) 289 1.3 thorpej { 290 1.30 rmind ksem_t *ks; 291 1.3 thorpej 292 1.30 rmind KASSERT(mutex_owned(&ksem_lock)); 293 1.3 thorpej 294 1.30 rmind LIST_FOREACH(ks, &ksem_head, ks_entry) { 295 1.30 rmind if (strcmp(ks->ks_name, name) == 0) { 296 1.30 rmind mutex_enter(&ks->ks_lock); 297 1.30 rmind return ks; 298 1.3 thorpej } 299 1.1 christos } 300 1.30 rmind return NULL; 301 1.1 christos } 302 1.1 christos 303 1.3 thorpej static int 304 1.30 rmind ksem_perm(lwp_t *l, ksem_t *ks) 305 1.3 thorpej { 306 1.30 rmind kauth_cred_t uc = l->l_cred; 307 1.3 thorpej 308 1.30 rmind KASSERT(mutex_owned(&ks->ks_lock)); 309 1.30 rmind 310 1.38 elad if (kauth_authorize_system(uc, KAUTH_SYSTEM_SEMAPHORE, 0, ks, NULL, NULL) != 0) 311 1.62 riastrad return SET_ERROR(EACCES); 312 1.38 elad 313 1.38 elad return 0; 314 1.3 thorpej } 315 1.3 thorpej 316 1.30 rmind /* 317 1.52 thorpej * Bits 1..23 are random, just pluck a few of those and assume the 318 1.52 thorpej * distribution is going to be pretty good. 319 1.52 thorpej */ 320 1.52 thorpej #define KSEM_PSHARED_HASH(id) (((id) >> 1) & ksem_pshared_hashmask) 321 1.52 thorpej 322 1.52 thorpej static void 323 1.52 thorpej ksem_remove_pshared(ksem_t *ksem) 324 1.52 thorpej { 325 1.52 thorpej rw_enter(&ksem_pshared_lock, RW_WRITER); 326 1.52 thorpej LIST_REMOVE(ksem, ks_entry); 327 1.52 thorpej rw_exit(&ksem_pshared_lock); 328 1.52 thorpej } 329 1.52 thorpej 330 1.52 thorpej static ksem_t * 331 1.52 thorpej ksem_lookup_pshared_locked(intptr_t id) 332 1.52 thorpej { 333 1.52 thorpej u_long bucket = KSEM_PSHARED_HASH(id); 334 1.52 thorpej ksem_t *ksem = NULL; 335 1.52 thorpej 336 1.52 thorpej /* ksem_t is locked and referenced upon return. */ 337 1.52 thorpej 338 1.52 thorpej LIST_FOREACH(ksem, &ksem_pshared_hashtab[bucket], ks_entry) { 339 1.52 thorpej if (ksem->ks_pshared_id == id) { 340 1.52 thorpej mutex_enter(&ksem->ks_lock); 341 1.52 thorpej if (ksem->ks_pshared_proc == NULL) { 342 1.52 thorpej /* 343 1.52 thorpej * This entry is dead, and in the process 344 1.52 thorpej * of being torn down; skip it. 345 1.52 thorpej */ 346 1.52 thorpej mutex_exit(&ksem->ks_lock); 347 1.52 thorpej continue; 348 1.52 thorpej } 349 1.52 thorpej ksem->ks_ref++; 350 1.52 thorpej KASSERT(ksem->ks_ref != 0); 351 1.52 thorpej return ksem; 352 1.52 thorpej } 353 1.52 thorpej } 354 1.52 thorpej 355 1.52 thorpej return NULL; 356 1.52 thorpej } 357 1.52 thorpej 358 1.52 thorpej static ksem_t * 359 1.52 thorpej ksem_lookup_pshared(intptr_t id) 360 1.52 thorpej { 361 1.52 thorpej rw_enter(&ksem_pshared_lock, RW_READER); 362 1.52 thorpej ksem_t *ksem = ksem_lookup_pshared_locked(id); 363 1.52 thorpej rw_exit(&ksem_pshared_lock); 364 1.52 thorpej return ksem; 365 1.52 thorpej } 366 1.52 thorpej 367 1.52 thorpej static void 368 1.52 thorpej ksem_alloc_pshared_id(ksem_t *ksem) 369 1.52 thorpej { 370 1.59 riastrad ksem_t *ksem0; 371 1.52 thorpej uint32_t try; 372 1.52 thorpej 373 1.52 thorpej KASSERT(ksem->ks_pshared_proc != NULL); 374 1.52 thorpej 375 1.52 thorpej rw_enter(&ksem_pshared_lock, RW_WRITER); 376 1.52 thorpej for (;;) { 377 1.52 thorpej try = (cprng_fast32() & ~KSEM_MARKER_MASK) | 378 1.52 thorpej KSEM_PSHARED_MARKER; 379 1.52 thorpej 380 1.59 riastrad if ((ksem0 = ksem_lookup_pshared_locked(try)) == NULL) { 381 1.52 thorpej /* Got it! */ 382 1.52 thorpej break; 383 1.52 thorpej } 384 1.59 riastrad ksem_release(ksem0, -1); 385 1.52 thorpej } 386 1.52 thorpej ksem->ks_pshared_id = try; 387 1.52 thorpej u_long bucket = KSEM_PSHARED_HASH(ksem->ks_pshared_id); 388 1.52 thorpej LIST_INSERT_HEAD(&ksem_pshared_hashtab[bucket], ksem, ks_entry); 389 1.52 thorpej rw_exit(&ksem_pshared_lock); 390 1.52 thorpej } 391 1.52 thorpej 392 1.52 thorpej /* 393 1.30 rmind * ksem_get: get the semaphore from the descriptor. 394 1.30 rmind * 395 1.52 thorpej * => locks the semaphore, if found, and holds an extra reference. 396 1.30 rmind * => holds a reference on the file descriptor. 397 1.30 rmind */ 398 1.30 rmind static int 399 1.52 thorpej ksem_get(intptr_t id, ksem_t **ksret, int *fdp) 400 1.13 cube { 401 1.30 rmind ksem_t *ks; 402 1.52 thorpej int fd; 403 1.52 thorpej 404 1.52 thorpej if ((id & KSEM_MARKER_MASK) == KSEM_PSHARED_MARKER) { 405 1.52 thorpej /* 406 1.52 thorpej * ksem_lookup_pshared() returns the ksem_t * 407 1.52 thorpej * locked and referenced. 408 1.52 thorpej */ 409 1.52 thorpej ks = ksem_lookup_pshared(id); 410 1.52 thorpej if (ks == NULL) 411 1.62 riastrad return SET_ERROR(EINVAL); 412 1.52 thorpej KASSERT(ks->ks_pshared_id == id); 413 1.52 thorpej KASSERT(ks->ks_pshared_proc != NULL); 414 1.52 thorpej fd = -1; 415 1.52 thorpej } else if (id <= INT_MAX) { 416 1.52 thorpej fd = (int)id; 417 1.52 thorpej file_t *fp = fd_getfile(fd); 418 1.13 cube 419 1.52 thorpej if (__predict_false(fp == NULL)) 420 1.62 riastrad return SET_ERROR(EINVAL); 421 1.52 thorpej if (__predict_false(fp->f_type != DTYPE_SEM)) { 422 1.52 thorpej fd_putfile(fd); 423 1.62 riastrad return SET_ERROR(EINVAL); 424 1.52 thorpej } 425 1.52 thorpej ks = fp->f_ksem; 426 1.52 thorpej mutex_enter(&ks->ks_lock); 427 1.52 thorpej ks->ks_ref++; 428 1.52 thorpej } else { 429 1.62 riastrad return SET_ERROR(EINVAL); 430 1.13 cube } 431 1.13 cube 432 1.30 rmind *ksret = ks; 433 1.52 thorpej *fdp = fd; 434 1.30 rmind return 0; 435 1.1 christos } 436 1.1 christos 437 1.30 rmind /* 438 1.30 rmind * ksem_create: allocate and setup a new semaphore structure. 439 1.30 rmind */ 440 1.1 christos static int 441 1.30 rmind ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val) 442 1.1 christos { 443 1.30 rmind ksem_t *ks; 444 1.14 elad kauth_cred_t uc; 445 1.30 rmind char *kname; 446 1.1 christos size_t len; 447 1.1 christos 448 1.30 rmind /* Pre-check for the limit. */ 449 1.30 rmind if (nsems >= ksem_max) { 450 1.62 riastrad return SET_ERROR(ENFILE); 451 1.30 rmind } 452 1.30 rmind 453 1.30 rmind if (val > SEM_VALUE_MAX) { 454 1.62 riastrad return SET_ERROR(EINVAL); 455 1.30 rmind } 456 1.30 rmind 457 1.1 christos if (name != NULL) { 458 1.1 christos len = strlen(name); 459 1.1 christos if (len > SEM_MAX_NAMELEN) { 460 1.62 riastrad return SET_ERROR(ENAMETOOLONG); 461 1.1 christos } 462 1.30 rmind /* Name must start with a '/' but not contain one. */ 463 1.1 christos if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) { 464 1.62 riastrad return SET_ERROR(EINVAL); 465 1.1 christos } 466 1.30 rmind kname = kmem_alloc(++len, KM_SLEEP); 467 1.30 rmind strlcpy(kname, name, len); 468 1.30 rmind } else { 469 1.30 rmind kname = NULL; 470 1.30 rmind len = 0; 471 1.30 rmind } 472 1.30 rmind 473 1.30 rmind ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP); 474 1.30 rmind mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE); 475 1.30 rmind cv_init(&ks->ks_cv, "psem"); 476 1.30 rmind ks->ks_name = kname; 477 1.30 rmind ks->ks_namelen = len; 478 1.30 rmind ks->ks_mode = mode; 479 1.30 rmind ks->ks_value = val; 480 1.30 rmind ks->ks_ref = 1; 481 1.30 rmind 482 1.30 rmind uc = l->l_cred; 483 1.30 rmind ks->ks_uid = kauth_cred_geteuid(uc); 484 1.30 rmind ks->ks_gid = kauth_cred_getegid(uc); 485 1.60 chs chgsemcnt(ks->ks_uid, 1); 486 1.60 chs atomic_inc_uint(&nsems_total); 487 1.30 rmind 488 1.30 rmind *ksret = ks; 489 1.30 rmind return 0; 490 1.30 rmind } 491 1.30 rmind 492 1.30 rmind static void 493 1.30 rmind ksem_free(ksem_t *ks) 494 1.30 rmind { 495 1.3 thorpej 496 1.34 rmind KASSERT(!cv_has_waiters(&ks->ks_cv)); 497 1.34 rmind 498 1.60 chs chgsemcnt(ks->ks_uid, -1); 499 1.60 chs atomic_dec_uint(&nsems_total); 500 1.60 chs 501 1.52 thorpej if (ks->ks_pshared_id) { 502 1.52 thorpej KASSERT(ks->ks_pshared_proc == NULL); 503 1.52 thorpej ksem_remove_pshared(ks); 504 1.52 thorpej } 505 1.30 rmind if (ks->ks_name) { 506 1.30 rmind KASSERT(ks->ks_namelen > 0); 507 1.30 rmind kmem_free(ks->ks_name, ks->ks_namelen); 508 1.13 cube } 509 1.30 rmind mutex_destroy(&ks->ks_lock); 510 1.30 rmind cv_destroy(&ks->ks_cv); 511 1.30 rmind kmem_free(ks, sizeof(ksem_t)); 512 1.1 christos } 513 1.1 christos 514 1.52 thorpej #define KSEM_ID_IS_PSHARED(id) \ 515 1.52 thorpej (((id) & KSEM_MARKER_MASK) == KSEM_PSHARED_MARKER) 516 1.52 thorpej 517 1.52 thorpej static void 518 1.52 thorpej ksem_release(ksem_t *ksem, int fd) 519 1.52 thorpej { 520 1.52 thorpej bool destroy = false; 521 1.52 thorpej 522 1.52 thorpej KASSERT(mutex_owned(&ksem->ks_lock)); 523 1.52 thorpej 524 1.52 thorpej KASSERT(ksem->ks_ref > 0); 525 1.52 thorpej if (--ksem->ks_ref == 0) { 526 1.52 thorpej /* 527 1.52 thorpej * Destroy if the last reference and semaphore is unnamed, 528 1.52 thorpej * or unlinked (for named semaphore). 529 1.52 thorpej */ 530 1.52 thorpej destroy = (ksem->ks_flags & KS_UNLINKED) || 531 1.52 thorpej (ksem->ks_name == NULL); 532 1.52 thorpej } 533 1.52 thorpej mutex_exit(&ksem->ks_lock); 534 1.52 thorpej 535 1.52 thorpej if (destroy) { 536 1.52 thorpej ksem_free(ksem); 537 1.52 thorpej } 538 1.52 thorpej if (fd != -1) { 539 1.52 thorpej fd_putfile(fd); 540 1.52 thorpej } 541 1.52 thorpej } 542 1.52 thorpej 543 1.1 christos int 544 1.30 rmind sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap, 545 1.30 rmind register_t *retval) 546 1.1 christos { 547 1.23 dsl /* { 548 1.1 christos unsigned int value; 549 1.29 ad intptr_t *idp; 550 1.23 dsl } */ 551 1.13 cube 552 1.52 thorpej return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), 553 1.52 thorpej copyin, copyout); 554 1.13 cube } 555 1.13 cube 556 1.13 cube int 557 1.52 thorpej do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyin_t docopyin, 558 1.52 thorpej copyout_t docopyout) 559 1.13 cube { 560 1.30 rmind proc_t *p = l->l_proc; 561 1.30 rmind ksem_t *ks; 562 1.30 rmind file_t *fp; 563 1.52 thorpej intptr_t id, arg; 564 1.30 rmind int fd, error; 565 1.1 christos 566 1.52 thorpej /* 567 1.52 thorpej * Newer versions of librt / libpthread pass us 'PSRD' in *idp to 568 1.52 thorpej * indicate that a pshared semaphore is wanted. In that case we 569 1.52 thorpej * allocate globally unique ID and return that, rather than the 570 1.52 thorpej * process-scoped file descriptor ID. 571 1.52 thorpej */ 572 1.52 thorpej error = (*docopyin)(idp, &arg, sizeof(*idp)); 573 1.52 thorpej if (error) { 574 1.52 thorpej return error; 575 1.52 thorpej } 576 1.52 thorpej 577 1.30 rmind error = fd_allocfile(&fp, &fd); 578 1.1 christos if (error) { 579 1.30 rmind return error; 580 1.1 christos } 581 1.30 rmind fp->f_type = DTYPE_SEM; 582 1.30 rmind fp->f_flag = FREAD | FWRITE; 583 1.30 rmind fp->f_ops = &semops; 584 1.3 thorpej 585 1.52 thorpej if (fd >= KSEM_MARKER_MIN) { 586 1.52 thorpej /* 587 1.52 thorpej * This is super-unlikely, but we check for it anyway 588 1.52 thorpej * because potential collisions with the pshared marker 589 1.52 thorpej * would be bad. 590 1.52 thorpej */ 591 1.52 thorpej fd_abort(p, fp, fd); 592 1.62 riastrad return SET_ERROR(EMFILE); 593 1.52 thorpej } 594 1.52 thorpej 595 1.52 thorpej /* Note the mode does not matter for anonymous semaphores. */ 596 1.52 thorpej error = ksem_create(l, NULL, &ks, 0, val); 597 1.30 rmind if (error) { 598 1.30 rmind fd_abort(p, fp, fd); 599 1.30 rmind return error; 600 1.30 rmind } 601 1.3 thorpej 602 1.52 thorpej if (arg == KSEM_PSHARED) { 603 1.52 thorpej ks->ks_pshared_proc = curproc; 604 1.52 thorpej ks->ks_pshared_fd = fd; 605 1.52 thorpej ksem_alloc_pshared_id(ks); 606 1.52 thorpej id = ks->ks_pshared_id; 607 1.52 thorpej } else { 608 1.52 thorpej id = (intptr_t)fd; 609 1.52 thorpej } 610 1.52 thorpej 611 1.52 thorpej error = (*docopyout)(&id, idp, sizeof(*idp)); 612 1.30 rmind if (error) { 613 1.52 thorpej ksem_free(ks); 614 1.30 rmind fd_abort(p, fp, fd); 615 1.30 rmind return error; 616 1.30 rmind } 617 1.52 thorpej 618 1.42 matt fp->f_ksem = ks; 619 1.30 rmind fd_affix(p, fp, fd); 620 1.30 rmind return error; 621 1.1 christos } 622 1.1 christos 623 1.1 christos int 624 1.30 rmind sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap, 625 1.30 rmind register_t *retval) 626 1.1 christos { 627 1.23 dsl /* { 628 1.1 christos const char *name; 629 1.1 christos int oflag; 630 1.1 christos mode_t mode; 631 1.1 christos unsigned int value; 632 1.29 ad intptr_t *idp; 633 1.23 dsl } */ 634 1.13 cube 635 1.13 cube return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag), 636 1.13 cube SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout); 637 1.13 cube } 638 1.13 cube 639 1.13 cube int 640 1.13 cube do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode, 641 1.29 ad unsigned int value, intptr_t *idp, copyout_t docopyout) 642 1.13 cube { 643 1.51 christos char *name; 644 1.30 rmind proc_t *p = l->l_proc; 645 1.30 rmind ksem_t *ksnew = NULL, *ks; 646 1.30 rmind file_t *fp; 647 1.29 ad intptr_t id; 648 1.30 rmind int fd, error; 649 1.1 christos 650 1.51 christos error = name_copyin(semname, &name); 651 1.30 rmind if (error) { 652 1.30 rmind return error; 653 1.30 rmind } 654 1.30 rmind error = fd_allocfile(&fp, &fd); 655 1.30 rmind if (error) { 656 1.51 christos name_destroy(&name); 657 1.30 rmind return error; 658 1.30 rmind } 659 1.30 rmind fp->f_type = DTYPE_SEM; 660 1.30 rmind fp->f_flag = FREAD | FWRITE; 661 1.30 rmind fp->f_ops = &semops; 662 1.30 rmind 663 1.52 thorpej if (fd >= KSEM_MARKER_MIN) { 664 1.52 thorpej /* 665 1.52 thorpej * This is super-unlikely, but we check for it anyway 666 1.52 thorpej * because potential collisions with the pshared marker 667 1.52 thorpej * would be bad. 668 1.52 thorpej */ 669 1.52 thorpej fd_abort(p, fp, fd); 670 1.62 riastrad return SET_ERROR(EMFILE); 671 1.52 thorpej } 672 1.52 thorpej 673 1.30 rmind /* 674 1.30 rmind * The ID (file descriptor number) can be stored early. 675 1.30 rmind * Note that zero is a special value for libpthread. 676 1.30 rmind */ 677 1.30 rmind id = (intptr_t)fd; 678 1.30 rmind error = (*docopyout)(&id, idp, sizeof(*idp)); 679 1.30 rmind if (error) { 680 1.30 rmind goto err; 681 1.30 rmind } 682 1.30 rmind 683 1.30 rmind if (oflag & O_CREAT) { 684 1.30 rmind /* Create a new semaphore. */ 685 1.30 rmind error = ksem_create(l, name, &ksnew, mode, value); 686 1.30 rmind if (error) { 687 1.30 rmind goto err; 688 1.30 rmind } 689 1.30 rmind KASSERT(ksnew != NULL); 690 1.30 rmind } 691 1.1 christos 692 1.30 rmind /* Lookup for a semaphore with such name. */ 693 1.30 rmind mutex_enter(&ksem_lock); 694 1.30 rmind ks = ksem_lookup(name); 695 1.51 christos name_destroy(&name); 696 1.30 rmind if (ks) { 697 1.30 rmind KASSERT(mutex_owned(&ks->ks_lock)); 698 1.30 rmind mutex_exit(&ksem_lock); 699 1.3 thorpej 700 1.3 thorpej /* Check for exclusive create. */ 701 1.13 cube if (oflag & O_EXCL) { 702 1.30 rmind mutex_exit(&ks->ks_lock); 703 1.62 riastrad error = SET_ERROR(EEXIST); 704 1.30 rmind goto err; 705 1.1 christos } 706 1.1 christos /* 707 1.30 rmind * Verify permissions. If we can access it, 708 1.30 rmind * add the reference of this thread. 709 1.1 christos */ 710 1.15 ad error = ksem_perm(l, ks); 711 1.30 rmind if (error == 0) { 712 1.30 rmind ks->ks_ref++; 713 1.30 rmind } 714 1.30 rmind mutex_exit(&ks->ks_lock); 715 1.1 christos if (error) { 716 1.30 rmind goto err; 717 1.30 rmind } 718 1.30 rmind } else { 719 1.30 rmind /* Fail if not found and not creating. */ 720 1.30 rmind if ((oflag & O_CREAT) == 0) { 721 1.30 rmind mutex_exit(&ksem_lock); 722 1.30 rmind KASSERT(ksnew == NULL); 723 1.62 riastrad error = SET_ERROR(ENOENT); 724 1.31 rmind goto err; 725 1.1 christos } 726 1.3 thorpej 727 1.30 rmind /* Check for the limit locked. */ 728 1.30 rmind if (nsems >= ksem_max) { 729 1.30 rmind mutex_exit(&ksem_lock); 730 1.62 riastrad error = SET_ERROR(ENFILE); 731 1.30 rmind goto err; 732 1.30 rmind } 733 1.3 thorpej 734 1.30 rmind /* 735 1.32 rmind * Finally, insert semaphore into the list. 736 1.30 rmind * Note: it already has the initial reference. 737 1.30 rmind */ 738 1.30 rmind ks = ksnew; 739 1.30 rmind LIST_INSERT_HEAD(&ksem_head, ks, ks_entry); 740 1.30 rmind nsems++; 741 1.30 rmind mutex_exit(&ksem_lock); 742 1.30 rmind 743 1.30 rmind ksnew = NULL; 744 1.30 rmind } 745 1.30 rmind KASSERT(ks != NULL); 746 1.42 matt fp->f_ksem = ks; 747 1.30 rmind fd_affix(p, fp, fd); 748 1.30 rmind err: 749 1.51 christos name_destroy(&name); 750 1.30 rmind if (error) { 751 1.30 rmind fd_abort(p, fp, fd); 752 1.3 thorpej } 753 1.30 rmind if (ksnew) { 754 1.30 rmind ksem_free(ksnew); 755 1.1 christos } 756 1.30 rmind return error; 757 1.30 rmind } 758 1.1 christos 759 1.30 rmind int 760 1.30 rmind sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap, 761 1.30 rmind register_t *retval) 762 1.30 rmind { 763 1.30 rmind /* { 764 1.30 rmind intptr_t id; 765 1.30 rmind } */ 766 1.52 thorpej intptr_t id = SCARG(uap, id); 767 1.52 thorpej int fd, error; 768 1.52 thorpej ksem_t *ks; 769 1.52 thorpej 770 1.52 thorpej error = ksem_get(id, &ks, &fd); 771 1.52 thorpej if (error) { 772 1.52 thorpej return error; 773 1.52 thorpej } 774 1.33 rmind 775 1.52 thorpej /* This is only for named semaphores. */ 776 1.52 thorpej if (ks->ks_name == NULL) { 777 1.62 riastrad error = SET_ERROR(EINVAL); 778 1.52 thorpej } 779 1.52 thorpej ksem_release(ks, -1); 780 1.52 thorpej if (error) { 781 1.52 thorpej if (fd != -1) 782 1.52 thorpej fd_putfile(fd); 783 1.52 thorpej return error; 784 1.33 rmind } 785 1.33 rmind return fd_close(fd); 786 1.1 christos } 787 1.1 christos 788 1.30 rmind static int 789 1.39 christos ksem_read_fop(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred, 790 1.39 christos int flags) 791 1.39 christos { 792 1.39 christos size_t len; 793 1.39 christos char *name; 794 1.42 matt ksem_t *ks = fp->f_ksem; 795 1.39 christos 796 1.39 christos mutex_enter(&ks->ks_lock); 797 1.39 christos len = ks->ks_namelen; 798 1.39 christos name = ks->ks_name; 799 1.39 christos mutex_exit(&ks->ks_lock); 800 1.39 christos if (name == NULL || len == 0) 801 1.39 christos return 0; 802 1.39 christos return uiomove(name, len, uio); 803 1.39 christos } 804 1.39 christos 805 1.39 christos static int 806 1.39 christos ksem_stat_fop(file_t *fp, struct stat *ub) 807 1.39 christos { 808 1.42 matt ksem_t *ks = fp->f_ksem; 809 1.39 christos 810 1.39 christos mutex_enter(&ks->ks_lock); 811 1.39 christos 812 1.39 christos memset(ub, 0, sizeof(*ub)); 813 1.39 christos 814 1.39 christos ub->st_mode = ks->ks_mode | ((ks->ks_name && ks->ks_namelen) 815 1.39 christos ? _S_IFLNK : _S_IFREG); 816 1.39 christos ub->st_uid = ks->ks_uid; 817 1.39 christos ub->st_gid = ks->ks_gid; 818 1.39 christos ub->st_size = ks->ks_value; 819 1.39 christos ub->st_blocks = (ub->st_size) ? 1 : 0; 820 1.39 christos ub->st_nlink = ks->ks_ref; 821 1.39 christos ub->st_blksize = 4096; 822 1.39 christos 823 1.39 christos nanotime(&ub->st_atimespec); 824 1.39 christos ub->st_mtimespec = ub->st_ctimespec = ub->st_birthtimespec = 825 1.39 christos ub->st_atimespec; 826 1.39 christos 827 1.39 christos /* 828 1.39 christos * Left as 0: st_dev, st_ino, st_rdev, st_flags, st_gen. 829 1.39 christos * XXX (st_dev, st_ino) should be unique. 830 1.39 christos */ 831 1.39 christos mutex_exit(&ks->ks_lock); 832 1.39 christos return 0; 833 1.39 christos } 834 1.39 christos 835 1.39 christos static int 836 1.30 rmind ksem_close_fop(file_t *fp) 837 1.1 christos { 838 1.42 matt ksem_t *ks = fp->f_ksem; 839 1.1 christos 840 1.53 thorpej mutex_enter(&ks->ks_lock); 841 1.53 thorpej 842 1.53 thorpej if (ks->ks_pshared_id) { 843 1.53 thorpej if (ks->ks_pshared_proc != curproc) { 844 1.53 thorpej /* Do nothing if this is not the creator. */ 845 1.53 thorpej mutex_exit(&ks->ks_lock); 846 1.53 thorpej return 0; 847 1.53 thorpej } 848 1.53 thorpej /* Mark this semaphore as dead. */ 849 1.53 thorpej ks->ks_pshared_proc = NULL; 850 1.1 christos } 851 1.3 thorpej 852 1.52 thorpej ksem_release(ks, -1); 853 1.30 rmind return 0; 854 1.1 christos } 855 1.1 christos 856 1.1 christos int 857 1.30 rmind sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap, 858 1.30 rmind register_t *retval) 859 1.1 christos { 860 1.23 dsl /* { 861 1.1 christos const char *name; 862 1.23 dsl } */ 863 1.51 christos char *name; 864 1.30 rmind ksem_t *ks; 865 1.30 rmind u_int refcnt; 866 1.1 christos int error; 867 1.1 christos 868 1.51 christos error = name_copyin(SCARG(uap, name), &name); 869 1.1 christos if (error) 870 1.1 christos return error; 871 1.1 christos 872 1.30 rmind mutex_enter(&ksem_lock); 873 1.30 rmind ks = ksem_lookup(name); 874 1.51 christos name_destroy(&name); 875 1.3 thorpej if (ks == NULL) { 876 1.30 rmind mutex_exit(&ksem_lock); 877 1.62 riastrad return SET_ERROR(ENOENT); 878 1.1 christos } 879 1.30 rmind KASSERT(mutex_owned(&ks->ks_lock)); 880 1.3 thorpej 881 1.30 rmind /* Verify permissions. */ 882 1.30 rmind error = ksem_perm(l, ks); 883 1.30 rmind if (error) { 884 1.30 rmind mutex_exit(&ks->ks_lock); 885 1.30 rmind mutex_exit(&ksem_lock); 886 1.30 rmind return error; 887 1.30 rmind } 888 1.3 thorpej 889 1.31 rmind /* Remove from the global list. */ 890 1.3 thorpej LIST_REMOVE(ks, ks_entry); 891 1.30 rmind nsems--; 892 1.31 rmind mutex_exit(&ksem_lock); 893 1.3 thorpej 894 1.30 rmind refcnt = ks->ks_ref; 895 1.30 rmind if (refcnt) { 896 1.30 rmind /* Mark as unlinked, if there are references. */ 897 1.30 rmind ks->ks_flags |= KS_UNLINKED; 898 1.30 rmind } 899 1.30 rmind mutex_exit(&ks->ks_lock); 900 1.3 thorpej 901 1.30 rmind if (refcnt == 0) { 902 1.3 thorpej ksem_free(ks); 903 1.30 rmind } 904 1.30 rmind return 0; 905 1.1 christos } 906 1.1 christos 907 1.1 christos int 908 1.30 rmind sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap, 909 1.30 rmind register_t *retval) 910 1.1 christos { 911 1.23 dsl /* { 912 1.29 ad intptr_t id; 913 1.23 dsl } */ 914 1.52 thorpej int fd, error; 915 1.30 rmind ksem_t *ks; 916 1.1 christos 917 1.52 thorpej error = ksem_get(SCARG(uap, id), &ks, &fd); 918 1.30 rmind if (error) { 919 1.30 rmind return error; 920 1.3 thorpej } 921 1.30 rmind KASSERT(mutex_owned(&ks->ks_lock)); 922 1.1 christos if (ks->ks_value == SEM_VALUE_MAX) { 923 1.62 riastrad error = SET_ERROR(EOVERFLOW); 924 1.3 thorpej goto out; 925 1.1 christos } 926 1.30 rmind ks->ks_value++; 927 1.30 rmind if (ks->ks_waiters) { 928 1.20 ad cv_broadcast(&ks->ks_cv); 929 1.30 rmind } 930 1.30 rmind out: 931 1.52 thorpej ksem_release(ks, fd); 932 1.30 rmind return error; 933 1.3 thorpej } 934 1.3 thorpej 935 1.36 joerg int 936 1.41 matt do_ksem_wait(lwp_t *l, intptr_t id, bool try_p, struct timespec *abstime) 937 1.3 thorpej { 938 1.52 thorpej int fd, error, timeo; 939 1.30 rmind ksem_t *ks; 940 1.3 thorpej 941 1.52 thorpej error = ksem_get(id, &ks, &fd); 942 1.30 rmind if (error) { 943 1.30 rmind return error; 944 1.30 rmind } 945 1.30 rmind KASSERT(mutex_owned(&ks->ks_lock)); 946 1.3 thorpej while (ks->ks_value == 0) { 947 1.3 thorpej ks->ks_waiters++; 948 1.41 matt if (!try_p && abstime != NULL) { 949 1.40 christos error = ts2timo(CLOCK_REALTIME, TIMER_ABSTIME, abstime, 950 1.40 christos &timeo, NULL); 951 1.36 joerg if (error != 0) 952 1.36 joerg goto out; 953 1.36 joerg } else { 954 1.36 joerg timeo = 0; 955 1.36 joerg } 956 1.62 riastrad error = try_p ? SET_ERROR(EAGAIN) : cv_timedwait_sig(&ks->ks_cv, 957 1.36 joerg &ks->ks_lock, timeo); 958 1.3 thorpej ks->ks_waiters--; 959 1.3 thorpej if (error) 960 1.3 thorpej goto out; 961 1.3 thorpej } 962 1.3 thorpej ks->ks_value--; 963 1.30 rmind out: 964 1.52 thorpej ksem_release(ks, fd); 965 1.30 rmind return error; 966 1.1 christos } 967 1.1 christos 968 1.1 christos int 969 1.30 rmind sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap, 970 1.30 rmind register_t *retval) 971 1.1 christos { 972 1.23 dsl /* { 973 1.29 ad intptr_t id; 974 1.23 dsl } */ 975 1.1 christos 976 1.36 joerg return do_ksem_wait(l, SCARG(uap, id), false, NULL); 977 1.36 joerg } 978 1.36 joerg 979 1.36 joerg int 980 1.36 joerg sys__ksem_timedwait(struct lwp *l, const struct sys__ksem_timedwait_args *uap, 981 1.36 joerg register_t *retval) 982 1.36 joerg { 983 1.36 joerg /* { 984 1.36 joerg intptr_t id; 985 1.36 joerg const struct timespec *abstime; 986 1.36 joerg } */ 987 1.36 joerg struct timespec ts; 988 1.36 joerg int error; 989 1.36 joerg 990 1.36 joerg error = copyin(SCARG(uap, abstime), &ts, sizeof(ts)); 991 1.36 joerg if (error != 0) 992 1.36 joerg return error; 993 1.36 joerg 994 1.36 joerg if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000) 995 1.62 riastrad return SET_ERROR(EINVAL); 996 1.36 joerg 997 1.36 joerg error = do_ksem_wait(l, SCARG(uap, id), false, &ts); 998 1.36 joerg if (error == EWOULDBLOCK) 999 1.62 riastrad error = SET_ERROR(ETIMEDOUT); 1000 1.36 joerg return error; 1001 1.1 christos } 1002 1.1 christos 1003 1.1 christos int 1004 1.30 rmind sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap, 1005 1.30 rmind register_t *retval) 1006 1.1 christos { 1007 1.23 dsl /* { 1008 1.29 ad intptr_t id; 1009 1.23 dsl } */ 1010 1.1 christos 1011 1.36 joerg return do_ksem_wait(l, SCARG(uap, id), true, NULL); 1012 1.1 christos } 1013 1.1 christos 1014 1.1 christos int 1015 1.30 rmind sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap, 1016 1.30 rmind register_t *retval) 1017 1.1 christos { 1018 1.23 dsl /* { 1019 1.29 ad intptr_t id; 1020 1.1 christos unsigned int *value; 1021 1.23 dsl } */ 1022 1.52 thorpej int fd, error; 1023 1.30 rmind ksem_t *ks; 1024 1.1 christos unsigned int val; 1025 1.1 christos 1026 1.52 thorpej error = ksem_get(SCARG(uap, id), &ks, &fd); 1027 1.30 rmind if (error) { 1028 1.30 rmind return error; 1029 1.30 rmind } 1030 1.30 rmind KASSERT(mutex_owned(&ks->ks_lock)); 1031 1.1 christos val = ks->ks_value; 1032 1.52 thorpej ksem_release(ks, fd); 1033 1.3 thorpej 1034 1.30 rmind return copyout(&val, SCARG(uap, value), sizeof(val)); 1035 1.1 christos } 1036 1.1 christos 1037 1.1 christos int 1038 1.30 rmind sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap, 1039 1.30 rmind register_t *retval) 1040 1.1 christos { 1041 1.23 dsl /* { 1042 1.29 ad intptr_t id; 1043 1.23 dsl } */ 1044 1.52 thorpej int fd, error; 1045 1.30 rmind ksem_t *ks; 1046 1.1 christos 1047 1.52 thorpej intptr_t id = SCARG(uap, id); 1048 1.52 thorpej 1049 1.52 thorpej error = ksem_get(id, &ks, &fd); 1050 1.30 rmind if (error) { 1051 1.30 rmind return error; 1052 1.3 thorpej } 1053 1.30 rmind KASSERT(mutex_owned(&ks->ks_lock)); 1054 1.3 thorpej 1055 1.30 rmind /* Operation is only for unnamed semaphores. */ 1056 1.3 thorpej if (ks->ks_name != NULL) { 1057 1.62 riastrad error = SET_ERROR(EINVAL); 1058 1.30 rmind goto out; 1059 1.3 thorpej } 1060 1.30 rmind /* Cannot destroy if there are waiters. */ 1061 1.3 thorpej if (ks->ks_waiters) { 1062 1.62 riastrad error = SET_ERROR(EBUSY); 1063 1.30 rmind goto out; 1064 1.3 thorpej } 1065 1.52 thorpej if (KSEM_ID_IS_PSHARED(id)) { 1066 1.52 thorpej /* Cannot destroy if we did't create it. */ 1067 1.52 thorpej KASSERT(fd == -1); 1068 1.52 thorpej KASSERT(ks->ks_pshared_proc != NULL); 1069 1.52 thorpej if (ks->ks_pshared_proc != curproc) { 1070 1.62 riastrad error = SET_ERROR(EINVAL); 1071 1.52 thorpej goto out; 1072 1.52 thorpej } 1073 1.52 thorpej fd = ks->ks_pshared_fd; 1074 1.52 thorpej 1075 1.52 thorpej /* Mark it dead so subsequent lookups fail. */ 1076 1.52 thorpej ks->ks_pshared_proc = NULL; 1077 1.52 thorpej 1078 1.52 thorpej /* Do an fd_getfile() to for the benefit of fd_close(). */ 1079 1.52 thorpej file_t *fp __diagused = fd_getfile(fd); 1080 1.52 thorpej KASSERT(fp != NULL); 1081 1.52 thorpej KASSERT(fp->f_ksem == ks); 1082 1.52 thorpej } 1083 1.30 rmind out: 1084 1.52 thorpej ksem_release(ks, -1); 1085 1.30 rmind if (error) { 1086 1.52 thorpej if (!KSEM_ID_IS_PSHARED(id)) 1087 1.52 thorpej fd_putfile(fd); 1088 1.27 ad return error; 1089 1.27 ad } 1090 1.32 rmind return fd_close(fd); 1091 1.22 rmind } 1092