[PATCH] Allow atomic sets of non-overlapping CPU sets for a global cpuset
John Baldwin
jhb at freebsd.org
Fri May 31 16:16:58 UTC 2013
So there's an oddity with cpuset I've run into recently at work. Suppose I
have created a new cpuset and want to change the set of CPUs for that set (say
from a mask of just CPU 1 to a mask of just CPU 2). I can't do that
atomically. I have to first set the mask to contain both the old set (CPU 1)
and the new set (CPU 2) and then change it a second time to only contain the
new set (CPU 2). The reason is that cpuset_modify() runs cpuset_testupdate()
on the set it is about to modify, so when I try to change it in a single
operation the new mask doesn't overlap with the old mask and it fails with
EDEADLK.
% cpuset -c -l 1 /bin/sh
$ cpuset -gi
pid -1 cpuset id: 2
$ cpuset -g
pid -1 mask: 1
$ cpuset -l 2 -s 2
cpuset: setaffinity: Resource deadlock avoided
I think that the correct logic here is that we should only check descendants
of the set we are changing, but not the set we are about to change. The patch
does this and allows my test case above to work:
Index: kern_cpuset.c
===================================================================
--- kern_cpuset.c (revision 251132)
+++ kern_cpuset.c (working copy)
@@ -303,7 +303,7 @@ cpuset_create(struct cpuset **setp, struct cpuset
* empty as well as RDONLY flags.
*/
static int
-cpuset_testupdate(struct cpuset *set, cpuset_t *mask)
+cpuset_testupdate(struct cpuset *set, cpuset_t *mask, int check_mask)
{
struct cpuset *nset;
cpuset_t newmask;
@@ -312,13 +312,16 @@ static int
mtx_assert(&cpuset_lock, MA_OWNED);
if (set->cs_flags & CPU_SET_RDONLY)
return (EPERM);
- if (!CPU_OVERLAP(&set->cs_mask, mask))
- return (EDEADLK);
- CPU_COPY(&set->cs_mask, &newmask);
- CPU_AND(&newmask, mask);
+ if (check_mask) {
+ if (!CPU_OVERLAP(&set->cs_mask, mask))
+ return (EDEADLK);
+ CPU_COPY(&set->cs_mask, &newmask);
+ CPU_AND(&newmask, mask);
+ } else
+ CPU_COPY(mask, &newmask);
error = 0;
LIST_FOREACH(nset, &set->cs_children, cs_siblings)
- if ((error = cpuset_testupdate(nset, &newmask)) != 0)
+ if ((error = cpuset_testupdate(nset, &newmask, 1)) != 0)
break;
return (error);
}
@@ -370,11 +373,11 @@ cpuset_modify(struct cpuset *set, cpuset_t *mask)
if (root && !CPU_SUBSET(&root->cs_mask, mask))
return (EINVAL);
mtx_lock_spin(&cpuset_lock);
- error = cpuset_testupdate(set, mask);
+ error = cpuset_testupdate(set, mask, 0);
if (error)
goto out;
+ CPU_COPY(mask, &set->cs_mask);
cpuset_update(set, mask);
- CPU_COPY(mask, &set->cs_mask);
out:
mtx_unlock_spin(&cpuset_lock);
--
John Baldwin
More information about the freebsd-arch
mailing list