DMA

Introduction

One of the primary motives for DMA is parallelism, allowing I/O devices to transfer data over a but without interrupting the processor.

DMA Output

To understand DMA output, consider an example using DMA to write data to a disk block. The operating system creates a buffer, places data into the buffer, creates a write request (in memory) and passes a pointer to the write request to the I/O device.

Once the device receives the pointer, the processor can continue to executing other processes.

The DMA hardware will user the bus to access the write request and obtain the buffer address to transfer data from buffer to disk. Once an entire block is complete, the disk interrupts the processor.

DMA Input

Similarly, to receive data from a disk block using DMA, the OS creates a buffer to hold the data and issues a read request (in memory), and passes a pointer the request to the I/O device. The processor is used to initiate this request; once the device has the pointer to the request, the processor is free to move on.

The DMA hardware uses the bus to access the request, locate the buffer, and transfers a block from the disk device to the buffer. Once complete, the disk interrupts the processor.

In both cases, only one interrupt occurs per block transferred.

Multiple Buffers

The previous examples of DMA Input/Output are indeed overly simplistic; in actuality, an operating system will allocate multiple request blocks (and buffers) and link them together as a liked list. The address of the list is shared.

Consider what may happen in the context of networking. A linked list is passed by the operating system to the the networking device. Each node in the list contains a pointer to a buffer and a status bit; the network device uses a request node to locate the buffer and uses DMA to copy the packet into the buffer. After generating an interrupt, it moves on to the next request.

Ring Buffer

Most devices use a ring buffer, whereby the last node points to the first. This prevents the devices from reaching the end of the list.

The code snippets included below are merely examples to illustrate the described functionality.

// example node structure

typedef struct _node {
    unsigned int status: 1;
    void *buffer;
    struct node* next;
} request_t;

When receiving input, the operating system initializes each node in the list, setting the status to EMPTY (as an example), and points to a buffer. After filling a buffer, the DMA hardware changes the status to FULL and interrupts.

// Example initialization

#define bitcheck(byte,nbit) ((byte) &   (1<<(nbit)))
#define BUFFER 512
#define EMPTY 0
#define FULL 1

void initialize_ring_buffer(request_t *head)
{
    request_t *current = head;
    while (current->next != NULL) {
        current->buffer = malloc(BUFFER);
        if (!current->buffer) {
            perror("Failed to allocate Buffer");
            return 1;
        }
        
        current->status = EMPTY;
        current = current->next;
    }
}

Last updated