world leader in high performance signal processing
Trace: » io

Kernel 2.6 Device Drivers, Block I/O structure

The BIO structure provides a way to handle the different data delivery mechanisms required for some block driver I/O systems.

The BIO structure encapsulates the data structures to be written to or read from the device and the device specific access routines. Buffered IO requests to block devices use a generic request structure. Under 2.4 kernels this used a statically defined buffer head structure to define the data to be transferred.

In the 2.6 Driver model the buffer head structure has now been replaced by the optional BIO structure to allow more flexibility in defining the data and any transfer characteristics to the I/O device.

Key elements of the bio structure

This is the main unit of I/O for the block layer and lower layers (ie drivers and stacking drivers)

  from
      linux/include/bio.h
 
  struct bio {
      sector_t                  bi_sector;
      struct bio                *bi_next;       /* request queue link */
      struct block_device       *bi_bdev;
      unsigned long             bi_flags;       /* status, command, etc */
      unsigned long             bi_rw;          /* bottom bits READ/WRITE,
                                                 * top bits priority
                                                 */
      unsigned short            bi_vcnt;        /* how many bio_vec's */
      unsigned short            bi_idx;  /* current index into bvl_vec */
     /* Number of segments in this BIO after
       * physical address coalescing is performed.
       */
      unsigned short            bi_phys_segments;
 
       /* Number of segments after physical and DMA remapping
       * hardware coalescing is performed.
       */
      unsigned short            bi_hw_segments;
 
      unsigned int              bi_size;        /* residual I/O count */
      unsigned int              bi_max_vecs;    /* max bvl_vecs we can hold */
 
      struct bio_vec            *bi_io_vec;     /* the actual vec list */
 
      bio_end_io_t              *bi_end_io;
      atomic_t                  bi_cnt;         /* pin count */
 
      void                      *bi_private;
 
      bio_destructor_t  *bi_destructor; /* destructor */
   };

The BIO Vec Structure

This is a list of data pages that are to be read from or written to the device.

A BIO service will process each page in the list according to the specific requirements of the device.

 from
      linux/include/bio.h
 
   struct bio_vec {
      struct page       *bv_page;
      unsigned int      bv_len;
      unsigned int      bv_offset;
   };

BIO References

Look in the following files for more details

      linux/include/bio.h

      Documentation/block/biodoc.txt

BIO Example Use

This outline example is taken from the MTD flash driver. There are some special requirements for this driver due to the nature of flash devices.

Refer to the code in drivers/mtd/mtdblock.c driver for more details.

Setting up a list of BIO's for data output

  • If the data starts in the middle of a block then read the old data and

overwrite the end of the block with the new data.

  • If the data starts on a page boundary and fills the page then grab a page

from the page cache

  • If the data starts on a page boundary and does not fill the page then

read the old data and overwrite the start of the block with the new data.

  • grab a page

As each page is created with the correct data it is added to the list of pages to be processed by the BIO.

      bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);

[...]

      bio_add_page(bio, page, PAGE_SIZE, 0);

The data is written to the device using the submit_bio

     (in ll_rw_blk.c) function
       struct completion event;
 
      init_completion(&event);
      bio->bi_private = &event;
      bio->bi_end_io = bi_write_complete;
      submit_bio(WRITE_SYNC, bio);
      wait_for_completion(&event);
             ( see bio_endio in bio.c )

This in turn will call:

 
     generic_make_request(bio); // function
        ( ll_rw_blk.c )

This in turn will call the make_request_fn for the Device I/O Queue.

The data passed to the queue function is the BIO structure.

      int ret;
      request_queue_t *q
 
      // this gets the gendisk queue
      q = bdev_get_queue(bio->bi_bdev);
         ( this is returns bdev->bd_disk->queue )
      do {
          ret = q->make_request_fn(q, bio);
           ( see ll_rw_blk.c )
         } while(ret);