Minor nit: you ended up using CPU_ZERO() instead; another good example could be to create a new set, grab the affinity, then clear half of the cpus; we can defer on that one, though.
I'd leave CPU_CLR in as an xref even if the example didn't end up using it and add CPU_ZERO to this list; all three are useful to point out.
I feel that I made the API overly complicated because I was trying to unify an API for administration and for programming. This example is more complex than necessary if you are simply trying to change the set for the process. A non-anonymous or numeric set is only required if you wish to refer to it later. Think of it more like a process group where you want to be able to apply that constraint to multiple things at once. Normal programs simply exist within their current process group and only in specific circumstances do you create a new one. The numbered sets are this 'group'. They exist in the middle of the hierarchy with the root set above them giving an absolute limit on available CPUs that may be from jail or the actual system. Below them exists anonymous sets for programs that have constrained themselves to a subset of the numbered set.
If you simply set the mask on a process or thread it will use an anonymous set that is a subset of the existing set. In this example you have created a numerically identified set which clones the root set from the hierarchy (jail or system root). However, you then modify the process to use an anonymous set and don't really use the numbered set, only isolating yourself from potential changes to the current numbered set. This happens because the level is specified as WHICH. Meaning, operate on this pid in particular. CPU_LEVEL_CPUSET would have internally operated on the set that the pid refers to. Or since you have the id of the cpuset you just created you could operate on CPU_WHICH_CPUSET and pass the set in the id. The LEVEL argument tells you which level of the cpuset graph you are operating on and the WHICH tells you how to look up the cpuset in the graph, or what the identifier references.
If that seems convoluted it's because there are two intended programming models here and the example mixes them.
For the programmer simply wishing to bind their threads they should just use LEVEL_WHICH, WHICH_PID/TID and completely ignore numbered sets. They may want to get their allowed cpus with cpuset_getaffinity() before updating the mask or if the administrator has restricted the CPUs they have access to with jails or specific sets the affinity may fail.
The sets are really intended to be manipulated directly by cpuset(1) and the rest of the API is there to support that usage model. I did a survey of other operating systems and I found these same complexities but because they are split among multiple APIs it may be easier to parse but much more verbose to implement. The cpuset API as it exists probably should have convenience wrappers in front of it to hide some of this complexity. pthread_setaffinity is a perhaps simpler example.
Following feedback from jeff, break the example into two:
- The first creates a new anonymous set associated with the current process, and updates its affinity.
- The second creates a new named set associated with the current process, and updates its affinity. The CPU set ID can then be used in the future.
Updating D27803: Add a code example to cpuset(2) showing how to modify the affinity of the
current process. Improve cross referencing.
I’d like to think about committing this, if folk wouldn’t mind updating their reviews, with the following commit message:
Add a code example to cpuset(2) showing how to modify the affinity of the current process. Improve cross referencing. MFC after: 1 week Reviewed by: jeff, jrtc27, kevans
Hmm. I'm not seeing this one. Possibly you've not spotted the comma in ".Xr CPU_SET 9 ,"? Or, I'm just not seeing it due to staring at mandoc too much, and you should be more specific :-).