1
votes

When the kernel tries to read a block from a hard drive it send a software interrupt, which will be handled by the device driver. If the device driver splits the work of handling the request into top and bottom halves through work queues, how does the kernel knows that the data is not available until the bottom half finishes?

In other words, how does the kernel knows that the driver has not fetched the required block and copied into the supplied buffer yet?

Obviously, if the kernel expects the data is readily available once the top half finishes execution and returns, then it might read junk data.

1

1 Answers

3
votes

The block device driver API has changed a few times since the inception of Linux, but today it basically looks like what follows.

The initialization function calls blk_init_queue, passing a request callback and an optional lock for that queue:

struct request_queue *q;
q = blk_init_queue(my_request_cb, &my_dev->lock);

my_request_cb is a callback that will handle all I/O for that block device. I/O requests will be pushed into this queue and my_request_cb will be called to handle them one after the other, when the kernel block driver layer decides. This queue is then added to the disk:

struct gendisk *disk;
disk->queue = q;

and then the disk is added to the system:

add_disk(disk);

The disk has other informations like the major number, the first minor number and other file operations (open, release, ioctl and others, but no read and no write like found in character devices).

Now, my_request_cb can be called at any time, and won't necessarily be called from the context of the process that initiated a read/write on the block device. This call is asynchronous by the kernel.

This function is declared like this:

static void my_request_cb(struct request_queue *q);

The queue q contains an ordered list of requests to this block device. The function may then look at the next request (blk_fetch_request(q)). To mark a request as completed, it will call blk_end_request_all (other variations exist, depending on the situation).

And this is where I answer your question: the kernel knows a particular block device request is done when its driver calls blk_end_request_all or a similar function for this request. The driver does not have to end a request within my_request_cb: it may, for example, start a DMA transfer, requeue the request, ignore others, and only when the interrupt for a completed DMA transfer is asserted, end it, effectively telling the kernel that this specific read/write operation is completed.

LDD3/chapter 16 can help, but some things changed since 2.6.