1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright 2026 The FreeBSD Foundation 5 * 6 * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 7 * under sponsorship from the FreeBSD Foundation. 8 */ 9 10 #include <sys/systm.h> 11 #include <sys/conf.h> 12 #include <sys/fcntl.h> 13 #include <sys/file.h> 14 #include <sys/filedesc.h> 15 #include <sys/kernel.h> 16 #include <sys/limits.h> 17 #include <sys/malloc.h> 18 #include <sys/module.h> 19 #include <sys/proc.h> 20 #include <sys/stat.h> 21 #include <sys/sysent.h> 22 #include <sys/user.h> 23 #include <dev/ntsync/ntsyncvar.h> 24 25 static struct cdev *ntsync_cdev; 26 MALLOC_DEFINE(M_NTSYNC, "ntsync", "ntsync"); 27 28 static void ntsync_free_priv(struct ntsync_priv *priv); 29 30 /* 31 * Returning error from an ioctl handler prevents the generic ioctl 32 * code from copying out the result. Use direct access to ioctl(2) 33 * args to get the parameters block pointer to implement Linux 34 * semantic of both returning an error and updating the parameters 35 * block. 36 */ 37 static int 38 ntsync_ioctl_copyout(struct thread *td, const void *ptr, size_t sz) 39 { 40 void *uptr; 41 42 if (SV_PROC_ABI(td->td_proc) != SV_ABI_FREEBSD) 43 return (0); 44 uptr = (void *)(uintptr_t)td->td_sa.args[2]; 45 return (copyout(ptr, uptr, sz)); 46 } 47 48 static bool 49 ntsync_wait_any(struct ntsync_wait_state *state) 50 { 51 struct ntsync_obj *obj; 52 int i; 53 54 MPASS(state->any); 55 NTSYNC_PRIV_ASSERT(state->owner); 56 57 for (i = 0; i < state->obj_count; i++) { 58 obj = state->objs[i]; 59 if (obj->is_signaled(obj, state, i)) { 60 state->index = i; 61 obj->consume(obj, state, state->index); 62 return (true); 63 } 64 } 65 return (false); 66 } 67 68 static bool 69 ntsync_wait_all_prepare(struct ntsync_wait_state *state, bool *stop) 70 { 71 struct ntsync_obj *obj; 72 int alerti, i; 73 bool first; 74 75 MPASS(state->all); 76 MPASS(state->error == 0); 77 MPASS(!*stop); 78 NTSYNC_PRIV_ASSERT(state->owner); 79 80 alerti = state->alert_event == NULL ? 0 : 1; 81 first = true; 82 83 for (i = 0; i < state->obj_count - alerti; i++) { 84 obj = state->objs[i]; 85 if (!obj->prepare(obj, state, i, stop)) 86 return (false); 87 if (*stop) { 88 MPASS(state->error != 0); 89 return (false); 90 } 91 MPASS (state->error == 0); 92 if (first) { 93 first = false; 94 state->index = i; 95 } 96 } 97 return (true); 98 } 99 100 static void 101 ntsync_wait_all_commit(struct ntsync_wait_state *state) 102 { 103 struct ntsync_obj *obj; 104 int i, alerti; 105 106 MPASS(state->all); 107 NTSYNC_PRIV_ASSERT(state->owner); 108 alerti = state->alert_event == NULL ? 0 : 1; 109 110 for (i = 0; i < state->obj_count - alerti; i++) { 111 obj = state->objs[i]; 112 obj->commit(obj, state, i); 113 } 114 } 115 116 static void 117 ntsync_wait_link_waiters(struct ntsync_wait_state *state) 118 { 119 struct ntsync_obj *obj; 120 struct ntsync_obj_waiter *waiter; 121 int i; 122 123 NTSYNC_PRIV_ASSERT(state->owner); 124 125 for (i = 0; i < state->obj_count; i++) { 126 obj = state->objs[i]; 127 waiter = &state->waiters[i]; 128 waiter->state = state; 129 TAILQ_INSERT_TAIL(&obj->waiters, waiter, link); 130 } 131 } 132 133 static void 134 ntsync_wait_unlink_waiters(struct ntsync_wait_state *state) 135 { 136 struct ntsync_obj *obj; 137 struct ntsync_obj_waiter *waiter; 138 int i; 139 140 NTSYNC_PRIV_ASSERT(state->owner); 141 142 for (i = 0; i < state->obj_count; i++) { 143 obj = state->objs[i]; 144 waiter = &state->waiters[i]; 145 TAILQ_REMOVE(&obj->waiters, waiter, link); 146 } 147 } 148 149 static void 150 ntsync_wait_post_commit(struct ntsync_wait_state *state) 151 { 152 struct ntsync_obj *obj; 153 int alerti, i; 154 155 NTSYNC_PRIV_ASSERT(state->owner); 156 157 alerti = state->alert_event == NULL ? 0 : 1; 158 for (i = 0; i < state->obj_count - alerti; i++) { 159 obj = state->objs[i]; 160 obj->post_commit(obj, state, i); 161 } 162 } 163 164 static void 165 ntsync_wait_check_ready(struct ntsync_wait_state *state) 166 { 167 struct ntsync_obj *ae; 168 int index; 169 bool stop; 170 171 NTSYNC_PRIV_ASSERT(state->owner); 172 173 if (state->ready) 174 return; 175 176 if (state->all) { 177 stop = false; 178 if (ntsync_wait_all_prepare(state, &stop)) { 179 MPASS(!stop); 180 ntsync_wait_all_commit(state); 181 state->ready = true; 182 ntsync_wait_post_commit(state); 183 } else if (stop) { 184 /* skip */ 185 } else if (state->alert_event != NULL) { 186 ae = &state->alert_event->obj; 187 index = state->obj_count - 1; 188 if (ae->is_signaled(ae, state, index)) { 189 state->index = index; 190 ae->consume(ae, state, index); 191 ae->post_commit(ae, state, index); 192 state->ready = true; 193 } 194 } 195 } else { /* state->any */ 196 if (ntsync_wait_any(state)) 197 state->ready = true; 198 } 199 } 200 201 /* 202 * Perform the wait. Errors returned through state->error still 203 * result in the copyout of the ntsync_wait_args after the wait, while 204 * errors returned as the function result do not. 205 */ 206 static int 207 ntsync_wait_locked(struct ntsync_wait_state *state, struct thread *td) 208 { 209 int error; 210 211 NTSYNC_PRIV_ASSERT(state->owner); 212 213 for (;;) { 214 ntsync_wait_check_ready(state); 215 if (state->ready) 216 break; 217 error = msleep_sbt(state, &state->owner->lock, 218 PCATCH, "ntsync", state->sb, 0, 219 C_ABSOLUTE /* | C_HARDCLOCK XXXKIB */); 220 221 /* 222 * Check state->ready before checking error from 223 * msleep(). If there was a wake up that set the 224 * readiness before us receiving a signal or timeout, 225 * the objects states are modified to reflect wakeup. 226 * Due to this, ready should result in normal return. 227 */ 228 if (state->ready) { 229 error = 0; 230 break; 231 } 232 233 if (error != 0) { 234 if (error == EAGAIN) 235 error = ETIMEDOUT; 236 break; 237 } 238 } 239 return (error); 240 } 241 242 static int 243 ntsync_wait(struct ntsync_wait_state *state, struct thread *td) 244 { 245 int error; 246 247 NTSYNC_PRIV_LOCK(state->owner); 248 ntsync_wait_link_waiters(state); 249 error = ntsync_wait_locked(state, td); 250 ntsync_wait_unlink_waiters(state); 251 NTSYNC_PRIV_UNLOCK(state->owner); 252 return (error); 253 } 254 255 static void 256 ntsync_wakeup_waiters(struct ntsync_obj *obj) 257 { 258 struct ntsync_obj_waiter *w; 259 260 NTSYNC_PRIV_ASSERT(obj->owner); 261 262 TAILQ_FOREACH(w, &obj->waiters, link) { 263 ntsync_wait_check_ready(w->state); 264 if (w->state->ready) 265 wakeup(w->state); 266 } 267 } 268 269 static int 270 ntsync_create_obj(struct ntsync_obj *obj, struct fileops *fops, 271 struct ntsync_priv *priv, struct thread *td) 272 { 273 struct file *fp; 274 int error, fd; 275 276 error = falloc_noinstall(td, &fp); 277 if (error != 0) 278 return (error); 279 280 /* 281 * The priv fd cannot be closed during object creation since 282 * it is fget-ed around ioctl. 283 */ 284 obj->owner = priv; 285 286 TAILQ_INIT(&obj->waiters); 287 NTSYNC_PRIV_LOCK(priv); 288 MPASS(!priv->closed); 289 if (priv->objs_cnt == UINT_MAX) { 290 NTSYNC_PRIV_UNLOCK(priv); 291 fdrop(fp, td); 292 return (EMFILE); 293 } 294 priv->objs_cnt++; 295 NTSYNC_PRIV_UNLOCK(priv); 296 297 finit(fp, FREAD | FWRITE, DTYPE_NTSYNC, obj, fops); 298 error = finstall(td, fp, &fd, 0, NULL); 299 if (error != 0) { 300 NTSYNC_PRIV_LOCK(priv); 301 MPASS(priv->objs_cnt > 0); 302 priv->objs_cnt--; 303 NTSYNC_PRIV_UNLOCK(priv); 304 } else { 305 td->td_retval[0] = fd; 306 } 307 fdrop(fp, td); 308 return (error); 309 } 310 311 static void 312 ntsync_close_obj(struct ntsync_obj *obj, struct thread *td) 313 { 314 struct ntsync_priv *priv; 315 316 priv = obj->owner; 317 NTSYNC_PRIV_LOCK(priv); 318 MPASS(priv->objs_cnt > 0); 319 MPASS(TAILQ_EMPTY(&obj->waiters)); 320 priv->objs_cnt--; 321 NTSYNC_PRIV_UNLOCK(priv); 322 ntsync_free_priv(priv); 323 } 324 325 static bool 326 ntsync_sem_is_signaled(struct ntsync_obj *obj, struct ntsync_wait_state *state, 327 int index) 328 { 329 struct ntsync_obj_sem *sem; 330 331 MPASS(obj->type == NTSYNC_OBJ_SEM); 332 NTSYNC_PRIV_ASSERT(obj->owner); 333 sem = OBJ_TO_SEM(obj); 334 return (sem->a.count != 0); 335 } 336 337 static void 338 ntsync_sem_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state, 339 int index) 340 { 341 struct ntsync_obj_sem *sem; 342 343 MPASS(obj->type == NTSYNC_OBJ_SEM); 344 NTSYNC_PRIV_ASSERT(obj->owner); 345 sem = OBJ_TO_SEM(obj); 346 MPASS(sem->a.count != 0); 347 sem->a.count--; 348 } 349 350 static bool 351 ntsync_sem_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state, 352 int index, bool *stop) 353 { 354 struct ntsync_obj_sem *sem; 355 356 MPASS(obj->type == NTSYNC_OBJ_SEM); 357 NTSYNC_PRIV_ASSERT(obj->owner); 358 sem = OBJ_TO_SEM(obj); 359 if (sem->a.count == 0) 360 return (false); 361 sem->a1 = sem->a; 362 sem->a1.count--; 363 return (true); 364 } 365 366 static void 367 ntsync_sem_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state, 368 int index) 369 { 370 struct ntsync_obj_sem *sem; 371 372 MPASS(obj->type == NTSYNC_OBJ_SEM); 373 NTSYNC_PRIV_ASSERT(obj->owner); 374 sem = OBJ_TO_SEM(obj); 375 sem->a = sem->a1; 376 } 377 378 static void 379 ntsync_sem_post_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state, 380 int index) 381 { 382 } 383 384 static int 385 ntsync_sem_close(struct file *fp, struct thread *td) 386 { 387 struct ntsync_obj_sem *sem; 388 389 sem = fp->f_data; 390 ntsync_close_obj(&sem->obj, td); 391 free(sem, M_NTSYNC); 392 return (0); 393 } 394 395 int 396 ntsync_sem_release(struct thread *td, struct file *fp, uint32_t *val) 397 { 398 struct ntsync_obj *obj; 399 struct ntsync_obj_sem *sem; 400 struct ntsync_priv *priv; 401 uint32_t prev; 402 int error; 403 404 obj = fp->f_data; 405 if (obj->type != NTSYNC_OBJ_SEM) 406 return (EINVAL); 407 sem = OBJ_TO_SEM(obj); 408 priv = obj->owner; 409 error = 0; 410 411 NTSYNC_PRIV_LOCK(priv); 412 if (sem->a.count + *val < sem->a.count || 413 sem->a.count + *val > sem->a.max) { 414 error = EOVERFLOW; 415 } else { 416 prev = sem->a.count; 417 sem->a.count += *val; 418 if (sem->a.count != 0) 419 ntsync_wakeup_waiters(obj); 420 *val = prev; 421 } 422 NTSYNC_PRIV_UNLOCK(priv); 423 return (error); 424 } 425 426 int 427 ntsync_sem_read(struct thread *td, struct file *fp, struct ntsync_sem_args *a) 428 { 429 struct ntsync_obj *obj; 430 struct ntsync_obj_sem *sem; 431 struct ntsync_priv *priv; 432 433 obj = fp->f_data; 434 if (obj->type != NTSYNC_OBJ_SEM) 435 return (EINVAL); 436 sem = OBJ_TO_SEM(obj); 437 priv = obj->owner; 438 NTSYNC_PRIV_LOCK(priv); 439 *a = sem->a; 440 NTSYNC_PRIV_UNLOCK(priv); 441 return (0); 442 } 443 444 static int 445 ntsync_sem_ioctl(struct file *fp, u_long com, void *data, 446 struct ucred *active_cred, struct thread *td) 447 { 448 int error; 449 450 switch (com) { 451 case NTSYNC_IOC_SEM_RELEASE: 452 error = ntsync_sem_release(td, fp, data); 453 break; 454 case NTSYNC_IOC_SEM_READ: 455 error = ntsync_sem_read(td, fp, data); 456 break; 457 default: 458 error = ENOTTY; 459 break; 460 } 461 return (error); 462 } 463 464 static int 465 ntsync_sem_stat(struct file *fp, struct stat *sbp, struct ucred *cred) 466 { 467 struct ntsync_obj *obj; 468 struct ntsync_obj_sem *sem; 469 470 MPASS(fp->f_type == DTYPE_NTSYNC); 471 obj = fp->f_data; 472 MPASS(obj->type == NTSYNC_OBJ_SEM); 473 sem = OBJ_TO_SEM(obj); 474 475 memset(sbp, 0, sizeof(*sbp)); 476 sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR; 477 NTSYNC_PRIV_LOCK(obj->owner); 478 sbp->st_size = sem->a.max; 479 sbp->st_nlink = sem->a.count; 480 NTSYNC_PRIV_UNLOCK(obj->owner); 481 return (0); 482 } 483 484 static int 485 ntsync_sem_fill_kinfo(struct file *fp, struct kinfo_file *kif, 486 struct filedesc *fdp) 487 { 488 struct ntsync_obj *obj; 489 struct ntsync_obj_sem *sem; 490 491 MPASS(fp->f_type == DTYPE_NTSYNC); 492 obj = fp->f_data; 493 MPASS(obj->type == NTSYNC_OBJ_SEM); 494 sem = OBJ_TO_SEM(obj); 495 496 kif->kf_type = KF_TYPE_NTSYNC; 497 kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_SEM; 498 kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner; 499 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.count = sem->a.count; 500 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_sem.max = sem->a.max; 501 return (0); 502 } 503 504 struct fileops ntsync_sem_fops = { 505 .fo_read = invfo_rdwr, 506 .fo_write = invfo_rdwr, 507 .fo_truncate = invfo_truncate, 508 .fo_ioctl = ntsync_sem_ioctl, 509 .fo_poll = invfo_poll, 510 .fo_kqfilter = invfo_kqfilter, 511 .fo_stat = ntsync_sem_stat, 512 .fo_close = ntsync_sem_close, 513 .fo_chmod = invfo_chmod, 514 .fo_chown = invfo_chown, 515 .fo_sendfile = invfo_sendfile, 516 .fo_fill_kinfo = ntsync_sem_fill_kinfo, 517 .fo_flags = DFLAG_PASSABLE, 518 }; 519 520 static int 521 ntsync_create_sem(struct ntsync_sem_args *args, struct ntsync_priv *priv, 522 struct thread *td) 523 { 524 struct ntsync_obj_sem *sem; 525 int error; 526 527 if (args->count > args->max) 528 return (EINVAL); 529 530 sem = malloc(sizeof(*sem), M_NTSYNC, M_WAITOK | M_ZERO); 531 sem->obj.type = NTSYNC_OBJ_SEM; 532 sem->obj.is_signaled = ntsync_sem_is_signaled; 533 sem->obj.consume = ntsync_sem_consume; 534 sem->obj.prepare = ntsync_sem_prepare; 535 sem->obj.commit = ntsync_sem_commit; 536 sem->obj.post_commit = ntsync_sem_post_commit; 537 sem->a = *args; 538 539 error = ntsync_create_obj(&sem->obj, &ntsync_sem_fops, priv, td); 540 if (error != 0) 541 free(sem, M_NTSYNC); 542 543 return (error); 544 } 545 546 static bool 547 ntsync_mutex_can_lock(struct ntsync_obj_mutex *mutex, uint32_t nwa_owner) 548 { 549 return (mutex->a.owner == 0 || 550 (mutex->a.owner == nwa_owner && mutex->a.count < UINT32_MAX) || 551 mutex->abandoned); 552 } 553 554 static bool 555 ntsync_mutex_is_signaled(struct ntsync_obj *obj, 556 struct ntsync_wait_state *state, int index) 557 { 558 struct ntsync_obj_mutex *mutex; 559 560 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 561 NTSYNC_PRIV_ASSERT(obj->owner); 562 mutex = OBJ_TO_MUTEX(obj); 563 return (ntsync_mutex_can_lock(mutex, state->nwa->owner)); 564 } 565 566 static void 567 ntsync_mutex_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state, 568 int index) 569 { 570 struct ntsync_obj_mutex *mutex; 571 572 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 573 NTSYNC_PRIV_ASSERT(obj->owner); 574 mutex = OBJ_TO_MUTEX(obj); 575 MPASS(ntsync_mutex_can_lock(mutex, state->nwa->owner)); 576 if (state->nwa->owner == 0) { 577 state->error = EINVAL; 578 return; 579 } 580 if (mutex->a.owner == 0 || mutex->abandoned) 581 mutex->a.count = 1; 582 else 583 mutex->a.count++; 584 mutex->a.owner = state->nwa->owner; 585 if (mutex->abandoned && state->error == 0) 586 state->error = EOWNERDEAD; 587 mutex->abandoned = false; 588 } 589 590 static bool 591 ntsync_mutex_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state, 592 int index, bool *stop) 593 { 594 struct ntsync_obj_mutex *mutex; 595 596 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 597 NTSYNC_PRIV_ASSERT(obj->owner); 598 mutex = OBJ_TO_MUTEX(obj); 599 if (!ntsync_mutex_can_lock(mutex, state->nwa->owner)) 600 return (false); 601 if (state->nwa->owner == 0) { 602 state->error = EINVAL; 603 *stop = true; 604 return (false); 605 } 606 mutex->a1 = mutex->a; 607 if (mutex->a.owner == 0 || mutex->abandoned) 608 mutex->a1.count = 1; 609 else 610 mutex->a1.count++; 611 mutex->a1.owner = state->nwa->owner; 612 return (true); 613 } 614 615 static void 616 ntsync_mutex_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state, 617 int index) 618 { 619 struct ntsync_obj_mutex *mutex; 620 621 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 622 NTSYNC_PRIV_ASSERT(obj->owner); 623 mutex = OBJ_TO_MUTEX(obj); 624 mutex->a = mutex->a1; 625 if (mutex->abandoned) 626 state->error = EOWNERDEAD; 627 mutex->abandoned = false; 628 } 629 630 static void 631 ntsync_mutex_post_commit(struct ntsync_obj *obj, 632 struct ntsync_wait_state *state, int index) 633 { 634 } 635 636 static int 637 ntsync_mutex_close(struct file *fp, struct thread *td) 638 { 639 struct ntsync_obj_mutex *mutex; 640 641 mutex = fp->f_data; 642 ntsync_close_obj(&mutex->obj, td); 643 free(mutex, M_NTSYNC); 644 return (0); 645 } 646 647 int 648 ntsync_mutex_unlock(struct thread *td, struct file *fp, 649 struct ntsync_mutex_args *a) 650 { 651 struct ntsync_obj *obj; 652 struct ntsync_obj_mutex *mutex; 653 struct ntsync_priv *priv; 654 uint32_t prev; 655 int error; 656 657 obj = fp->f_data; 658 if (obj->type != NTSYNC_OBJ_MUTEX) 659 return (EINVAL); 660 mutex = OBJ_TO_MUTEX(obj); 661 priv = obj->owner; 662 663 NTSYNC_PRIV_LOCK(priv); 664 if (a->owner == 0) { 665 error = EINVAL; 666 } else if (a->owner != mutex->a.owner) { 667 error = EPERM; 668 } else { 669 error = 0; 670 prev = mutex->a.count; 671 MPASS(mutex->a.count > 0); 672 mutex->a.count--; 673 a->count = prev; 674 if (mutex->a.count == 0) { 675 mutex->a.owner = 0; 676 ntsync_wakeup_waiters(obj); 677 } 678 } 679 NTSYNC_PRIV_UNLOCK(priv); 680 return (error); 681 } 682 683 int 684 ntsync_mutex_kill(struct thread *td, struct file *fp, uint32_t val) 685 { 686 struct ntsync_obj *obj; 687 struct ntsync_obj_mutex *mutex; 688 struct ntsync_priv *priv; 689 int error; 690 691 obj = fp->f_data; 692 if (obj->type != NTSYNC_OBJ_MUTEX) 693 return (EINVAL); 694 mutex = OBJ_TO_MUTEX(obj); 695 priv = obj->owner; 696 697 NTSYNC_PRIV_LOCK(priv); 698 if (val == 0) { 699 error = EINVAL; 700 } else if (mutex->a.owner != val) { 701 error = EPERM; 702 } else { 703 error = 0; 704 mutex->a.owner = 0; 705 mutex->a.count = 0; 706 mutex->abandoned = true; 707 ntsync_wakeup_waiters(obj); 708 } 709 NTSYNC_PRIV_UNLOCK(priv); 710 return (error); 711 } 712 713 int 714 ntsync_mutex_read(struct thread *td, struct file *fp, 715 struct ntsync_mutex_args *a, bool *doco) 716 { 717 struct ntsync_obj *obj; 718 struct ntsync_obj_mutex *mutex; 719 struct ntsync_priv *priv; 720 int error; 721 722 *doco = false; 723 obj = fp->f_data; 724 if (obj->type != NTSYNC_OBJ_MUTEX) 725 return (EINVAL); 726 mutex = OBJ_TO_MUTEX(obj); 727 priv = obj->owner; 728 error = 0; 729 730 NTSYNC_PRIV_LOCK(priv); 731 *a = mutex->a; 732 if (mutex->abandoned) 733 error = EOWNERDEAD; 734 NTSYNC_PRIV_UNLOCK(priv); 735 *doco = true; 736 return (error); 737 } 738 739 static int 740 ntsync_mutex_ioctl(struct file *fp, u_long com, void *data, 741 struct ucred *active_cred, struct thread *td) 742 { 743 struct ntsync_mutex_args aa; 744 int error, error1; 745 bool doco; 746 747 doco = false; 748 switch (com) { 749 case NTSYNC_IOC_MUTEX_UNLOCK: 750 error = ntsync_mutex_unlock(td, fp, data); 751 break; 752 case NTSYNC_IOC_MUTEX_KILL: 753 error = ntsync_mutex_kill(td, fp, *(uint32_t *)data); 754 break; 755 case NTSYNC_IOC_MUTEX_READ: 756 error = ntsync_mutex_read(td, fp, &aa, &doco); 757 if (doco) { 758 error1 = ntsync_ioctl_copyout(td, &aa, sizeof(aa)); 759 if (error1 != 0) 760 error = error1; 761 } 762 break; 763 default: 764 error = ENOTTY; 765 break; 766 } 767 return (error); 768 } 769 770 static int 771 ntsync_mutex_stat(struct file *fp, struct stat *sbp, struct ucred *cred) 772 { 773 struct ntsync_obj *obj; 774 struct ntsync_obj_mutex *mutex; 775 776 MPASS(fp->f_type == DTYPE_NTSYNC); 777 obj = fp->f_data; 778 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 779 mutex = OBJ_TO_MUTEX(obj); 780 781 memset(sbp, 0, sizeof(*sbp)); 782 sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR; 783 NTSYNC_PRIV_LOCK(obj->owner); 784 sbp->st_size = mutex->a.owner; 785 sbp->st_nlink = mutex->a.count; 786 NTSYNC_PRIV_UNLOCK(obj->owner); 787 return (0); 788 } 789 790 static int 791 ntsync_mutex_fill_kinfo(struct file *fp, struct kinfo_file *kif, 792 struct filedesc *fdp) 793 { 794 struct ntsync_obj *obj; 795 struct ntsync_obj_mutex *mutex; 796 797 MPASS(fp->f_type == DTYPE_NTSYNC); 798 obj = fp->f_data; 799 MPASS(obj->type == NTSYNC_OBJ_MUTEX); 800 mutex = OBJ_TO_MUTEX(obj); 801 802 kif->kf_type = KF_TYPE_NTSYNC; 803 kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_MUTEX; 804 kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner; 805 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.owner = 806 mutex->a.owner; 807 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_mutex.count = 808 mutex->a.count; 809 return (0); 810 } 811 812 struct fileops ntsync_mutex_fops = { 813 .fo_read = invfo_rdwr, 814 .fo_write = invfo_rdwr, 815 .fo_truncate = invfo_truncate, 816 .fo_ioctl = ntsync_mutex_ioctl, 817 .fo_poll = invfo_poll, 818 .fo_kqfilter = invfo_kqfilter, 819 .fo_stat = ntsync_mutex_stat, 820 .fo_close = ntsync_mutex_close, 821 .fo_chmod = invfo_chmod, 822 .fo_chown = invfo_chown, 823 .fo_sendfile = invfo_sendfile, 824 .fo_fill_kinfo = ntsync_mutex_fill_kinfo, 825 .fo_flags = DFLAG_PASSABLE, 826 }; 827 828 static int 829 ntsync_create_mutex(struct ntsync_mutex_args *args, struct ntsync_priv *priv, 830 struct thread *td) 831 { 832 struct ntsync_obj_mutex *mutex; 833 int error; 834 835 if ((args->owner != 0 && args->count == 0) || 836 (args->owner == 0 && args->count != 0)) 837 return (EINVAL); 838 839 mutex = malloc(sizeof(*mutex), M_NTSYNC, M_WAITOK | M_ZERO); 840 mutex->obj.type = NTSYNC_OBJ_MUTEX; 841 mutex->obj.is_signaled = ntsync_mutex_is_signaled; 842 mutex->obj.consume = ntsync_mutex_consume; 843 mutex->obj.prepare = ntsync_mutex_prepare; 844 mutex->obj.commit = ntsync_mutex_commit; 845 mutex->obj.post_commit = ntsync_mutex_post_commit; 846 mutex->a = *args; 847 mutex->abandoned = false; 848 849 error = ntsync_create_obj(&mutex->obj, &ntsync_mutex_fops, priv, td); 850 if (error != 0) 851 free(mutex, M_NTSYNC); 852 853 return (error); 854 } 855 856 static bool 857 ntsync_event_is_signaled(struct ntsync_obj *obj, 858 struct ntsync_wait_state *state, int index) 859 { 860 struct ntsync_obj_event *event; 861 862 MPASS(obj->type == NTSYNC_OBJ_EVENT); 863 NTSYNC_PRIV_ASSERT(obj->owner); 864 event = OBJ_TO_EVENT(obj); 865 return (event->a.signaled != 0); 866 } 867 868 static void 869 ntsync_event_consume(struct ntsync_obj *obj, struct ntsync_wait_state *state, 870 int index) 871 { 872 struct ntsync_obj_event *event; 873 874 MPASS(obj->type == NTSYNC_OBJ_EVENT); 875 NTSYNC_PRIV_ASSERT(obj->owner); 876 MPASS(ntsync_event_is_signaled(obj, state, index)); 877 878 event = OBJ_TO_EVENT(obj); 879 if (event->a.manual == 0) 880 event->a.signaled = 0; 881 } 882 883 static bool 884 ntsync_event_prepare(struct ntsync_obj *obj, struct ntsync_wait_state *state, 885 int index, bool *stop) 886 { 887 struct ntsync_obj_event *event; 888 889 MPASS(obj->type == NTSYNC_OBJ_EVENT); 890 NTSYNC_PRIV_ASSERT(obj->owner); 891 event = OBJ_TO_EVENT(obj); 892 if (!ntsync_event_is_signaled(obj, state, index)) 893 return (false); 894 event->a1 = event->a; 895 return (true); 896 } 897 898 static void 899 ntsync_event_commit(struct ntsync_obj *obj, struct ntsync_wait_state *state, 900 int index) 901 { 902 struct ntsync_obj_event *event; 903 904 MPASS(obj->type == NTSYNC_OBJ_EVENT); 905 NTSYNC_PRIV_ASSERT(obj->owner); 906 event = OBJ_TO_EVENT(obj); 907 event->a = event->a1; 908 if (event->pulse && event->a.manual == 0) { 909 event->a.signaled = 0; 910 event->pulse = false; 911 } 912 } 913 914 static void 915 ntsync_event_post_commit(struct ntsync_obj *obj, 916 struct ntsync_wait_state *state, int index) 917 { 918 struct ntsync_obj_event *event; 919 920 MPASS(obj->type == NTSYNC_OBJ_EVENT); 921 NTSYNC_PRIV_ASSERT(obj->owner); 922 event = OBJ_TO_EVENT(obj); 923 if (event->a.manual == 0) 924 event->a.signaled = 0; 925 } 926 927 static int 928 ntsync_event_close(struct file *fp, struct thread *td) 929 { 930 struct ntsync_obj_event *event; 931 932 event = fp->f_data; 933 ntsync_close_obj(&event->obj, td); 934 free(event, M_NTSYNC); 935 return (0); 936 } 937 938 int 939 ntsync_event_set(struct thread *td, struct file *fp, uint32_t *val) 940 { 941 struct ntsync_obj *obj; 942 struct ntsync_obj_event *event; 943 struct ntsync_priv *priv; 944 uint32_t prev; 945 946 obj = fp->f_data; 947 if (obj->type != NTSYNC_OBJ_EVENT) 948 return (EINVAL); 949 event = OBJ_TO_EVENT(obj); 950 priv = obj->owner; 951 952 NTSYNC_PRIV_LOCK(priv); 953 prev = event->a.signaled; 954 event->a.signaled = 1; 955 ntsync_wakeup_waiters(obj); 956 NTSYNC_PRIV_UNLOCK(priv); 957 958 *val = prev; 959 return (0); 960 } 961 962 int 963 ntsync_event_reset(struct thread *td, struct file *fp, uint32_t *val) 964 { 965 struct ntsync_obj *obj; 966 struct ntsync_obj_event *event; 967 struct ntsync_priv *priv; 968 uint32_t prev; 969 970 obj = fp->f_data; 971 if (obj->type != NTSYNC_OBJ_EVENT) 972 return (EINVAL); 973 event = OBJ_TO_EVENT(obj); 974 priv = obj->owner; 975 976 NTSYNC_PRIV_LOCK(priv); 977 prev = event->a.signaled; 978 event->a.signaled = 0; 979 NTSYNC_PRIV_UNLOCK(priv); 980 981 *val = prev; 982 return (0); 983 } 984 985 int 986 ntsync_event_pulse(struct thread *td, struct file *fp, uint32_t *val) 987 { 988 struct ntsync_obj *obj; 989 struct ntsync_obj_event *event; 990 struct ntsync_priv *priv; 991 uint32_t prev; 992 993 obj = fp->f_data; 994 if (obj->type != NTSYNC_OBJ_EVENT) 995 return (EINVAL); 996 event = OBJ_TO_EVENT(obj); 997 priv = obj->owner; 998 999 NTSYNC_PRIV_LOCK(priv); 1000 prev = event->a.signaled; 1001 event->a.signaled = 1; 1002 event->pulse = true; 1003 ntsync_wakeup_waiters(obj); 1004 event->a.signaled = 0; 1005 event->pulse = false; 1006 NTSYNC_PRIV_UNLOCK(priv); 1007 1008 *val = prev; 1009 return (0); 1010 } 1011 1012 int 1013 ntsync_event_read(struct thread *td, struct file *fp, 1014 struct ntsync_event_args *a) 1015 { 1016 struct ntsync_obj *obj; 1017 struct ntsync_obj_event *event; 1018 struct ntsync_priv *priv; 1019 1020 obj = fp->f_data; 1021 if (obj->type != NTSYNC_OBJ_EVENT) 1022 return (EINVAL); 1023 event = OBJ_TO_EVENT(obj); 1024 priv = obj->owner; 1025 1026 NTSYNC_PRIV_LOCK(priv); 1027 *a = event->a; 1028 NTSYNC_PRIV_UNLOCK(priv); 1029 1030 return (0); 1031 } 1032 1033 static int 1034 ntsync_event_ioctl(struct file *fp, u_long com, void *data, 1035 struct ucred *active_cred, struct thread *td) 1036 { 1037 int error; 1038 1039 switch (com) { 1040 case NTSYNC_IOC_EVENT_SET: 1041 error = ntsync_event_set(td, fp, data); 1042 break; 1043 case NTSYNC_IOC_EVENT_RESET: 1044 error = ntsync_event_reset(td, fp, data); 1045 break; 1046 case NTSYNC_IOC_EVENT_PULSE: 1047 error = ntsync_event_pulse(td, fp, data); 1048 break; 1049 case NTSYNC_IOC_EVENT_READ: 1050 error = ntsync_event_read(td, fp, data); 1051 break; 1052 default: 1053 error = ENOTTY; 1054 break; 1055 } 1056 return (error); 1057 } 1058 1059 static int 1060 ntsync_event_stat(struct file *fp, struct stat *sbp, struct ucred *cred) 1061 { 1062 struct ntsync_obj *obj; 1063 struct ntsync_obj_event *event; 1064 1065 MPASS(fp->f_type == DTYPE_NTSYNC); 1066 obj = fp->f_data; 1067 MPASS(obj->type == NTSYNC_OBJ_EVENT); 1068 event = OBJ_TO_EVENT(obj); 1069 1070 memset(sbp, 0, sizeof(*sbp)); 1071 sbp->st_mode = S_IFREG /* XXXKIB */ | S_IRUSR | S_IWUSR; 1072 NTSYNC_PRIV_LOCK(obj->owner); 1073 sbp->st_size = event->a.signaled; 1074 sbp->st_nlink = event->a.manual; 1075 NTSYNC_PRIV_UNLOCK(obj->owner); 1076 return (0); 1077 } 1078 1079 static int 1080 ntsync_event_fill_kinfo(struct file *fp, struct kinfo_file *kif, 1081 struct filedesc *fdp) 1082 { 1083 struct ntsync_obj *obj; 1084 struct ntsync_obj_event *event; 1085 1086 MPASS(fp->f_type == DTYPE_NTSYNC); 1087 obj = fp->f_data; 1088 MPASS(obj->type == NTSYNC_OBJ_EVENT); 1089 event = OBJ_TO_EVENT(obj); 1090 1091 kif->kf_type = KF_TYPE_NTSYNC; 1092 kif->kf_un.kf_ntsync.kf_ntsync_type = KF_NTSYNC_TYPE_EVENT; 1093 kif->kf_un.kf_ntsync.kf_ntsync_dev = (uintptr_t)obj->owner; 1094 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.signaled = 1095 event->a.signaled; 1096 kif->kf_un.kf_ntsync.kf_ntsync_un.kf_ntsync_event.manual = 1097 event->a.manual; 1098 return (0); 1099 } 1100 1101 struct fileops ntsync_event_fops = { 1102 .fo_read = invfo_rdwr, 1103 .fo_write = invfo_rdwr, 1104 .fo_truncate = invfo_truncate, 1105 .fo_ioctl = ntsync_event_ioctl, 1106 .fo_poll = invfo_poll, 1107 .fo_kqfilter = invfo_kqfilter, 1108 .fo_stat = ntsync_event_stat, 1109 .fo_close = ntsync_event_close, 1110 .fo_chmod = invfo_chmod, 1111 .fo_chown = invfo_chown, 1112 .fo_sendfile = invfo_sendfile, 1113 .fo_fill_kinfo = ntsync_event_fill_kinfo, 1114 .fo_flags = DFLAG_PASSABLE, 1115 }; 1116 1117 static int 1118 ntsync_create_event(struct ntsync_event_args *args, struct ntsync_priv *priv, 1119 struct thread *td) 1120 { 1121 struct ntsync_obj_event *event; 1122 int error; 1123 1124 event = malloc(sizeof(*event), M_NTSYNC, M_WAITOK | M_ZERO); 1125 event->obj.type = NTSYNC_OBJ_EVENT; 1126 event->obj.is_signaled = ntsync_event_is_signaled; 1127 event->obj.consume = ntsync_event_consume; 1128 event->obj.prepare = ntsync_event_prepare; 1129 event->obj.commit = ntsync_event_commit; 1130 event->obj.post_commit = ntsync_event_post_commit; 1131 event->a = *args; 1132 1133 error = ntsync_create_obj(&event->obj, &ntsync_event_fops, priv, td); 1134 if (error != 0) 1135 free(event, M_NTSYNC); 1136 1137 return (error); 1138 } 1139 1140 static void 1141 ntsync_free_priv(struct ntsync_priv *priv) 1142 { 1143 bool do_free; 1144 1145 NTSYNC_PRIV_LOCK(priv); 1146 do_free = priv->closed && priv->objs_cnt == 0; 1147 NTSYNC_PRIV_UNLOCK(priv); 1148 if (do_free) { 1149 mtx_destroy(&priv->lock); 1150 free(priv, M_NTSYNC); 1151 } 1152 } 1153 1154 static void 1155 ntsync_priv_dtr(void *data) 1156 { 1157 ntsync_free_priv(data); 1158 } 1159 1160 static int 1161 ntsync_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 1162 { 1163 struct ntsync_priv *priv; 1164 1165 priv = malloc(sizeof(*priv), M_NTSYNC, M_WAITOK); 1166 priv->closed = false; 1167 priv->objs_cnt = 0; 1168 mtx_init(&priv->lock, "ntsync", "ntsync", MTX_DEF | MTX_NEW); 1169 devfs_set_cdevpriv(priv, ntsync_priv_dtr); 1170 return (0); 1171 } 1172 1173 static int 1174 ntsync_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 1175 { 1176 struct ntsync_priv *priv; 1177 void *a; 1178 int error; 1179 1180 error = devfs_get_cdevpriv(&a); 1181 if (error == 0) { 1182 priv = a; 1183 NTSYNC_PRIV_LOCK(priv); 1184 priv->closed = true; 1185 NTSYNC_PRIV_UNLOCK(priv); 1186 } 1187 devfs_clear_cdevpriv(); 1188 return (0); 1189 } 1190 1191 static int 1192 ntsync_wait_state_get(struct ntsync_wait_args *nwa, u_long cmd, 1193 struct ntsync_priv *owner, struct ntsync_wait_state **statep, 1194 struct thread *td) 1195 { 1196 struct ntsync_wait_state *state; 1197 struct ntsync_obj *obj; 1198 struct bintime btb; 1199 int error, i, j; 1200 1201 if (nwa->count > NTSYNC_MAX_WAIT_COUNT) 1202 return (EINVAL); 1203 if ((nwa->flags & ~NTSYNC_WAIT_REALTIME) != 0) 1204 return (EINVAL); 1205 1206 state = malloc(sizeof(*state), M_NTSYNC, M_WAITOK | M_ZERO); 1207 state->nwa = nwa; 1208 state->owner = owner; 1209 state->all = cmd == NTSYNC_IOC_WAIT_ALL; 1210 state->any = !state->all; 1211 error = copyin((void *)(uintptr_t)nwa->objs, &state->fds[0], 1212 nwa->count * sizeof(state->fds[0])); 1213 if (error != 0) 1214 return (error); 1215 1216 i = 0; 1217 if (nwa->alert != 0) { 1218 error = fget_cap(td, nwa->alert, &cap_no_rights, NULL, 1219 &state->fp_alert, NULL); 1220 if (error != 0) { 1221 state->fp_alert = NULL; 1222 goto error_out; 1223 } 1224 if (state->fp_alert->f_type != DTYPE_NTSYNC) { 1225 error = EINVAL; 1226 goto error_out; 1227 } 1228 obj = state->fp_alert->f_data; 1229 if (obj->type != NTSYNC_OBJ_EVENT || obj->owner != owner) { 1230 error = EINVAL; 1231 goto error_out; 1232 } 1233 state->alert_event = OBJ_TO_EVENT(obj); 1234 } 1235 1236 for (; i < nwa->count; i++) { 1237 error = fget_cap(td, state->fds[i], &cap_no_rights, NULL, 1238 &state->fps[i], NULL); 1239 if (error != 0) { 1240 state->fps[i] = NULL; 1241 goto error_out; 1242 } 1243 if (state->fps[i]->f_type != DTYPE_NTSYNC || 1244 (obj = state->fps[i]->f_data)->owner != owner) { 1245 i++; 1246 error = EINVAL; 1247 goto error_out; 1248 } 1249 } 1250 1251 state->obj_count = nwa->count; 1252 for (i = 0; i < nwa->count; i++) 1253 state->objs[i] = state->fps[i]->f_data; 1254 if (state->alert_event != NULL) { 1255 state->objs[i] = &state->alert_event->obj; 1256 state->obj_count++; 1257 } 1258 1259 if (state->all) { 1260 /* Check no dups */ 1261 for (i = 0; i < state->obj_count; i++) { 1262 obj = state->objs[i]; 1263 for (j = i + 1; j < state->obj_count; j++) { 1264 if (obj == state->objs[j]) { 1265 i = state->obj_count; 1266 error = EINVAL; 1267 goto error_out; 1268 } 1269 } 1270 } 1271 } 1272 1273 if (nwa->timeout == UINT64_MAX) { 1274 state->sb = 0; 1275 } else { 1276 state->sb = nstosbt(nwa->timeout); 1277 if ((nwa->flags & NTSYNC_WAIT_REALTIME) != 0) { 1278 getboottimebin(&btb); 1279 state->sb += bttosbt(btb); 1280 } 1281 } 1282 1283 *statep = state; 1284 return (0); 1285 1286 error_out: 1287 for (j = 0; j < i; j++) 1288 fdrop(state->fps[j], td); 1289 if (state->fp_alert != NULL) 1290 fdrop(state->fp_alert, td); 1291 return (error); 1292 } 1293 1294 static void 1295 ntsync_wait_state_put(struct ntsync_wait_state *state, struct thread *td) 1296 { 1297 int i; 1298 1299 for (i = 0; i < state->nwa->count; i++) 1300 fdrop(state->fps[i], td); 1301 if (state->fp_alert != NULL) 1302 fdrop(state->fp_alert, td); 1303 free(state, M_NTSYNC); 1304 } 1305 1306 static int 1307 ntsync_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 1308 struct thread *td) 1309 { 1310 struct ntsync_priv *owner; 1311 struct ntsync_wait_args *nwa; 1312 struct ntsync_wait_state *state; 1313 void *a; 1314 int error; 1315 1316 error = devfs_get_cdevpriv(&a); 1317 if (error != 0) 1318 return (error); 1319 owner = a; 1320 1321 switch (cmd) { 1322 case NTSYNC_IOC_CREATE_SEM: 1323 error = ntsync_create_sem((struct ntsync_sem_args *)data, 1324 owner, td); 1325 break; 1326 case NTSYNC_IOC_CREATE_MUTEX: 1327 error = ntsync_create_mutex((struct ntsync_mutex_args *)data, 1328 owner, td); 1329 break; 1330 case NTSYNC_IOC_CREATE_EVENT: 1331 error = ntsync_create_event((struct ntsync_event_args *)data, 1332 owner, td); 1333 break; 1334 case NTSYNC_IOC_WAIT_ANY: 1335 nwa = (struct ntsync_wait_args *)data; 1336 error = ntsync_wait_state_get(nwa, cmd, owner, &state, td); 1337 if (error != 0) 1338 break; 1339 error = ntsync_wait(state, td); 1340 if (error == 0) { 1341 nwa->index = state->index; 1342 error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa)); 1343 if (error == 0) 1344 error = state->error; 1345 } 1346 ntsync_wait_state_put(state, td); 1347 break; 1348 case NTSYNC_IOC_WAIT_ALL: 1349 nwa = (struct ntsync_wait_args *)data; 1350 error = ntsync_wait_state_get(nwa, cmd, owner, &state, td); 1351 if (error != 0) 1352 break; 1353 error = ntsync_wait(state, td); 1354 if (error == 0) { 1355 nwa->index = state->index; 1356 error = ntsync_ioctl_copyout(td, nwa, sizeof(*nwa)); 1357 if (error == 0) 1358 error = state->error; 1359 } 1360 ntsync_wait_state_put(state, td); 1361 break; 1362 1363 default: 1364 error = ENOTTY; 1365 break; 1366 } 1367 return (error); 1368 } 1369 1370 struct cdevsw ntsync_cdevsw = { 1371 .d_version = D_VERSION, 1372 .d_flags = 0, 1373 .d_open = ntsync_open, 1374 .d_close = ntsync_close, 1375 .d_ioctl = ntsync_ioctl, 1376 .d_name = "ntsync", 1377 }; 1378 1379 static int 1380 ntsync_modevent(module_t mod __unused, int type, void *data __unused) 1381 { 1382 struct make_dev_args mda; 1383 int error; 1384 1385 error = 0; 1386 switch (type) { 1387 case MOD_LOAD: 1388 make_dev_args_init(&mda); 1389 mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; 1390 mda.mda_devsw = &ntsync_cdevsw; 1391 mda.mda_uid = UID_ROOT; 1392 mda.mda_gid = GID_GAMES; 1393 mda.mda_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | 1394 S_IROTH | S_IWOTH; 1395 1396 error = make_dev_s(&mda, &ntsync_cdev, "ntsync"); 1397 if (error != 0) { 1398 printf("cannot create ntsync dev err %d\n", error); 1399 break; 1400 } 1401 if (bootverbose) 1402 printf("ntsync\n"); 1403 break; 1404 1405 case MOD_UNLOAD: 1406 destroy_dev(ntsync_cdev); 1407 break; 1408 1409 case MOD_SHUTDOWN: 1410 break; 1411 1412 default: 1413 error = EOPNOTSUPP; 1414 } 1415 1416 return (error); 1417 } 1418 1419 DEV_MODULE(ntsync, ntsync_modevent, NULL); 1420 MODULE_VERSION(ntsync, 1); 1421