High memory can be a pain to work with. The addressing limitations of
32-bit processors make it impossible to map all of high memory into the
kernel's address space. So various workarounds must be employed to manage
high memory portably; this need is one of the reasons for the increasing
use of struct page
pointers in the kernel.
When the kernel needs to access a high memory page directly, an ad hoc
memory mapping must be set up. This is the purpose of the functions
kmap() and kunmap(), which have existed since high memory
support was first implemented. kmap() is relatively expensive to
use, however; it requires global page table changes, and it can put the
calling function to sleep. It is thus a poor fit to many parts of the
kernel where performance is important.
To address these performance issues, a new type of kernel mapping (the
"atomic kmap") has been created (they actually existed, in a slightly
different form, in 2.4.1). Atomic kmaps are intended for short-term
use in small, atomic sections of kernel code; it is illegal to sleep while
holding an atomic kmap. Atomic kmaps are a per-CPU structure; given the
constraints on their use, there is no point in sharing them across
processors. They are also available in very limited numbers.
In fact, there are only about a dozen atomic kmap slots available on each
processor (the actual number is architecture-dependent), and users of
atomic kmaps must specify which slot to use. A new enumerated type
(km_type) has been defined to give names to the atomic kmap
slots. The slots that will be of most interest to driver writers are:
- KM_USER0, KM_USER1. These slots are to be used
by code called from user space (i.e. system calls).
- KM_IRQ0, KM_IRQ1. Slots for interrupt handlers
- KM_SOFTIRQ0, KM_SOFTIRQ1; for code running out of
a software interrupt, such as a tasklet.
Several other slots exist, but they have been set aside for specific
purposes and should not be used.
The actual interface for obtaining an atomic kmap is:
void *kmap_atomic(struct page *page, enum km_type type);
The return value is a kernel virtual address which may be used to address
the given page. kmap_atomic() will always succeed, since the slot
to use has been given to it. It will also disable preemption while the
atomic kmap is held.
When you have finished with the atomic kmap, you should undo it with:
void kunmap_atomic(void *address, enum km_type type);
Users of atomic kmaps should be very aware of the fact that nothing in the
kernel prevents one function from stepping on another function's mappings.
Code which holds atomic kmaps thus needs to be short and simple. If you
are using one of the KM_IRQ slots, you should have locally
disabled interrupts first. As long
as everybody is careful, conflicts over atomic kmap slots do not arise.
Should you need to obtain a struct page pointer for an address
obtained from kmap_atomic(), you can use:
struct page *kmap_atomic_to_page(void *address);
If you are wanting to map buffers obtained from the block layer in a BIO
structure, you should use the BIO-specific kmap functions (described in the BIO article) instead.
Atomic kmaps are a useful resource for performance-critical code. They
should not be overused, however. For any code which might sleep, or which
can afford to wait for a mapping, the old standard kmap() should
be used instead.
to post comments)