diff options
author | jeff <jeff@FreeBSD.org> | 2008-03-05 01:49:20 +0000 |
---|---|---|
committer | jeff <jeff@FreeBSD.org> | 2008-03-05 01:49:20 +0000 |
commit | 7e2fbaa872a30c5fcfa4206fd26455dcdcddb698 (patch) | |
tree | 16840d6ed4e15946d5c1b5f5af333569d3d610b4 /sys/kern/kern_cpuset.c | |
parent | ed2af560699691d50dc3a8de204f01066be5c2d2 (diff) | |
download | FreeBSD-src-7e2fbaa872a30c5fcfa4206fd26455dcdcddb698.zip FreeBSD-src-7e2fbaa872a30c5fcfa4206fd26455dcdcddb698.tar.gz |
- Verify that when a user supplies a mask that is bigger than the kernel
mask none of the upper bits are set.
- Be more careful about enforcing the boundaries of masks and child sets.
- Introduce a few more CPU_* macros for implementing these tests.
- Change the cpusetsize argument to be bytes rather than bits to match
other apis.
Sponsored by: Nokia
Diffstat (limited to 'sys/kern/kern_cpuset.c')
-rw-r--r-- | sys/kern/kern_cpuset.c | 104 |
1 files changed, 78 insertions, 26 deletions
diff --git a/sys/kern/kern_cpuset.c b/sys/kern/kern_cpuset.c index b262dc1..73fc1f4 100644 --- a/sys/kern/kern_cpuset.c +++ b/sys/kern/kern_cpuset.c @@ -188,26 +188,23 @@ static int _cpuset_create(struct cpuset *set, struct cpuset *parent, cpuset_t *mask, cpusetid_t id) { - int error; - error = 0; + if (!CPU_OVERLAP(&parent->cs_mask, mask)) + return (EDEADLK); CPU_COPY(mask, &set->cs_mask); LIST_INIT(&set->cs_children); refcount_init(&set->cs_ref, 1); set->cs_flags = 0; mtx_lock_spin(&cpuset_lock); CPU_AND(mask, &parent->cs_mask); - if (!CPU_EMPTY(mask)) { - set->cs_id = id; - set->cs_parent = cpuset_ref(parent); - LIST_INSERT_HEAD(&parent->cs_children, set, cs_siblings); - if (set->cs_id != CPUSET_INVALID) - LIST_INSERT_HEAD(&cpuset_ids, set, cs_link); - } else - error = EDEADLK; + set->cs_id = id; + set->cs_parent = cpuset_ref(parent); + LIST_INSERT_HEAD(&parent->cs_children, set, cs_siblings); + if (set->cs_id != CPUSET_INVALID) + LIST_INSERT_HEAD(&cpuset_ids, set, cs_link); mtx_unlock_spin(&cpuset_lock); - return (error); + return (0); } /* @@ -250,11 +247,11 @@ cpuset_testupdate(struct cpuset *set, cpuset_t *mask) mtx_assert(&cpuset_lock, MA_OWNED); if (set->cs_flags & CPU_SET_RDONLY) return (EPERM); - error = 0; + if (!CPU_OVERLAP(&set->cs_mask, mask)) + return (EDEADLK); CPU_COPY(&set->cs_mask, &newmask); CPU_AND(&newmask, mask); - if (CPU_EMPTY(&newmask)) - return (EDEADLK); + error = 0; LIST_FOREACH(nset, &set->cs_children, cs_siblings) if ((error = cpuset_testupdate(nset, &newmask)) != 0) break; @@ -285,11 +282,19 @@ cpuset_update(struct cpuset *set, cpuset_t *mask) static int cpuset_modify(struct cpuset *set, cpuset_t *mask) { + struct cpuset *root; int error; error = suser(curthread); if (error) return (error); + /* + * Verify that we have access to this set of + * cpus. + */ + root = set->cs_parent; + if (root && !CPU_SUBSET(&root->cs_mask, mask)) + return (EINVAL); mtx_lock_spin(&cpuset_lock); error = cpuset_testupdate(set, mask); if (error) @@ -310,12 +315,10 @@ static struct cpuset * cpuset_root(struct cpuset *set) { - mtx_lock_spin(&cpuset_lock); for (; set->cs_parent != NULL; set = set->cs_parent) if (set->cs_flags & CPU_SET_ROOT) break; cpuset_ref(set); - mtx_unlock_spin(&cpuset_lock); return (set); } @@ -329,11 +332,9 @@ static struct cpuset * cpuset_base(struct cpuset *set) { - mtx_lock_spin(&cpuset_lock); if (set->cs_id == CPUSET_INVALID) set = set->cs_parent; cpuset_ref(set); - mtx_unlock_spin(&cpuset_lock); return (set); } @@ -435,6 +436,8 @@ cpuset_shadow(struct cpuset *set, struct cpuset *fset, cpuset_t *mask) parent = set->cs_parent; else parent = set; + if (!CPU_SUBSET(&parent->cs_mask, mask)) + return (EINVAL); return (_cpuset_create(fset, parent, mask, CPUSET_INVALID)); } @@ -456,6 +459,7 @@ cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask) { struct setlist freelist; struct setlist droplist; + struct cpuset *tdset; struct cpuset *nset; struct thread *td; struct proc *p; @@ -491,13 +495,41 @@ cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask) PROC_SLOCK_ASSERT(p, MA_OWNED); /* * Now that the appropriate locks are held and we have enough cpusets, - * replace each thread's cpuset while using deferred release. We + * make sure the operation will succeed before applying changes. The + * proc lock prevents td_cpuset from changing between calls. + */ + error = 0; + FOREACH_THREAD_IN_PROC(p, td) { + thread_lock(td); + tdset = td->td_cpuset; + /* + * Verify that a new mask doesn't specify cpus outside of + * the set the thread is a member of. + */ + if (mask) { + if (tdset->cs_id == CPUSET_INVALID) + tdset = tdset->cs_parent; + if (!CPU_SUBSET(&tdset->cs_mask, mask)) + error = EINVAL; + /* + * Verify that a new set won't leave an existing thread + * mask without a cpu to run on. It can, however, restrict + * the set. + */ + } else if (tdset->cs_id == CPUSET_INVALID) { + if (!CPU_OVERLAP(&set->cs_mask, &tdset->cs_mask)) + error = EINVAL; + } + thread_unlock(td); + if (error) + goto unlock_out; + } + /* + * Replace each thread's cpuset while using deferred release. We * must do this because the PROC_SLOCK has to be held while traversing * the thread list and this limits the type of operations allowed. */ - error = 0; FOREACH_THREAD_IN_PROC(p, td) { - struct cpuset *tdset; thread_lock(td); /* * If we presently have an anonymous set or are applying a @@ -528,6 +560,7 @@ cpuset_setproc(pid_t pid, struct cpuset *set, cpuset_t *mask) sched_affinity(td); thread_unlock(td); } +unlock_out: PROC_SUNLOCK(p); PROC_UNLOCK(p); out: @@ -763,9 +796,10 @@ cpuset_getaffinity(struct thread *td, struct cpuset_getaffinity_args *uap) int error; int size; - if (uap->cpusetsize < CPU_SETSIZE || uap->cpusetsize > CPU_MAXSIZE) + if (uap->cpusetsize < sizeof(cpuset_t) || + uap->cpusetsize * NBBY > CPU_MAXSIZE) return (ERANGE); - size = uap->cpusetsize / NBBY; + size = uap->cpusetsize; mask = malloc(size, M_TEMP, M_WAITOK | M_ZERO); error = cpuset_which(uap->which, uap->id, &p, &ttd, &set); if (error) @@ -846,12 +880,30 @@ cpuset_setaffinity(struct thread *td, struct cpuset_setaffinity_args *uap) cpuset_t *mask; int error; - if (uap->cpusetsize < CPU_SETSIZE || uap->cpusetsize > CPU_MAXSIZE) + if (uap->cpusetsize < sizeof(cpuset_t) || + uap->cpusetsize * NBBY > CPU_MAXSIZE) return (ERANGE); - mask = malloc(uap->cpusetsize / NBBY, M_TEMP, M_WAITOK | M_ZERO); - error = copyin(uap->mask, mask, uap->cpusetsize / NBBY); + mask = malloc(uap->cpusetsize, M_TEMP, M_WAITOK | M_ZERO); + error = copyin(uap->mask, mask, uap->cpusetsize); if (error) goto out; + /* + * Verify that no high bits are set. + */ + if (uap->cpusetsize > sizeof(cpuset_t)) { + char *end; + char *cp; + + end = cp = (char *)&mask->__bits; + end += uap->cpusetsize; + cp += sizeof(cpuset_t); + while (cp != end) + if (*cp++ != 0) { + error = EINVAL; + goto out; + } + + } switch (uap->level) { case CPU_LEVEL_ROOT: case CPU_LEVEL_CPUSET: |