This is the default scheduler function for all classes for all resource containers. This is written conforming to the scheduler framework given above. The scheduling starts with root.
The resource container allocates CPU for the containers having fixed CPU share, by an amount proportional to the CPU reservation of the children. Any remaining CPU time is allocated to the tasks in the scheduler bindings of the container. After that it will allocate the remaining CPU time for the time shared child containers, by an amount proportional to their weights. At anytime, if the CPU usage of the container exceeds the allocated time, or all the runnable containers and tasks are given the CPU then the control is passed to parent container by calling the class specific scheduling function of the parent.
The root has a different scheduler function; it returns NULL. This is because once all the child containers and tasks are scheduled by the resource container, it will call the schedule function of the parent to give back the control of the CPU. But for root, this cannot be the case as we have to restart the scheduling of the whole tree. One way to achieve this is to check whether the container is root every time before calling the scheduler function of the parent container and restart the scheduling of the tree if it is root.
Another solution to this problem, which is cleaner and what we follow, is to have a different scheduler function for root. When the scheduler function for the container returns NULL, the schedule() function calls another routine rc_root_schedule() which restarts the scheduling of the tree, and returns the next thread to run.
rc_root_schedule() checks for the runnable flag of the root. If it is zero, then there are no processes in the system and it returns the idle_task. Otherwise it calls rc_schedule() with root and the highest runnable flag as the argument. If this function returns a task, that task is returned to schedule(). Otherwise it calls rc_schedule() again. This is because the root may be looking at some part of the tree (towards right) where there may not be any task in the highest class, so the first call to rc_schedule() will return NULL. The second call to rc_schedule() will restart the scheduling from the left most child and it will eventually find a task to run.
Similarly the update function for root is slightly different from update function of other resource containers, since the parent of root is root itself.
One important implementation issue that has a bearing on the design is that even if we are using regular tree traversal for scheduling, we cannot use any information on the stack between two context switches since the kernel stack changes for each context switch. So any information that is needed across context switches has to be kept in the resource container data structures itself. That is why schedule() calls rc_schedule() to find the next thread, rather than rc_schedule() itself acting as the sole scheduler function.