linux块设备驱动之请求过程剖析
2015-07-12 21:31
411 查看
当设备完成在一个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结构。
下面的函数是处理一个req,其中最主要的是rq_for_each_bio宏,该宏是遍历req请求中的每一个bio;而sbull_xfer_bio()函数是处理一个bio结构的函数;
如果需要直接访问这些页,需要首先保证正确的内核虚拟地址是存在的,可以使用下面的函数进行映射: char *__bio_kmap_atomic(struct bio *bio, int i, enum km_type type);这个函数直接映射了当前段号为i的bio_vec中的缓冲区。
bio_for_each_segment宏遍历了bio结构中的每一个段,获取内核虚拟地址以访问缓冲地址,然后调用sbull_transfer函数实现真正的数据操作。
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块设备驱动之请求过程剖析
相关文章推荐
- linux shenll 远程执行本地脚本或者执行命令
- 鸟哥Linux私房菜知识汇总8至9章
- git使用详解
- Linux常用命令集合
- 一些linux命令
- Linux 文件系统
- linux 管道
- Linux 查看IP、查看Gateway、查看DNS
- linux 输入输出重定向
- linux 环境变量
- GNU Linux中的SO_RCVLOWAT和SO_SNDLOWAT说明
- linux命令
- linux用户管理,运行级别
- 在 Linux 中热添加内存(Suse11)
- linux下线程的创建,转帖
- linux文件目录
- linux命令
- linux命令及开发java
- linux下几种目标文件的分析
- linux使用技巧