How to Read a Sector Using a Bio Request in Linux Kernel

linux kernel, struct bio : how pages are read/written


what is the meaning of bio.bio_phys_segments?

The generic block layer can merge different segments. When the page frames in memory and the chunks of disk data, that are adjacent on the disk, are contiguous then the resultant merge operation creates a larger memory area
which is called physical segment.

Then what is bi_hw_segments?

Yet another merge operation is allowed on architectures that handle the mapping between bus addresses and physical addresses through a dedicated bus circuitry. The memory area resulting from this kind of merge operation is called hardware segment. On the 80 x 86 architecture, which has no such dynamic mapping between bus addresses and physical addresses,hardware segments always coincide with physical segments.

That is, the first disk sectors (starting at bio->bi_sector) will be written from / read to bio->bi_io_vec[0].bv_page then bio->bi_io_vec[1].pv_page etc. 
Is that right? If so, should bio_vec->bv_len be always a multiple of sector_size or 512? Since a page is usually 4096bytes, should bv_offset be exactly one of {0,512,1024,1536,...,3584,4096}? I mean, does it make sense for example to request 100bytes to be written on a page starting at offset 200?

The bi_io_vec contains the page frame for the IO. bv_offset is the offset in the page frame. Before actual writing/reading on the disk every thing is mapped to sector as disk deals in sectors. This doesn't imply that length has to be in the multiple of sectors. So this will result into unaligned read/writes which is taken care by underlying device driver.

if a struct bio is so complex and powerfull, why do we create lists of struct bio and name them struct request and queue them requests in the request_queue? Why not have a bio_queue for the block device where each struct bio is stored until it is serviced?

Request queue is per device structure and takes care of flushing. Every block device has its own request queue. And bio structure is generic entity for IO. If you incorporate request_queue featues into bio then you will create a single global bio_queue and that too very heavy structure. Not a good idea. So basically these two structures serve different purposes in context of IO operation.

Hope it helps.

How can I read a known sector of a disk from Linux kernel module

These functions helped me. I used submit_bio and submitted a bio to the disk. The major and minor shall be assumed as input to read_sector_thread_func

static struct bio * my_mpage_alloc(struct block_device *bdev,
sector_t first_sector, int nr_vecs,
gfp_t gfp_flags)
{
struct bio *bio;

bio = my_bio_alloc(gfp_flags, nr_vecs);

if (bio == NULL && (current->flags & PF_MEMALLOC)) {
while (!bio && (nr_vecs /= 2))
bio = my_bio_alloc(gfp_flags, nr_vecs);
}

if (bio) {
//bio->bi_bdev = bdev;
bio->bi_iter.bi_sector = first_sector;
}
return bio;
}

static void src_endio(struct bio *bio)
{
if (bio->bi_private) {
complete(bio->bi_private);
}
return;
}

static int read_sector_thread_func(void *data)
{
struct block_device *r_bdev = NULL;
struct bio *r_bio = NULL;
struct page *page = NULL;
int part = NULL;
int len = 0;
struct gendisk * disk = NULL;
unsigned char * addr;
u64 first_sector_num = 0;

disk = get_gendisk(MKDEV(global_dev_major, global_dev_minor), &part);

DECLARE_COMPLETION_ONSTACK(r_wait);

page = alloc_pages(GFP_ATOMIC, 0);
printk("%d : %s ", __LINE__, __func__);
r_bio = my_mpage_alloc(r_bdev, first_sector_num, 1, GFP_ATOMIC);

if(r_bio==NULL)
{
printk("bio is NULL");
return;
}

bio_add_page(r_bio, page, 512, 0);
r_bio->bi_private = &r_wait;
r_bio->bi_end_io = src_endio;
r_bio->bi_disk = disk;

bio_associate_blkg(r_bio);
bio_get(r_bio);
bio_set_op_attrs(r_bio, REQ_OP_READ, 0);

submit_bio(r_bio);

wait_for_completion_io(&r_wait);

bio_put(r_bio);
return 0;
}

bio_endio - how to get disk sectors content

It need to save .bi_iter field before submiting BIO request to backend device driver, and restore it in the __my_bio_endio () routine.
So more details of processing concurrent BIO is described in this topic

Read chunks in a disk (kernel programming)

page_address will return a pointer (void*) that you can use to read or write the content of the page. However, if it is a page in high memory it will only work if the page is mapped.

Using kmap may be preferable, as it will do that check for you :

void *kmap(struct page *page)
{
might_sleep();
if (!PageHighMem(page))
return page_address(page);
return kmap_high(page);
}


Related Topics



Leave a reply



Submit