1 /*- 2 * Copyright (c) 2003, Jeffrey Roberson <jeff@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include "opt_posix.h" 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/lock.h> 34 #include <sys/mutex.h> 35 #include <sys/proc.h> 36 #include <sys/resourcevar.h> 37 #include <sys/sched.h> 38 #include <sys/sysctl.h> 39 #include <sys/smp.h> 40 #include <sys/sysent.h> 41 #include <sys/systm.h> 42 #include <sys/sysproto.h> 43 #include <sys/signalvar.h> 44 #include <sys/ucontext.h> 45 #include <sys/thr.h> 46 #include <sys/rtprio.h> 47 #include <posix4/sched.h> 48 #include <posix4/posix4.h> 49 #include <sys/umtx.h> 50 #include <sys/limits.h> 51 52 #include <machine/frame.h> 53 54 extern int max_threads_per_proc; 55 56 static int create_thread(struct thread *td, mcontext_t *ctx, 57 void (*start_func)(void *), void *arg, 58 char *stack_base, size_t stack_size, 59 char *tls_base, 60 long *child_tid, long *parent_tid, 61 int flags, struct thr_sched_param *sched); 62 63 /* 64 * System call interface. 65 */ 66 int 67 thr_create(struct thread *td, struct thr_create_args *uap) 68 /* ucontext_t *ctx, long *id, int flags */ 69 { 70 ucontext_t ctx; 71 int error; 72 73 if ((error = copyin(uap->ctx, &ctx, sizeof(ctx)))) 74 return (error); 75 76 error = create_thread(td, &ctx.uc_mcontext, NULL, NULL, 77 NULL, 0, NULL, uap->id, NULL, uap->flags, NULL); 78 return (error); 79 } 80 81 int 82 thr_new(struct thread *td, struct thr_new_args *uap) 83 /* struct thr_param * */ 84 { 85 struct thr_param param; 86 struct thr_sched_param sched_param, *sched; 87 int error; 88 89 if (uap->param_size < sizeof(param)) 90 return (EINVAL); 91 if ((error = copyin(uap->param, ¶m, sizeof(param)))) 92 return (error); 93 sched = NULL; 94 if (param.sched_param != NULL) { 95 if (param.sched_param_size != sizeof(struct thr_sched_param)) 96 return (EINVAL); 97 98 error = copyin(param.sched_param, &sched_param, 99 sizeof(sched_param)); 100 if (error) 101 return (error); 102 sched = &sched_param; 103 } 104 105 error = create_thread(td, NULL, param.start_func, param.arg, 106 param.stack_base, param.stack_size, param.tls_base, 107 param.child_tid, param.parent_tid, param.flags, 108 sched); 109 return (error); 110 } 111 112 static int 113 create_thread(struct thread *td, mcontext_t *ctx, 114 void (*start_func)(void *), void *arg, 115 char *stack_base, size_t stack_size, 116 char *tls_base, 117 long *child_tid, long *parent_tid, 118 int flags, struct thr_sched_param *sched) 119 { 120 stack_t stack; 121 struct thread *newtd; 122 struct ksegrp *kg, *newkg; 123 struct proc *p; 124 long id; 125 int error; 126 127 error = 0; 128 p = td->td_proc; 129 kg = td->td_ksegrp; 130 131 /* Have race condition but it is cheap. */ 132 if (p->p_numthreads >= max_threads_per_proc) 133 return (EPROCLIM); 134 135 if (sched != NULL) { 136 switch(sched->policy) { 137 case SCHED_FIFO: 138 case SCHED_RR: 139 /* Only root can set scheduler policy */ 140 if (suser(td) != 0) 141 return (EPERM); 142 if (sched->param.sched_priority < RTP_PRIO_MIN || 143 sched->param.sched_priority > RTP_PRIO_MAX) 144 return (EINVAL); 145 break; 146 case SCHED_OTHER: 147 break; 148 default: 149 return (EINVAL); 150 } 151 } 152 153 /* Initialize our td and new ksegrp.. */ 154 newtd = thread_alloc(); 155 156 /* 157 * Try the copyout as soon as we allocate the td so we don't 158 * have to tear things down in a failure case below. 159 * Here we copy out tid to two places, one for child and one 160 * for parent, because pthread can create a detached thread, 161 * if parent wants to safely access child tid, it has to provide 162 * its storage, because child thread may exit quickly and 163 * memory is freed before parent thread can access it. 164 */ 165 id = newtd->td_tid; 166 if ((child_tid != NULL && 167 (error = copyout(&id, child_tid, sizeof(long)))) || 168 (parent_tid != NULL && 169 (error = copyout(&id, parent_tid, sizeof(long))))) { 170 thread_free(newtd); 171 return (error); 172 } 173 bzero(&newtd->td_startzero, 174 __rangeof(struct thread, td_startzero, td_endzero)); 175 bcopy(&td->td_startcopy, &newtd->td_startcopy, 176 __rangeof(struct thread, td_startcopy, td_endcopy)); 177 newtd->td_proc = td->td_proc; 178 newtd->td_ucred = crhold(td->td_ucred); 179 180 cpu_set_upcall(newtd, td); 181 182 if (ctx != NULL) { /* old way to set user context */ 183 error = set_mcontext(newtd, ctx); 184 if (error != 0) { 185 thread_free(newtd); 186 crfree(td->td_ucred); 187 return (error); 188 } 189 } else { 190 /* Set up our machine context. */ 191 stack.ss_sp = stack_base; 192 stack.ss_size = stack_size; 193 /* Set upcall address to user thread entry function. */ 194 cpu_set_upcall_kse(newtd, start_func, arg, &stack); 195 /* Setup user TLS address and TLS pointer register. */ 196 error = cpu_set_user_tls(newtd, tls_base); 197 if (error != 0) { 198 thread_free(newtd); 199 crfree(td->td_ucred); 200 return (error); 201 } 202 } 203 204 newkg = ksegrp_alloc(); 205 bzero(&newkg->kg_startzero, 206 __rangeof(struct ksegrp, kg_startzero, kg_endzero)); 207 bcopy(&kg->kg_startcopy, &newkg->kg_startcopy, 208 __rangeof(struct ksegrp, kg_startcopy, kg_endcopy)); 209 sched_init_concurrency(newkg); 210 PROC_LOCK(td->td_proc); 211 td->td_proc->p_flag |= P_HADTHREADS; 212 newtd->td_sigmask = td->td_sigmask; 213 mtx_lock_spin(&sched_lock); 214 ksegrp_link(newkg, p); 215 thread_link(newtd, newkg); 216 PROC_UNLOCK(p); 217 218 /* let the scheduler know about these things. */ 219 sched_fork_ksegrp(td, newkg); 220 sched_fork_thread(td, newtd); 221 if (sched != NULL) { 222 struct rtprio rtp; 223 switch (sched->policy) { 224 case SCHED_FIFO: 225 rtp.type = PRI_FIFO; 226 rtp.prio = sched->param.sched_priority; 227 rtp_to_pri(&rtp, newkg); 228 sched_prio(newtd, newkg->kg_user_pri); 229 break; 230 case SCHED_RR: 231 rtp.type = PRI_REALTIME; 232 rtp.prio = sched->param.sched_priority; 233 rtp_to_pri(&rtp, newkg); 234 sched_prio(newtd, newkg->kg_user_pri); 235 break; 236 case SCHED_OTHER: 237 if (newkg->kg_pri_class != PRI_TIMESHARE) { 238 rtp.type = PRI_TIMESHARE; 239 rtp.prio = 0; 240 rtp_to_pri(&rtp, newkg); 241 sched_prio(newtd, newkg->kg_user_pri); 242 } 243 break; 244 default: 245 panic("sched policy"); 246 } 247 } 248 TD_SET_CAN_RUN(newtd); 249 /* if ((flags & THR_SUSPENDED) == 0) */ 250 setrunqueue(newtd, SRQ_BORING); 251 mtx_unlock_spin(&sched_lock); 252 253 return (error); 254 } 255 256 int 257 thr_self(struct thread *td, struct thr_self_args *uap) 258 /* long *id */ 259 { 260 long id; 261 int error; 262 263 id = td->td_tid; 264 if ((error = copyout(&id, uap->id, sizeof(long)))) 265 return (error); 266 267 return (0); 268 } 269 270 int 271 thr_exit(struct thread *td, struct thr_exit_args *uap) 272 /* long *state */ 273 { 274 struct proc *p; 275 276 p = td->td_proc; 277 278 /* Signal userland that it can free the stack. */ 279 if ((void *)uap->state != NULL) { 280 suword((void *)uap->state, 1); 281 kern_umtx_wake(td, uap->state, INT_MAX); 282 } 283 284 PROC_LOCK(p); 285 sigqueue_flush(&td->td_sigqueue); 286 mtx_lock_spin(&sched_lock); 287 288 /* 289 * Shutting down last thread in the proc. This will actually 290 * call exit() in the trampoline when it returns. 291 */ 292 if (p->p_numthreads != 1) { 293 thread_stopped(p); 294 thread_exit(); 295 /* NOTREACHED */ 296 } 297 mtx_unlock_spin(&sched_lock); 298 PROC_UNLOCK(p); 299 return (0); 300 } 301 302 int 303 thr_kill(struct thread *td, struct thr_kill_args *uap) 304 /* long id, int sig */ 305 { 306 struct thread *ttd; 307 struct proc *p; 308 int error; 309 310 p = td->td_proc; 311 error = 0; 312 PROC_LOCK(p); 313 if (uap->id == -1) { 314 if (uap->sig != 0 && !_SIG_VALID(uap->sig)) { 315 error = EINVAL; 316 } else { 317 error = ESRCH; 318 FOREACH_THREAD_IN_PROC(p, ttd) { 319 if (ttd != td) { 320 error = 0; 321 if (uap->sig == 0) 322 break; 323 tdsignal(p, ttd, uap->sig, NULL); 324 } 325 } 326 } 327 } else { 328 if (uap->id != td->td_tid) 329 ttd = thread_find(p, uap->id); 330 else 331 ttd = td; 332 if (ttd == NULL) 333 error = ESRCH; 334 else if (uap->sig == 0) 335 ; 336 else if (!_SIG_VALID(uap->sig)) 337 error = EINVAL; 338 else 339 tdsignal(p, ttd, uap->sig, NULL); 340 } 341 PROC_UNLOCK(p); 342 return (error); 343 } 344 345 int 346 thr_suspend(struct thread *td, struct thr_suspend_args *uap) 347 /* const struct timespec *timeout */ 348 { 349 struct timespec ts; 350 struct timeval tv; 351 int error; 352 int hz; 353 354 hz = 0; 355 error = 0; 356 if (uap->timeout != NULL) { 357 error = copyin((const void *)uap->timeout, (void *)&ts, 358 sizeof(struct timespec)); 359 if (error != 0) 360 return (error); 361 if (ts.tv_nsec < 0 || ts.tv_nsec > 1000000000) 362 return (EINVAL); 363 if (ts.tv_sec == 0 && ts.tv_nsec == 0) 364 return (ETIMEDOUT); 365 TIMESPEC_TO_TIMEVAL(&tv, &ts); 366 hz = tvtohz(&tv); 367 } 368 PROC_LOCK(td->td_proc); 369 if ((td->td_flags & TDF_THRWAKEUP) == 0) 370 error = msleep((void *)td, &td->td_proc->p_mtx, PCATCH, "lthr", 371 hz); 372 if (td->td_flags & TDF_THRWAKEUP) { 373 mtx_lock_spin(&sched_lock); 374 td->td_flags &= ~TDF_THRWAKEUP; 375 mtx_unlock_spin(&sched_lock); 376 PROC_UNLOCK(td->td_proc); 377 return (0); 378 } 379 PROC_UNLOCK(td->td_proc); 380 if (error == EWOULDBLOCK) 381 error = ETIMEDOUT; 382 else if (error == ERESTART) { 383 if (hz != 0) 384 error = EINTR; 385 } 386 return (error); 387 } 388 389 int 390 thr_wake(struct thread *td, struct thr_wake_args *uap) 391 /* long id */ 392 { 393 struct proc *p; 394 struct thread *ttd; 395 396 p = td->td_proc; 397 PROC_LOCK(p); 398 ttd = thread_find(p, uap->id); 399 if (ttd == NULL) { 400 PROC_UNLOCK(p); 401 return (ESRCH); 402 } 403 mtx_lock_spin(&sched_lock); 404 ttd->td_flags |= TDF_THRWAKEUP; 405 mtx_unlock_spin(&sched_lock); 406 wakeup((void *)ttd); 407 PROC_UNLOCK(p); 408 return (0); 409 } 410 411 int 412 thr_set_name(struct thread *td, struct thr_set_name_args *uap) 413 { 414 struct proc *p = td->td_proc; 415 char name[MAXCOMLEN + 1]; 416 struct thread *ttd; 417 int error; 418 419 error = 0; 420 name[0] = '\0'; 421 if (uap->name != NULL) { 422 error = copyinstr(uap->name, name, sizeof(name), 423 NULL); 424 if (error) 425 return (error); 426 } 427 PROC_LOCK(p); 428 if (uap->id == td->td_tid) 429 ttd = td; 430 else 431 ttd = thread_find(p, uap->id); 432 if (ttd != NULL) 433 strcpy(ttd->td_name, name); 434 else 435 error = ESRCH; 436 PROC_UNLOCK(p); 437 return (error); 438 } 439 440 int 441 thr_setscheduler(struct thread *td, struct thr_setscheduler_args *uap) 442 { 443 struct proc *p; 444 struct thread *ttd; 445 struct rtprio rtp; 446 struct sched_param param; 447 int ret; 448 449 if (uap->param_size != sizeof(struct sched_param)) 450 return (EINVAL); 451 452 ret = copyin(uap->param, ¶m, sizeof(struct sched_param)); 453 if (ret) 454 return (ret); 455 456 switch(uap->policy) { 457 case SCHED_FIFO: 458 if (suser(td) != 0) 459 return (EPERM); 460 rtp.type = PRI_FIFO; 461 break; 462 case SCHED_RR: 463 if (suser(td) != 0) 464 return (EPERM); 465 rtp.type = PRI_REALTIME; 466 break; 467 case SCHED_OTHER: 468 rtp.type = PRI_TIMESHARE; 469 break; 470 default: 471 return (EINVAL); 472 } 473 rtp.prio = param.sched_priority; 474 475 p = td->td_proc; 476 PROC_LOCK(p); 477 ret = p_cansched(td, p); 478 if (ret != 0) { 479 PROC_UNLOCK(p); 480 return (ret); 481 } 482 483 ttd = thread_find(p, uap->id); 484 if (ttd == NULL) { 485 PROC_UNLOCK(p); 486 return (ESRCH); 487 } 488 mtx_lock_spin(&sched_lock); 489 ret = rtp_to_pri(&rtp, ttd->td_ksegrp); 490 if (ret == 0) { 491 if (TD_IS_RUNNING(ttd)) 492 ttd->td_flags |= TDF_NEEDRESCHED; 493 else if (ttd->td_priority > ttd->td_ksegrp->kg_user_pri) 494 sched_prio(ttd, ttd->td_ksegrp->kg_user_pri); 495 } 496 mtx_unlock_spin(&sched_lock); 497 PROC_UNLOCK(p); 498 return (ret); 499 } 500 501 int 502 thr_getscheduler(struct thread *td, struct thr_getscheduler_args *uap) 503 { 504 struct proc *p; 505 struct thread *ttd; 506 struct rtprio rtp; 507 struct sched_param param; 508 int policy; 509 int ret; 510 511 if (uap->param_size != sizeof(struct sched_param)) 512 return (EINVAL); 513 514 p = td->td_proc; 515 PROC_LOCK(p); 516 ttd = thread_find(p, uap->id); 517 if (ttd == NULL) { 518 PROC_UNLOCK(p); 519 return (ESRCH); 520 } 521 mtx_lock_spin(&sched_lock); 522 switch(ttd->td_ksegrp->kg_pri_class) { 523 case PRI_TIMESHARE: 524 policy = SCHED_OTHER; 525 break; 526 case PRI_FIFO: 527 policy = SCHED_FIFO; 528 break; 529 case PRI_REALTIME: 530 policy = SCHED_RR; 531 break; 532 default: 533 policy = SCHED_OTHER; /* XXX SCHED_IDLE */ 534 } 535 pri_to_rtp(ttd->td_ksegrp, &rtp); 536 mtx_unlock_spin(&sched_lock); 537 PROC_UNLOCK(p); 538 539 param.sched_priority = rtp.prio; 540 ret = copyout(&policy, uap->policy, sizeof(policy)); 541 if (ret == 0) 542 ret = copyout(¶m, uap->param, sizeof(param)); 543 return (ret); 544 } 545 546 int 547 thr_setschedparam(struct thread *td, struct thr_setschedparam_args *uap) 548 { 549 struct proc *p; 550 struct thread *ttd; 551 struct rtprio rtp; 552 struct sched_param param; 553 int ret; 554 555 if (uap->param_size != sizeof(struct sched_param)) 556 return (EINVAL); 557 558 ret = copyin(uap->param, ¶m, sizeof(struct sched_param)); 559 if (ret) 560 return (ret); 561 562 p = td->td_proc; 563 PROC_LOCK(p); 564 ret = p_cansched(td, p); 565 if (ret != 0) { 566 PROC_UNLOCK(p); 567 return (ret); 568 } 569 570 ttd = thread_find(p, uap->id); 571 if (ttd == NULL) { 572 PROC_UNLOCK(p); 573 return (ESRCH); 574 } 575 576 mtx_lock_spin(&sched_lock); 577 pri_to_rtp(ttd->td_ksegrp, &rtp); 578 rtp.prio = param.sched_priority; 579 ret = rtp_to_pri(&rtp, ttd->td_ksegrp); 580 if (ret == 0) { 581 if (TD_IS_RUNNING(ttd)) 582 ttd->td_flags |= TDF_NEEDRESCHED; 583 else if (ttd->td_priority > ttd->td_ksegrp->kg_user_pri) 584 sched_prio(ttd, ttd->td_ksegrp->kg_user_pri); 585 } 586 mtx_unlock_spin(&sched_lock); 587 PROC_UNLOCK(p); 588 return (ret); 589 } 590