3. Avoiding Bounce Buffers

This section provides information on applying and using the bounce buffer patch on the Linux 2.4 kernel. The bounce buffer patch, written by Jens Axboe, enables device drivers that support Direct Memory Access (DMA) I/O to high-address physical memory to avoid bounce buffers.

This document provides a brief overview on memory and addressing in the Linux kernel, followed by information on why and how to make use of the bounce buffer patch.

3.1. Memory and Addressing in the Linux 2.4 Kernel

The Linux 2.4 kernel includes configuration options for specifying the amount of physical memory in the target computer. By default, the configuration is limited to the amount of memory that can be directly mapped into the kernel's virtual address space. The mapping starts at PAGE_OFFSET (normally 0xC0000000). On i386 systems the default mapping scheme limits the kernel-mode addressability to the first gigabyte (GB) of physical memory, also known as low memory. High-address physical memory is normally the memory above 1 GB. This memory is not directly accessible or permanently mapped by the kernel. Support for high-address physical memory is an option that is enabled during configuration of the Linux kernel.

3.2. The Problem with Bounce Buffers

When DMA I/O is performed to or from high-address physical memory, an area is allocated in memory known as a bounce buffer. When data travels between a device and high-address physical memory, it is first copied through the bounce buffer.

Systems with a large amount of high-address physical memory and intense I/O activity can create a large number of bounce buffer data copies. The excessive number of data copies can lead to a shortage of memory and performance degradation.

Peripheral component interface (PCI) devices normally address up to 4 GB of physical memory. When a bounce buffer is used for high-address physical memory that is below 4 GB, time and memory are wasted because the peripheral has the ability to address that memory directly. Using the bounce buffer patch can decrease, and possibly eliminate, the use of bounce buffers.

3.3. Locating the Patch

The latest version of the bounce buffer patch is block-highmem-all-<version>.gz , and it is available from Andrea Arcangeli's -aa series kernels at http://kernel.org/pub/linux/kernel/people/andrea/kernels/v2.4/.

3.3.1. Configuring the Linux Kernel to Avoid Bounce Buffers

This section includes information on configuring the Linux kernel to avoid bounce buffers. The Linux Kernel-HOWTO at http://www.linuxdoc.org/HOWTO/Kernel-HOWTO.html explains the process of re-compiling the Linux kernel.

The following kernel configuration options are required to enable the bounce buffer patch:

  • Development Code - To enable the configurator to display the High I/O Support option, select Code Maturity Level Options category and specify "y" to prompt for development and/or incomplete code/drivers.

  • High-Address Physical Memory Support - To enable high memory support for physical memory that is greater than 1 GB, select Processor type and feature category, and enter the actual amount of physical memory under the High Memory Support option.

  • High-Address Physical Memory I/O Support - To enable high DMA I/O to physical addresses greater than 1 GB, select Processor type and feature category, and enter "y" to HIGHMEM I/O support option. This configuration option is a new option introduced by the bounce buffer patch.

3.4. Modifying Your Device Driver to Avoid Bounce Buffers

The entire process of rebuilding a Linux device driver is beyond the scope of this document. However, additional information is available at http://www.xml.com/ldd/chapter/book/index.html.

Note

Modifications are required for all device drivers that are not listed above in the Enabled Device Drivers section.

If your device driver is capable of high-address physical memory DMA I/O, you can modify your device driver to make use of the bounce buffer patch by making the following modifications:

For SCSI Device Drivers: set the highmem_io bit in the Scsi_Host_Template structure, then call scsi_register ( ).

For IDE Drivers: set the highmem in the ide_hwif_t structure, then call ide_dmaproc ( ).

  1. Call pci_set_dma_mask ( ) to specify the address bits that the device can successfully use on DMA operations. Modify the code as follows:

    int pci_set_dma_mask (struct pci_dev *pdev, dma_addr_t mask);

    If DMA I/O can be supported with the specified mask, pci_set_dma_mask ( ) will set pdev->dma_mask and return 0. For SCSI or IDE, the mask value will also be passed by the mid-level drivers to blk_queue_bounce_limit ( ) so that bounce buffers are not created for memory directly addressable by the device. Drivers other than SCSI or IDE must call blk_queue_bounce_limit ( ) directly. Modify the code as follows:

    void blk_queue_bounce_limit (request_queue_t *q, u64 dma_addr);

  2. Use pci_map_page (dev, page, offset, size, direction) to map a memory region so that it is accessible by the peripheral device, instead of pci_map_single (dev, address, size, direction).

    The address parameter for pci_map_single ( ) correlates to the page and offset parameters of pci_map_page ( ). pci_map_page ( ) supports both the high and low physical memory.

    Use the virt_to_page ( ) macro to convert an address to a page/offset pair. The macro is defined by including pc.h. For example:

    void *address;
    struct page *page;
    unsigned long offset;

    page = virt_to_page (address);
    offset = (unsigned long) address & ~PAGE_MASK;

    Call pci_unmap_page ( ) after the DMA I/O transfer is complete, the mapping established by pci_map_page ( ) should be removed by calling pci_unmap_page ( ).

    ImportantImportant:
     

    pci_map_single ( ) is implemented using virt_to_bus ( ) . This function call handles low memory addresses only. Drivers supporting high-address physical memory should no longer call virt_to_bus ( ) or bus_to_virt ( ).

  3. Set your driver to map a scatter-gather DMA operation using pci_map_sg ( ). The driver should set the page and offset fields instead of the address field of the scatterlist structure. Refer to step 3 for converting an address to a page/offset pair.

    Note

    If your driver is already using the PCI DMA API, continue to use pci_map_page ( ) or pci_map_sg ( ) as appropriate. However, do not use the address field of the scatterlist structure.