kern_cpuset.c revision 176730
1/*- 2 * Copyright (c) 2008, 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 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/kern/kern_cpuset.c 176730 2008-03-02 07:39:22Z jeff $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/sysproto.h> 34#include <sys/kernel.h> 35#include <sys/lock.h> 36#include <sys/malloc.h> 37#include <sys/mutex.h> 38#include <sys/priv.h> 39#include <sys/proc.h> 40#include <sys/refcount.h> 41#include <sys/sched.h> 42#include <sys/smp.h> 43#include <sys/syscallsubr.h> 44#include <sys/cpuset.h> 45#include <sys/sx.h> 46#include <sys/refcount.h> 47#include <sys/queue.h> 48#include <sys/limits.h> 49 50#include <vm/uma.h> 51 52/* 53 * cpusets provide a mechanism for creating and manipulating sets of 54 * processors for the purpose of constraining the scheduling of threads to 55 * specific processors. 56 * 57 * Each process belongs to an identified set, by default this is set 1. Each 58 * thread may further restrict the cpus it may run on to a subset of this 59 * named set. This creates an anonymous set which other threads and processes 60 * may not join by number. 61 * 62 * The named set is referred to herein as the 'base' set to avoid ambiguity. 63 * This set is usually a child of a 'root' set while the anonymous set may 64 * simply be referred to as a mask. In the syscall api these are referred to 65 * as the ROOT, CPUSET, and MASK levels where CPUSET is called 'base' here. 66 * 67 * Threads inherit their set from their creator whether it be anonymous or 68 * not. This means that anonymous sets are immutable because they may be 69 * shared. To modify an anonymous set a new set is created with the desired 70 * mask and the same parent as the existing anonymous set. This gives the 71 * illusion of each thread having a private mask.A 72 * 73 * Via the syscall apis a user may ask to retrieve or modify the root, base, 74 * or mask that is discovered via a pid, tid, or setid. Modifying a set 75 * modifies all numbered and anonymous child sets to comply with the new mask. 76 * Modifying a pid or tid's mask applies only to that tid but must still 77 * exist within the assigned parent set. 78 * 79 * A thread may not be assigned to a a group seperate from other threads in 80 * the process. This is to remove ambiguity when the setid is queried with 81 * a pid argument. There is no other technical limitation. 82 * 83 * This somewhat complex arrangement is intended to make it easy for 84 * applications to query available processors and bind their threads to 85 * specific processors while also allowing administrators to dynamically 86 * reprovision by changing sets which apply to groups of processes. 87 * 88 * A simple application should not concern itself with sets at all and 89 * rather apply masks to its own threads via CPU_WHICH_TID and a -1 id 90 * meaning 'curthread'. It may query availble cpus for that tid with a 91 * getaffinity call using (CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1, ...). 92 */ 93static uma_zone_t cpuset_zone; 94static struct mtx cpuset_lock; 95static struct setlist cpuset_ids; 96struct cpuset *cpuset_zero; 97static struct unrhdr *cpuset_unr; 98 99/* 100 * Acquire a reference to a cpuset, all pointers must be tracked with refs. 101 */ 102struct cpuset * 103cpuset_ref(struct cpuset *set) 104{ 105 106 refcount_acquire(&set->cs_ref); 107 return (set); 108} 109 110/* 111 * Release a reference in a context where it is safe to allocte. 112 */ 113void 114cpuset_rel(struct cpuset *set) 115{ 116 cpusetid_t id; 117 118 if (refcount_release(&set->cs_ref) == 0) 119 return; 120 mtx_lock_spin(&cpuset_lock); 121 LIST_REMOVE(set, cs_siblings); 122 id = set->cs_id; 123 if (id != CPUSET_INVALID) 124 LIST_REMOVE(set, cs_link); 125 mtx_unlock_spin(&cpuset_lock); 126 cpuset_rel(set->cs_parent); 127 uma_zfree(cpuset_zone, set); 128 if (id != CPUSET_INVALID) 129 free_unr(cpuset_unr, id); 130} 131 132/* 133 * Deferred release must be used when in a context that is not safe to 134 * allocate/free. This places any unreferenced sets on the list 'head'. 135 */ 136static void 137cpuset_rel_defer(struct setlist *head, struct cpuset *set) 138{ 139 140 if (refcount_release(&set->cs_ref) == 0) 141 return; 142 mtx_lock_spin(&cpuset_lock); 143 LIST_REMOVE(set, cs_siblings); 144 if (set->cs_id != CPUSET_INVALID) 145 LIST_REMOVE(set, cs_link); 146 LIST_INSERT_HEAD(head, set, cs_link); 147 mtx_unlock_spin(&cpuset_lock); 148} 149 150/* 151 * Complete a deferred release. Removes the set from the list provided to 152 * cpuset_rel_defer. 153 */ 154static void 155cpuset_rel_complete(struct cpuset *set) 156{ 157 LIST_REMOVE(set, cs_link); 158 cpuset_rel(set->cs_parent); 159 uma_zfree(cpuset_zone, set); 160} 161 162/* 163 * Find a set based on an id. Returns it with a ref. 164 */ 165static struct cpuset * 166cpuset_lookup(cpusetid_t setid) 167{ 168 struct cpuset *set; 169 170 if (setid == CPUSET_INVALID) 171 return (NULL); 172 mtx_lock_spin(&cpuset_lock); 173 LIST_FOREACH(set, &cpuset_ids, cs_link) 174 if (set->cs_id == setid) 175 break; 176 if (set) 177 cpuset_ref(set); 178 mtx_unlock_spin(&cpuset_lock); 179 return (set); 180} 181 182/* 183 * Create a set in the space provided in 'set' with the provided parameters. 184 * The set is returned with a single ref. May return EDEADLK if the set 185 * will have no valid cpu based on restrictions from the parent. 186 */ 187static int 188_cpuset_create(struct cpuset *set, struct cpuset *parent, cpuset_t *mask, 189 cpusetid_t id) 190{ 191 int error; 192 193 error = 0; 194 CPU_COPY(mask, &set->cs_mask); 195 LIST_INIT(&set->cs_children); 196 refcount_init(&set->cs_ref, 1); 197 set->cs_flags = 0; 198 mtx_lock_spin(&cpuset_lock); 199 CPU_AND(mask, &parent->cs_mask); 200 if (!CPU_EMPTY(mask)) { 201 set->cs_id = id; 202 set->cs_parent = cpuset_ref(parent); 203 LIST_INSERT_HEAD(&parent->cs_children, set, cs_siblings); 204 if (set->cs_id != CPUSET_INVALID) 205 LIST_INSERT_HEAD(&cpuset_ids, set, cs_link); 206 } else 207 error = EDEADLK; 208 mtx_unlock_spin(&cpuset_lock); 209 210 return (error); 211} 212 213/* 214 * Create a new non-anonymous set with the requested parent and mask. May 215 * return failures if the mask is invalid or a new number can not be 216 * allocated. 217 */ 218static int 219cpuset_create(struct cpuset **setp, struct cpuset *parent, cpuset_t *mask) 220{ 221 struct cpuset *set; 222 cpusetid_t id; 223 int error; 224 225 id = alloc_unr(cpuset_unr); 226 if (id == -1) 227 return (ENFILE); 228 *setp = set = uma_zalloc(cpuset_zone, M_WAITOK); 229 error = _cpuset_create(set, parent, mask, id); 230 if (error == 0) 231 return (0); 232 free_unr(cpuset_unr, id); 233 uma_zfree(cpuset_zone, set); 234 235 return (error); 236} 237 238/* 239 * Recursively check for errors that would occur from applying mask to 240 * the tree of sets starting at 'set'. Checks for sets that would become 241 * empty as well as RDONLY flags. 242 */ 243static int 244cpuset_testupdate(struct cpuset *set, cpuset_t *mask) 245{ 246 struct cpuset *nset; 247 cpuset_t newmask; 248 int error; 249 250 mtx_assert(&cpuset_lock, MA_OWNED); 251 if (set->cs_flags & CPU_SET_RDONLY) 252 return (EPERM); 253 error = 0; 254 CPU_COPY(&set->cs_mask, &newmask); 255 CPU_AND(&newmask, mask); 256 if (CPU_EMPTY(&newmask)) 257 return (EDEADLK); 258 LIST_FOREACH(nset, &set->cs_children, cs_siblings) 259 if ((error = cpuset_testupdate(nset, &newmask)) != 0) 260 break; 261 return (error); 262} 263 264/* 265 * Applies the mask 'mask' without checking for empty sets or permissions. 266 */ 267static void 268cpuset_update(struct cpuset *set, cpuset_t *mask) 269{ 270 struct cpuset *nset; 271 272 mtx_assert(&cpuset_lock, MA_OWNED); 273 CPU_AND(&set->cs_mask, mask); 274 LIST_FOREACH(nset, &set->cs_children, cs_siblings) 275 cpuset_update(nset, &set->cs_mask); 276 277 return; 278} 279 280/* 281 * Modify the set 'set' to use a copy of the mask provided. Apply this new 282 * mask to restrict all children in the tree. Checks for validity before 283 * applying the changes. 284 */ 285static int 286cpuset_modify(struct cpuset *set, cpuset_t *mask) 287{ 288 int error; 289 290 error = suser(curthread); 291 if (error) 292 return (error); 293 mtx_lock_spin(&cpuset_lock); 294 error = cpuset_testupdate(set, mask); 295 if (error) 296 goto out; 297 cpuset_update(set, mask); 298 CPU_COPY(mask, &set->cs_mask); 299out: 300 mtx_unlock_spin(&cpuset_lock); 301 302 return (error); 303} 304 305/* 306 * Walks up the tree from 'set' to find the root. Returns the root 307 * referenced. 308 */ 309static struct cpuset * 310cpuset_root(struct cpuset *set) 311{ 312 313 mtx_lock_spin(&cpuset_lock); 314 for (; set->cs_parent != NULL; set = set->cs_parent) 315 if (set->cs_flags & CPU_SET_ROOT) 316 break; 317 cpuset_ref(set); 318 mtx_unlock_spin(&cpuset_lock); 319 320 return (set); 321} 322 323/* 324 * Find the first non-anonymous set starting from 'set'. Returns this set 325 * referenced. May return the passed in set with an extra ref if it is 326 * not anonymous. 327 */ 328static struct cpuset * 329cpuset_base(struct cpuset *set) 330{ 331 332 mtx_lock_spin(&cpuset_lock); 333 if (set->cs_id == CPUSET_INVALID) 334 set = set->cs_parent; 335 cpuset_ref(set); 336 mtx_unlock_spin(&cpuset_lock); 337 338 return (set); 339} 340 341/* 342 * Resolve the 'which' parameter of several cpuset apis. 343 * 344 * For WHICH_PID and WHICH_TID return a locked proc and valid proc/tid. Also 345 * checks for permission via p_cansched(). 346 * 347 * For WHICH_SET returns a valid set with a new reference. 348 * 349 * -1 may be supplied for any argument to mean the current proc/thread or 350 * the base set of the current thread. May fail with ESRCH/EPERM. 351 */ 352static int 353cpuset_which(cpuwhich_t which, id_t id, struct proc **pp, struct thread **tdp, 354 struct cpuset **setp) 355{ 356 struct cpuset *set; 357 struct thread *td; 358 struct proc *p; 359 int error; 360 361 *pp = p = NULL; 362 *tdp = td = NULL; 363 *setp = set = NULL; 364 switch (which) { 365 case CPU_WHICH_PID: 366 if (id == -1) { 367 PROC_LOCK(curproc); 368 p = curproc; 369 break; 370 } 371 if ((p = pfind(id)) == NULL) 372 return (ESRCH); 373 break; 374 case CPU_WHICH_TID: 375 if (id == -1) { 376 PROC_LOCK(curproc); 377 p = curproc; 378 td = curthread; 379 break; 380 } 381 sx_slock(&allproc_lock); 382 FOREACH_PROC_IN_SYSTEM(p) { 383 PROC_LOCK(p); 384 PROC_SLOCK(p); 385 FOREACH_THREAD_IN_PROC(p, td) 386 if (td->td_tid == id) 387 break; 388 PROC_SUNLOCK(p); 389 if (td != NULL) 390 break; 391 PROC_UNLOCK(p); 392 } 393 sx_sunlock(&allproc_lock); 394 if (td == NULL) 395 return (ESRCH); 396 break; 397 case CPU_WHICH_CPUSET: 398 if (id == -1) { 399 thread_lock(curthread); 400 set = cpuset_base(curthread->td_cpuset); 401 thread_unlock(curthread); 402 } else 403 set = cpuset_lookup(id); 404 if (set) { 405 *setp = set; 406 return (0); 407 } 408 return (ESRCH); 409 default: 410 return (EINVAL); 411 } 412 error = p_cansched(curthread, p); 413 if (error) { 414 PROC_UNLOCK(p); 415 return (error); 416 } 417 if (td == NULL) 418 td = FIRST_THREAD_IN_PROC(p); 419 *pp = p; 420 *tdp = td; 421 return (0); 422} 423 424/* 425 * Create an anonymous set with the provided mask in the space provided by 426 * 'fset'. If the passed in set is anonymous we use its parent otherwise 427 * the new set is a child of 'set'. 428 */ 429static int 430cpuset_shadow(struct cpuset *set, struct cpuset *fset, cpuset_t *mask) 431{ 432 struct cpuset *parent; 433 434 if (set->cs_id == CPUSET_INVALID) 435 parent = set->cs_parent; 436 else 437 parent = set; 438 return (_cpuset_create(fset, parent, mask, CPUSET_INVALID)); 439} 440 441/* 442 * Handle two cases for replacing the base set or mask of an entire process. 443 * 444 * 1) Set is non-null and mask is null. This reparents all anonymous sets 445 * to the provided set and replaces all non-anonymous td_cpusets with the 446 * provided set. 447 * 2) Mask is non-null and set is null. This replaces or creates anonymous 448 * sets for every thread with the existing base as a parent. 449 * 450 * This is overly complicated because we can't allocate while holding a 451 * spinlock and spinlocks must be held while changing and examining thread 452 * state. 453 */ 454static int 455cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask) 456{ 457 struct setlist freelist; 458 struct setlist droplist; 459 struct cpuset *nset; 460 struct thread *td; 461 struct proc *p; 462 int threads; 463 int nfree; 464 int error; 465 /* 466 * The algorithm requires two passes due to locking considerations. 467 * 468 * 1) Lookup the process and acquire the locks in the required order. 469 * 2) If enough cpusets have not been allocated release the locks and 470 * allocate them. Loop. 471 */ 472 LIST_INIT(&freelist); 473 LIST_INIT(&droplist); 474 nfree = 0; 475 for (;;) { 476 error = cpuset_which(CPU_WHICH_PID, pid, &p, &td, &nset); 477 if (error) 478 goto out; 479 PROC_SLOCK(p); 480 if (nfree >= p->p_numthreads) 481 break; 482 threads = p->p_numthreads; 483 PROC_SUNLOCK(p); 484 PROC_UNLOCK(p); 485 for (; nfree < threads; nfree++) { 486 nset = uma_zalloc(cpuset_zone, M_WAITOK); 487 LIST_INSERT_HEAD(&freelist, nset, cs_link); 488 } 489 } 490 PROC_LOCK_ASSERT(p, MA_OWNED); 491 PROC_SLOCK_ASSERT(p, MA_OWNED); 492 /* 493 * Now that the appropriate locks are held and we have enough cpusets, 494 * replace each thread's cpuset while using deferred release. We 495 * must do this because the PROC_SLOCK has to be held while traversing 496 * the thread list and this limits the type of operations allowed. 497 */ 498 error = 0; 499 FOREACH_THREAD_IN_PROC(p, td) { 500 struct cpuset *tdset; 501 thread_lock(td); 502 /* 503 * If we presently have an anonymous set or are applying a 504 * mask we must create an anonymous shadow set. That is 505 * either parented to our existing base or the supplied set. 506 * 507 * If we have a base set with no anonymous shadow we simply 508 * replace it outright. 509 */ 510 tdset = td->td_cpuset; 511 if (tdset->cs_id == CPUSET_INVALID || mask) { 512 nset = LIST_FIRST(&freelist); 513 LIST_REMOVE(nset, cs_link); 514 if (mask) 515 error = cpuset_shadow(tdset, nset, mask); 516 else 517 error = _cpuset_create(nset, set, 518 &tdset->cs_mask, CPUSET_INVALID); 519 if (error) { 520 LIST_INSERT_HEAD(&freelist, nset, cs_link); 521 thread_unlock(td); 522 break; 523 } 524 } else 525 nset = cpuset_ref(set); 526 cpuset_rel_defer(&droplist, tdset); 527 td->td_cpuset = nset; 528 sched_affinity(td); 529 thread_unlock(td); 530 } 531 PROC_SUNLOCK(p); 532 PROC_UNLOCK(p); 533out: 534 while ((nset = LIST_FIRST(&droplist)) != NULL) 535 cpuset_rel_complete(nset); 536 while ((nset = LIST_FIRST(&freelist)) != NULL) { 537 LIST_REMOVE(nset, cs_link); 538 uma_zfree(cpuset_zone, nset); 539 } 540 return (error); 541} 542 543/* 544 * Apply an anonymous mask to a single thread. 545 */ 546static int 547cpuset_setthread(lwpid_t id, cpuset_t *mask) 548{ 549 struct cpuset *nset; 550 struct cpuset *set; 551 struct thread *td; 552 struct proc *p; 553 int error; 554 555 nset = uma_zalloc(cpuset_zone, M_WAITOK); 556 error = cpuset_which(CPU_WHICH_TID, id, &p, &td, &nset); 557 if (error) 558 goto out; 559 thread_lock(td); 560 set = td->td_cpuset; 561 error = cpuset_shadow(set, nset, mask); 562 if (error == 0) { 563 cpuset_rel(td->td_cpuset); 564 td->td_cpuset = nset; 565 sched_affinity(td); 566 nset = NULL; 567 } 568 thread_unlock(td); 569 PROC_UNLOCK(p); 570out: 571 if (nset) 572 uma_zfree(cpuset_zone, nset); 573 return (error); 574} 575 576/* 577 * Creates the cpuset for thread0. We make two sets: 578 * 579 * 0 - The root set which should represent all valid processors in the 580 * system. It is initially created with a mask of all processors 581 * because we don't know what processors are valid until cpuset_init() 582 * runs. This set is immutable. 583 * 1 - The default set which all processes are a member of until changed. 584 * This allows an administrator to move all threads off of given cpus to 585 * dedicate them to high priority tasks or save power etc. 586 */ 587struct cpuset * 588cpuset_thread0(void) 589{ 590 struct cpuset *set; 591 int error; 592 593 cpuset_zone = uma_zcreate("cpuset", sizeof(struct cpuset), NULL, NULL, 594 NULL, NULL, UMA_ALIGN_PTR, 0); 595 mtx_init(&cpuset_lock, "cpuset", NULL, MTX_SPIN | MTX_RECURSE); 596 /* 597 * Create the root system set for the whole machine. Doesn't use 598 * cpuset_create() due to NULL parent. 599 */ 600 set = uma_zalloc(cpuset_zone, M_WAITOK | M_ZERO); 601 set->cs_mask.__bits[0] = -1; 602 LIST_INIT(&set->cs_children); 603 LIST_INSERT_HEAD(&cpuset_ids, set, cs_link); 604 set->cs_ref = 1; 605 set->cs_flags = CPU_SET_ROOT; 606 cpuset_zero = set; 607 /* 608 * Now derive a default, modifiable set from that to give out. 609 */ 610 set = uma_zalloc(cpuset_zone, M_WAITOK); 611 error = _cpuset_create(set, cpuset_zero, &cpuset_zero->cs_mask, 1); 612 KASSERT(error == 0, ("Error creating default set: %d\n", error)); 613 /* 614 * Initialize the unit allocator. 0 and 1 are allocated above. 615 */ 616 cpuset_unr = new_unrhdr(2, INT_MAX, NULL); 617 618 return (set); 619} 620 621/* 622 * This is called once the final set of system cpus is known. Modifies 623 * the root set and all children and mark the root readonly. 624 */ 625static void 626cpuset_init(void *arg) 627{ 628 cpuset_t mask; 629 630 CPU_ZERO(&mask); 631#ifdef SMP 632 mask.__bits[0] = all_cpus; 633#else 634 mask.__bits[0] = 1; 635#endif 636 if (cpuset_modify(cpuset_zero, &mask)) 637 panic("Can't set initial cpuset mask.\n"); 638 cpuset_zero->cs_flags |= CPU_SET_RDONLY; 639} 640SYSINIT(cpuset, SI_SUB_SMP, SI_ORDER_ANY, cpuset_init, NULL); 641 642#ifndef _SYS_SYSPROTO_H_ 643struct cpuset_args { 644 cpusetid_t *setid; 645}; 646#endif 647int 648cpuset(struct thread *td, struct cpuset_args *uap) 649{ 650 struct cpuset *root; 651 struct cpuset *set; 652 int error; 653 654 thread_lock(td); 655 root = cpuset_root(td->td_cpuset); 656 thread_unlock(td); 657 error = cpuset_create(&set, root, &root->cs_mask); 658 cpuset_rel(root); 659 if (error) 660 return (error); 661 error = cpuset_setproc(-1, set, NULL); 662 if (error == 0) 663 error = copyout(&set->cs_id, uap->setid, sizeof(set->cs_id)); 664 cpuset_rel(set); 665 return (error); 666} 667 668#ifndef _SYS_SYSPROTO_H_ 669struct cpuset_setid_args { 670 cpuwhich_t which; 671 id_t id; 672 cpusetid_t setid; 673}; 674#endif 675int 676cpuset_setid(struct thread *td, struct cpuset_setid_args *uap) 677{ 678 struct cpuset *set; 679 int error; 680 681 /* 682 * Presently we only support per-process sets. 683 */ 684 if (uap->which != CPU_WHICH_PID) 685 return (EINVAL); 686 set = cpuset_lookup(uap->setid); 687 if (set == NULL) 688 return (ESRCH); 689 error = cpuset_setproc(uap->id, set, NULL); 690 cpuset_rel(set); 691 return (error); 692} 693 694#ifndef _SYS_SYSPROTO_H_ 695struct cpuset_getid_args { 696 cpulevel_t level; 697 cpuwhich_t which; 698 id_t id; 699 cpusetid_t *setid; 700#endif 701int 702cpuset_getid(struct thread *td, struct cpuset_getid_args *uap) 703{ 704 struct cpuset *nset; 705 struct cpuset *set; 706 struct thread *ttd; 707 struct proc *p; 708 cpusetid_t id; 709 int error; 710 711 if (uap->level == CPU_LEVEL_WHICH && uap->which != CPU_WHICH_CPUSET) 712 return (EINVAL); 713 error = cpuset_which(uap->which, uap->id, &p, &ttd, &set); 714 if (error) 715 return (error); 716 switch (uap->which) { 717 case CPU_WHICH_TID: 718 case CPU_WHICH_PID: 719 thread_lock(ttd); 720 set = cpuset_base(ttd->td_cpuset); 721 thread_unlock(ttd); 722 PROC_UNLOCK(p); 723 break; 724 case CPU_WHICH_CPUSET: 725 break; 726 } 727 switch (uap->level) { 728 case CPU_LEVEL_ROOT: 729 nset = cpuset_root(set); 730 cpuset_rel(set); 731 set = nset; 732 break; 733 case CPU_LEVEL_CPUSET: 734 break; 735 case CPU_LEVEL_WHICH: 736 break; 737 } 738 id = set->cs_id; 739 cpuset_rel(set); 740 if (error == 0) 741 error = copyout(&id, uap->setid, sizeof(id)); 742 743 return (error); 744} 745 746#ifndef _SYS_SYSPROTO_H_ 747struct cpuset_getaffinity_args { 748 cpulevel_t level; 749 cpuwhich_t which; 750 int id; 751 int cpusetsize; 752 long *mask; 753}; 754#endif 755int 756cpuset_getaffinity(struct thread *td, struct cpuset_getaffinity_args *uap) 757{ 758 struct thread *ttd; 759 struct cpuset *nset; 760 struct cpuset *set; 761 struct proc *p; 762 cpuset_t *mask; 763 int error; 764 int size; 765 766 if (uap->cpusetsize < CPU_SETSIZE || uap->cpusetsize > CPU_MAXSIZE) 767 return (ERANGE); 768 size = uap->cpusetsize / NBBY; 769 mask = malloc(size, M_TEMP, M_WAITOK | M_ZERO); 770 error = cpuset_which(uap->which, uap->id, &p, &ttd, &set); 771 if (error) 772 goto out; 773 error = 0; 774 switch (uap->level) { 775 case CPU_LEVEL_ROOT: 776 case CPU_LEVEL_CPUSET: 777 switch (uap->which) { 778 case CPU_WHICH_TID: 779 case CPU_WHICH_PID: 780 thread_lock(ttd); 781 set = cpuset_ref(ttd->td_cpuset); 782 thread_unlock(ttd); 783 break; 784 case CPU_WHICH_CPUSET: 785 break; 786 } 787 if (uap->level == CPU_LEVEL_ROOT) 788 nset = cpuset_root(set); 789 else 790 nset = cpuset_base(set); 791 CPU_COPY(&nset->cs_mask, mask); 792 cpuset_rel(nset); 793 break; 794 case CPU_LEVEL_WHICH: 795 switch (uap->which) { 796 case CPU_WHICH_TID: 797 thread_lock(ttd); 798 CPU_COPY(&ttd->td_cpuset->cs_mask, mask); 799 thread_unlock(ttd); 800 break; 801 case CPU_WHICH_PID: 802 PROC_SLOCK(p); 803 FOREACH_THREAD_IN_PROC(p, ttd) { 804 thread_lock(ttd); 805 CPU_OR(mask, &ttd->td_cpuset->cs_mask); 806 thread_unlock(ttd); 807 } 808 PROC_SUNLOCK(p); 809 break; 810 case CPU_WHICH_CPUSET: 811 CPU_COPY(&set->cs_mask, mask); 812 break; 813 } 814 break; 815 default: 816 error = EINVAL; 817 break; 818 } 819 if (set) 820 cpuset_rel(set); 821 if (p) 822 PROC_UNLOCK(p); 823 if (error == 0) 824 error = copyout(mask, uap->mask, size); 825out: 826 free(mask, M_TEMP); 827 return (error); 828} 829 830#ifndef _SYS_SYSPROTO_H_ 831struct cpuset_setaffinity_args { 832 cpulevel_t level; 833 cpuwhich_t which; 834 int id; 835 int cpusetsize; 836 long * mask; 837}; 838#endif 839int 840cpuset_setaffinity(struct thread *td, struct cpuset_setaffinity_args *uap) 841{ 842 struct cpuset *nset; 843 struct cpuset *set; 844 struct thread *ttd; 845 struct proc *p; 846 cpuset_t *mask; 847 int error; 848 849 if (uap->cpusetsize < CPU_SETSIZE || uap->cpusetsize > CPU_MAXSIZE) 850 return (ERANGE); 851 mask = malloc(uap->cpusetsize / NBBY, M_TEMP, M_WAITOK | M_ZERO); 852 error = copyin(uap->mask, mask, uap->cpusetsize / NBBY); 853 if (error) 854 goto out; 855 switch (uap->level) { 856 case CPU_LEVEL_ROOT: 857 case CPU_LEVEL_CPUSET: 858 error = cpuset_which(uap->which, uap->id, &p, &ttd, &set); 859 if (error) 860 break; 861 switch (uap->which) { 862 case CPU_WHICH_TID: 863 case CPU_WHICH_PID: 864 thread_lock(ttd); 865 set = cpuset_ref(ttd->td_cpuset); 866 thread_unlock(ttd); 867 break; 868 case CPU_WHICH_CPUSET: 869 break; 870 } 871 if (uap->level == CPU_LEVEL_ROOT) 872 nset = cpuset_root(set); 873 else 874 nset = cpuset_base(set); 875 error = cpuset_modify(nset, mask); 876 cpuset_rel(nset); 877 cpuset_rel(set); 878 break; 879 case CPU_LEVEL_WHICH: 880 switch (uap->which) { 881 case CPU_WHICH_TID: 882 error = cpuset_setthread(uap->id, mask); 883 break; 884 case CPU_WHICH_PID: 885 error = cpuset_setproc(uap->id, NULL, mask); 886 break; 887 case CPU_WHICH_CPUSET: 888 error = cpuset_which(CPU_WHICH_CPUSET, uap->id, &p, 889 &ttd, &set); 890 if (error == 0) { 891 error = cpuset_modify(set, mask); 892 cpuset_rel(set); 893 } 894 break; 895 default: 896 error = EINVAL; 897 break; 898 } 899 break; 900 default: 901 error = EINVAL; 902 break; 903 } 904out: 905 free(mask, M_TEMP); 906 return (error); 907} 908