[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