|
|
Linux » Books » Developer »
Linux Device Driver Programmer's Guide,Porting to SGI Altix Systems
(document number: 007-4520-007 / published: 2008-09-24)
table of contents | additional info | download find in page
Chapter 10. Device Driver Memory Usage
Device drivers can dynamically allocate memory
via the many memory management interfaces that Linux provides. This chapter
describes memory interfaces that return addresses that can be mapped as
direct memory access (DMA) addresses.
Device Driver Memory Allocation
Device drivers should
never try to use kernel text or data for DMA. It is also inappropriate
to use memory areas allocated via the vmalloc routine
as a DMA area, even though it can be done. The vmalloc
routine allows the driver to allocate a large contiguous virtual address
range. However, the address cannot be used as input to any of the DMA
map routines.
Allocating Page Boundary Memory
Device drivers can use the __get_free_page()
routine to allocate a page of memory that can be mapped as a DMA address,
as in the following example: {
char *buf;
buf = (char*) __get_free_page(GFP_KERNEL);
} |
This routine returns a value of type unsigned long.
Memory is always allocated from the node that the thread is executing
first.
Memory areas allocated via this interface are region 7 addresses
and identity mapped addresses. These kernel virtual addresses can be used
as input to any of the PCI DMA map routines.
Allocating Page Boundary Memory on Specific Nodes
Device drivers can request memory on a specific node. The
rationale for a specific request would be data placement for performance
reasons. If you need to allocate memory on a specific node, use the
following interface: struct page * alloc_pages_node(int node_id, unsigned int gfp_mask, unsigned int order) |
The address returned in struct page * can be
used as input to any of the PCI DMA map routines, as in the following
example:
struct page * my_page;
my_page = alloc_pages_node(node_id, gfp_mask, order);
dma_addr = pci_map_single(my_dev, my_page -> virtual, size, direction); |
 | Note: The interface is available only if CONFIG_NUMA
is configured on, as in SGI Altix 3000 systems.
|
Allocating Byte-Range Memory
Device drivers can
ask for memory with any arbitrary length by using the following routine: void * kmalloc (size_t size, int flags); |
This routine returns region 7 and identity mapped kernel virtual
addresses. This kernel virtual address can also be used as input into
any of the PCI mapping routines.
The advantage of this routine is that it provides a single starting
virtual and physical address that is contiguous. However, if the kernel
cannot find a contiguous area large enough, this call will fail.
Accessing the User Memory Area
It is very important for drivers,
when reading or writing into the user area, to verify first that the specified
user address is valid. Linux provides two routines that enable kernel
level code for verification.
For verification within the user virtual area: int access_ok(type, user_address, size); |
For verification when the area is actually mapped with the correct
access rights: int verify_area(type, user_address, size);
|
Where: | type: | | VERIFY_READ - Verify for read access
VERIFY_WRITE - Verify for read/write access
| | user_address: | | User's virtual address
| | size: | | Size of the area in bytes
|
The following routines write or read a a single value to or from
the user area. The size of the single value depends on the size of (
*pointer): int put_user(value, pointer)
int get_user(value, pointer)
|
The following optimized routines write or read the given number
of byte count to or from the user area: int copy_to_user(user_address, kernel_address, byte_count);
int copy_from_user(kernel_address, user_address, byte_count); |
The following routines provide string manipulation from the user
area: long strlen_user(user_address);
long strnlen_user(user_address, count);
long strncpy_from_user(kernel_address, user_address, count); |
An important point to note is that all of these user area access
routines actually perform access verification first. The following corresponding
set of routines are available to the driver, which do not perform any
access verifications. The names of these routines are the same as the
precedint routines but with a double underscore (__) prepended. int __put_user(value, pointer)
int __get_user(value, pointer)
int __copy_to_user(user_address, kernel_address, byte_count)
int __copy_from_user(kernel_address, user_address, byte_count)
long __strlen_user(user_address)
long __strnlen_user(user_address, count)
long __strncpy_from_user(kernel_address, user_address, count) |
Disabling Validity Checking
Sometimes, kernel level routines require
calls to system call handlers, as do user level routines. However, all
of these handlers expect addresses in the call to be user addresses.
A kernel level routine calling a system call interface with a kernel virtual
address will definitely fail when the system call interface performs address
validation. For example, the following call will fail when sys_open
performs any address verification: sys_open("/dev/mydriver_config_file", O_RDONLY, 0); |
Linux provides the following set of routines that, in effect, disable
address validity checking: current_segment = get_fs();
set_fs(active_segment); |
The current_segment and active_segment
values are either KERNEL_DS or
USER_DS. If the current data segment is KERNEL_DS,
the access verification routines do not perform any verification. If the
current data segment is USER_DS, the access routines
verify that the specified user addresses are valid.
Therefore, if your driver requires negation of all address verification,
use the following code: mm_segment_t current_segment;
current_segment = get_fs(); /* Save current segment */
set_fs(KERNEL_DS);
fd = sys_open("/dev/mydriver_config_file", O_RDONLY, 0);
set_fs(current_segment) /* Restore saved segment */ |
Directly Mapping User Virtual Addresses
In general, device drivers allocate kernel buffers
as intermediate transfer areas to move data between the user area and
an external device. Sometimes, but not always, it can be more performance-efficient
to transfer data directly between the user area and the external device,
without incurring the additional CPU time for copying between kernel buffers
and the user area, and other such activities. Linux provides the Kernel
IO Buffer (kiobuf) facility, which allows a user area
to be mapped to kernel virtual space. Once this mapping is performed,
kernel virtual addresses can be DMA mapped to allow external devices to
write directly into the mapped user area.
kiobuf provides the following interfaces: Allocate and free kiobuf structures: int alloc_kiovec(int number_pages, struct kiobuf **iovec);
void free_kiovec(int number_pages, struct kiobuf **iovec); |
Map/unmap user addresses to kernel virtual addresses: int map_user_kiobuf(int rw, struct kiobuf *iobuf, unsigned long va, size_t len)
void unmap_kiobuf (struct kiobuf *iobuf) |
Lock the pages in the kiobuf: int lock_kiovec(int nr, struct kiobuf *iovec[], int wait)
int unlock_kiovec(int nr, struct kiobuf *iovec[]) |
For more information regarding this feature, see Linux
Device Drivers, chapter 13, “MMap and Dma.”
Linux Device Driver Programmer's Guide,Porting to SGI Altix Systems
(document number: 007-4520-007 / published: 2008-09-24)
table of contents | additional info | download
Front Matter
New Features in This Guide
About This Guide
Chapter 1. Introduction
Chapter 2. Architecture
Chapter 3. PCI-X Device Attachment
Chapter 4. PCI System Initialization
Chapter 5. Finding Your PCI Device
Chapter 6. PCI/PCI-X Configuration Space
Chapter 7. PCI-X I/O and Memory Resources
Chapter 8. PCI-X Interrupt Mechanism
Chapter 9. PCI-X Direct Memory Access (DMA)
Chapter 10. Device Driver Memory Usage
Chapter 11. Time Management
Chapter 12. Building Linux Kernels and Modules
Appendix A. Memory Operation Ordering on SGI Altix Systems
Index
home/search |
what's new |
help
|
|
|