LWN.net Logo

 


 
Summary page
Return to the Kernel page
 
Recent Features

LWN.net Weekly Edition for March 18, 2004

LWN.net Weekly Edition for March 11, 2004

The annotated SCO stock price chart

A grumpy editor's calendar search

LWN.net Weekly Edition for March 4, 2004

Printable page
 

 

Driver porting: Atomic kmaps

This article is part of the LWN Porting Drivers to 2.6 series.
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 to use.

  • 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.


No comments have been posted. Post one now

Copyright (©) 2003, Eklektix, Inc.
Linux (®) is a registered trademark of Linus Torvalds
Powered by Rackspace Managed Hosting.