diff --git a/sys/kern/kern_cpuset.c b/sys/kern/kern_cpuset.c index 0723059544f..24c1ba826ce 100644 --- a/sys/kern/kern_cpuset.c +++ b/sys/kern/kern_cpuset.c @@ -1090,10 +1090,43 @@ cpuset_setproc_setthread(struct cpuset *tdset, struct cpuset *set, if (error) return (error); - return cpuset_shadow(tdset, nsetp, &mask, &domain, freelist, + return cpuset_shadow(set, nsetp, &mask, &domain, freelist, domainlist); } +static int +cpuset_setproc_newbase(struct thread *td, struct cpuset *set, + struct cpuset *nroot, struct cpuset **nsetp, struct domainlist *domainlist) +{ + struct domainset ndomain; + cpuset_t nmask; + struct cpuset *pbase; + int error; + + pbase = cpuset_getbase(td->td_cpuset); + + /* Copy process mask, then further apply the new root mask. */ + CPU_COPY(&pbase->cs_mask, &nmask); + CPU_AND(&nmask, &nroot->cs_mask); + + domainset_copy(pbase->cs_domain, &ndomain); + DOMAINSET_AND(&ndomain.ds_mask, &set->cs_domain->ds_mask); + + /* Policy is too restrictive, will not work. */ + if (CPU_EMPTY(&nmask) || DOMAINSET_EMPTY(&ndomain.ds_mask)) + return (EDEADLK); + + error = cpuset_create(&pbase, set, &nmask); + if (error != 0) + return (error); + + /* Duplicates some work from above... oh well. */ + pbase->cs_domain = domainset_shadow(set->cs_domain, &ndomain, + domainlist); + *nsetp = pbase; + return (0); +} + /* * Handle three cases for updating an entire process. * @@ -1115,10 +1148,10 @@ cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask, struct setlist freelist; struct setlist droplist; struct domainlist domainlist; - struct cpuset *nset; + struct cpuset *base, *nset, *nroot, *tdroot; struct thread *td; struct proc *p; - int threads; + int needed; int nfree; int error; @@ -1134,21 +1167,41 @@ cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask, nfree = 1; LIST_INIT(&droplist); nfree = 0; + base = set; + nroot = NULL; + if (set != NULL) + nroot = cpuset_getroot(set); for (;;) { error = cpuset_which(CPU_WHICH_PID, pid, &p, &td, &nset); if (error) goto out; - if (nfree >= p->p_numthreads) + tdroot = cpuset_getroot(td->td_cpuset); + needed = p->p_numthreads; + if (set != NULL && tdroot != nroot) + needed++; + if (nfree >= needed) break; - threads = p->p_numthreads; PROC_UNLOCK(p); - if (nfree < threads) { - cpuset_freelist_add(&freelist, threads - nfree); - domainset_freelist_add(&domainlist, threads - nfree); - nfree = threads; + if (nfree < needed) { + cpuset_freelist_add(&freelist, needed - nfree); + domainset_freelist_add(&domainlist, needed - nfree); + nfree = needed; } } PROC_LOCK_ASSERT(p, MA_OWNED); + + /* + * If we're changing roots and the root set is what has been specified + * as the parent, then we'll create a new base with the process's mask + * applied to it. + */ + if (set != NULL && nroot != tdroot) { + error = cpuset_setproc_newbase(td, set, nroot, &base, + &domainlist); + if (error != 0) + goto unlock_out; + } + /* * Now that the appropriate locks are held and we have enough cpusets, * make sure the operation will succeed before applying changes. The @@ -1159,7 +1212,7 @@ cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask, thread_lock(td); if (set != NULL) error = cpuset_setproc_test_setthread(td->td_cpuset, - set); + base); else error = cpuset_setproc_test_maskthread(td->td_cpuset, mask, domain); @@ -1175,7 +1228,7 @@ cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask, FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); if (set != NULL) - error = cpuset_setproc_setthread(td->td_cpuset, set, + error = cpuset_setproc_setthread(td->td_cpuset, base, &nset, &freelist, &domainlist); else error = cpuset_setproc_maskthread(td->td_cpuset, mask, @@ -1190,6 +1243,8 @@ cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask, unlock_out: PROC_UNLOCK(p); out: + if (base != NULL && base != set) + cpuset_rel(base); while ((nset = LIST_FIRST(&droplist)) != NULL) cpuset_rel_complete(nset); cpuset_freelist_free(&freelist);