当设备完成在一个I/O请求的部分或者全部的扇区时,必须调用下面的函数通知块设备子系统:int end_that_request_first(struct request *req, int success, int count);该函数告诉块设备代码:驱动程序从前一次结束的地方开始,完成了规定数目的扇区的传输。请注意必须报告从第一个扇区到最后一个扇区的完成情况;
end_that_request_first 的返回值表明该请求中的所有扇区是否被传输。返回0表示把count个扇区已经传输完成,此时必须调用blkdev_dequeue_request函数删除请求,并把其传递给 void end_that_request_last(struct request *req);
end_that_request_last 通知任何等待已经完成请求的对象,并重复利用该request结构。
dev->q = blk_init_queue(sbull_full_request, &dev->lock);
static void sbull_full_request(request_queue_t *q)
{
struct request *req;
int sectors_xferred;
struct sbull_dev *dev = q->queuedata;
// elv_next_request函数表示从队列中获取下一个req
while ((req = elv_next_request(q)) != NULL){
if (!blk_fs_request(req)) {
printk(KERN_NOTICE "Skip non-fs requeest!\n");
end_request(req, 0);
continue;
}
// sbull_xfer_request函数才是真正处理req请求的
sectors_xferred = sbull_xfer_request(dev, req);
if (! end_that_request_first(req, 1, sectors_xferred)){
blkdev_dequeue_request(req);
end_that_request_last(req);
}
}
}
void end_request(struct request *req, int uptodate)
{
if (! end_that_request_first(req, uptodate, req->hard_cur_sectors)){
add_disk_randomness(req->rq_disk);
blkdev_dequeue_request(req);
end_that_request_last(req);
}
}
下面的函数是处理一个req,其中最主要的是rq_for_each_bio宏,该宏是遍历req请求中的每一个bio;而sbull_xfer_bio()函数是处理一个bio结构的函数;
static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
{
struct bio *bio;
int nset = 0;
rq_for_each_bio(bio, req){// 遍历req中的每一个bio
sbull_xfer_bio(dev, bio);// 该函数处理bio
nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
}
return nsect;
}
下面的函数sbull_xfer_bio是处理一个bio结构,而bio结构主要的是数组结构,所以bio_for_each_segment宏是遍历bio中的数组结构的,其中bvec指向当前的bio_vec入口,i是当前的段号。使用这些值来建立DMA传输。
如果需要直接访问这些页,需要首先保证正确的内核虚拟地址是存在的,可以使用下面的函数进行映射: char *__bio_kmap_atomic(struct bio *bio, int i, enum km_type type);这个函数直接映射了当前段号为i的bio_vec中的缓冲区。
bio_for_each_segment宏遍历了bio结构中的每一个段,获取内核虚拟地址以访问缓冲地址,然后调用sbull_transfer函数实现真正的数据操作。
static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
bio_for_each_segment(bvec, bio, i){
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
sbull_transfer(dev, sector, bio_cur_sector(bio),
buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0;
}
// sector开始扇区的索引号,指的是512字节的扇区,如果是2048字节的扇区,则要sector/4再传递
// nsect 表示要传递多少个扇区; buffer 数据缓存的地址指针;write 表示数据传递的方向,即:read/write;
static void sbull_transfer(struct sbull_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
{
unsigned long offset = sector*KERNEL_SECTOR_SIZE;
unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;
if ((offset + nbytes) > dev->size){
printk(KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
return;
}
if (write)
memcpy(dev->data + offset, buffer, nbytes);
else
memcpy(buffer, dev->data + offset, nbytes);
}
转载地址:
linux块设备驱动之请求过程剖析