diff options
Diffstat (limited to 'Documentation/vm/highmem.rst')
-rw-r--r-- | Documentation/vm/highmem.rst | 100 |
1 files changed, 60 insertions, 40 deletions
diff --git a/Documentation/vm/highmem.rst b/Documentation/vm/highmem.rst index 0f69a9fec34d..c9887f241c6c 100644 --- a/Documentation/vm/highmem.rst +++ b/Documentation/vm/highmem.rst @@ -50,61 +50,74 @@ space when they use mm context tags. Temporary Virtual Mappings ========================== -The kernel contains several ways of creating temporary mappings: +The kernel contains several ways of creating temporary mappings. The following +list shows them in order of preference of use. -* vmap(). This can be used to make a long duration mapping of multiple - physical pages into a contiguous virtual space. It needs global - synchronization to unmap. - -* kmap(). This permits a short duration mapping of a single page. It needs - global synchronization, but is amortized somewhat. It is also prone to - deadlocks when using in a nested fashion, and so it is not recommended for - new code. - -* kmap_atomic(). This permits a very short duration mapping of a single - page. Since the mapping is restricted to the CPU that issued it, it - performs well, but the issuing task is therefore required to stay on that - CPU until it has finished, lest some other task displace its mappings. +* kmap_local_page(). This function is used to require short term mappings. + It can be invoked from any context (including interrupts) but the mappings + can only be used in the context which acquired them. - kmap_atomic() may also be used by interrupt contexts, since it is does not - sleep and the caller may not sleep until after kunmap_atomic() is called. + This function should be preferred, where feasible, over all the others. - It may be assumed that k[un]map_atomic() won't fail. + These mappings are thread-local and CPU-local, meaning that the mapping + can only be accessed from within this thread and the thread is bound the + CPU while the mapping is active. Even if the thread is preempted (since + preemption is never disabled by the function) the CPU can not be + unplugged from the system via CPU-hotplug until the mapping is disposed. + It's valid to take pagefaults in a local kmap region, unless the context + in which the local mapping is acquired does not allow it for other reasons. -Using kmap_atomic -================= + kmap_local_page() always returns a valid virtual address and it is assumed + that kunmap_local() will never fail. -When and where to use kmap_atomic() is straightforward. It is used when code -wants to access the contents of a page that might be allocated from high memory -(see __GFP_HIGHMEM), for example a page in the pagecache. The API has two -functions, and they can be used in a manner similar to the following:: + Nesting kmap_local_page() and kmap_atomic() mappings is allowed to a certain + extent (up to KMAP_TYPE_NR) but their invocations have to be strictly ordered + because the map implementation is stack based. See kmap_local_page() kdocs + (included in the "Functions" section) for details on how to manage nested + mappings. - /* Find the page of interest. */ - struct page *page = find_get_page(mapping, offset); +* kmap_atomic(). This permits a very short duration mapping of a single + page. Since the mapping is restricted to the CPU that issued it, it + performs well, but the issuing task is therefore required to stay on that + CPU until it has finished, lest some other task displace its mappings. - /* Gain access to the contents of that page. */ - void *vaddr = kmap_atomic(page); + kmap_atomic() may also be used by interrupt contexts, since it does not + sleep and the callers too may not sleep until after kunmap_atomic() is + called. - /* Do something to the contents of that page. */ - memset(vaddr, 0, PAGE_SIZE); + Each call of kmap_atomic() in the kernel creates a non-preemptible section + and disable pagefaults. This could be a source of unwanted latency. Therefore + users should prefer kmap_local_page() instead of kmap_atomic(). - /* Unmap that page. */ - kunmap_atomic(vaddr); + It is assumed that k[un]map_atomic() won't fail. -Note that the kunmap_atomic() call takes the result of the kmap_atomic() call -not the argument. +* kmap(). This should be used to make short duration mapping of a single + page with no restrictions on preemption or migration. It comes with an + overhead as mapping space is restricted and protected by a global lock + for synchronization. When mapping is no longer needed, the address that + the page was mapped to must be released with kunmap(). -If you need to map two pages because you want to copy from one page to -another you need to keep the kmap_atomic calls strictly nested, like:: + Mapping changes must be propagated across all the CPUs. kmap() also + requires global TLB invalidation when the kmap's pool wraps and it might + block when the mapping space is fully utilized until a slot becomes + available. Therefore, kmap() is only callable from preemptible context. - vaddr1 = kmap_atomic(page1); - vaddr2 = kmap_atomic(page2); + All the above work is necessary if a mapping must last for a relatively + long time but the bulk of high-memory mappings in the kernel are + short-lived and only used in one place. This means that the cost of + kmap() is mostly wasted in such cases. kmap() was not intended for long + term mappings but it has morphed in that direction and its use is + strongly discouraged in newer code and the set of the preceding functions + should be preferred. - memcpy(vaddr1, vaddr2, PAGE_SIZE); + On 64-bit systems, calls to kmap_local_page(), kmap_atomic() and kmap() have + no real work to do because a 64-bit address space is more than sufficient to + address all the physical memory whose pages are permanently mapped. - kunmap_atomic(vaddr2); - kunmap_atomic(vaddr1); +* vmap(). This can be used to make a long duration mapping of multiple + physical pages into a contiguous virtual space. It needs global + synchronization to unmap. Cost of Temporary Mappings @@ -145,3 +158,10 @@ The general recommendation is that you don't use more than 8GiB on a 32-bit machine - although more might work for you and your workload, you're pretty much on your own - don't expect kernel developers to really care much if things come apart. + + +Functions +========= + +.. kernel-doc:: include/linux/highmem.h +.. kernel-doc:: include/linux/highmem-internal.h |