4.7 Switching Address Spaces
In Chapter 3, Processes, Tasks, and Threads, we discussed how Linux switches the execution context from one thread to another. We did not discuss how the address space is switched. The reason is that Linux treats context switching and address-space switching as separate operations. This makes sense because a context switch triggers an address-space switch only if the old and the new thread do not share the same address space. Conversely, there are occasions, such as an execve() system call, where the address space is switched but the currently executing thread remains the same. In other words, not every context switch causes an address-space switch, and vice versaa good indication that these are fundamentally separate operations.
4.7.1 Address-space switch interface
Linux uses the address-space switch interface (file include/asm/mmu_context.h) in Figure 4.39 to abstract platform differences in how an address-space switch can be effected. The activate_mm() routine switches the address space of the currently executing thread. It takes two arguments, prev mm and next_mm, which are pointers to the mm structure of the old and the new address space, respectively. Similarly, switch_mm() is called when the address space is switched as part of a context switch. The first two arguments have the same meaning as for activate_mm(), but this routine receives two additional arguments: next_task and cpu. The next_task argument is a pointer to the task structure of the thread that will be executed next. As usual, the currently running thread is implicitly identified by global variable current. The cpu argument is the unique ID of the CPU that is performing the address-space switch (see Chapter 8, Symmetric Multiprocessing).
Figure 4.39. Kernel interface to switch address spaces.
In an ideal world, switch_mm() would not be necessary and activate_mm() could be used instead. However, Linux provides separate routines to accommodate platforms that inextricably (and incorrectly) tie together context switching and address-space switching. On those platforms, an address-space switch can be effected only through a context switch, and activate_mm() must implicitly perform a dummy context switch. This means that if activate_mm() were used in place of switch_mm(), the dummy context switch would almost immediately be followed by a real context switch, which would be inefficient. In other words, switch_mm() can be thought of as a version of activate_mm() that is optimized for the case where it is known that the address-space switch will quickly be followed by a context switch. Of course, on all other platforms, switch_mm() can be implemented directly in terms of activate_mm().
4.7.2 IA-64 implementation
On Linux/ia64, switching the address space involves three simple steps:
Set current page-table pointer to the global directory of the new address space.
Ensure that the new address space has a valid ASN (region ID) by calling get_mmu-context().
Load the region ID of new address space into region registers by calling reload_context().
The first step is necessary only because Linux/ia64 maintains the physical address of the global directory of the current address space in kernel register k6. This simplifies the TLB fault handler slightly and also lets it run more efficiently.
The IA-64 architecture leaves context and address-space switching entirely to software, so there is no problem in keeping the two operations separate. Thus, switch_mm() can be implemented simply as a call to activate_mm(), passing the first two arguments, prev_mm and next_mm, and dropping the remaining three arguments.