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