目录
块I/O子系统对象
1 gendisk:通用磁盘
2 hd_struct:分区
3 block_device:块设备
4 request_queue:请求队列
在块I/O子系统,request_queue的直观理解是请求队列,但实际上它的精确含义要稍微解释一下。首先它可能包括派发队列或和I/O调度队列,也就是来自上层的请求,在队列中经过I/O调度器的合并、排序,最终被提交到块设备驱动。
请求队列可能包含派发队列和I/O调度队列。
派发队列被组织成链表的形式,这是需要向块设备驱动提交的待处理请求链表,链表表头为queue_head域。
I/O调度队列则蕴涵在I/O调度器描述符(即elevator域)中,具体组织形式和调度方式取决于I/O调度算法的实现。
struct request_queue {
struct list_head queue_head;
struct elevator_queue *elevator;
}
将请求提交到低层。
struct request_queue {
request_fn_proc *request_fn;
}
//申请请求队列
struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
{
struct request_queue *q;
//scsi_request_fn这个函数回调最后会赋值给q->request_fn
q = __scsi_alloc_queue(sdev->host, scsi_request_fn);
}
static void scsi_request_fn(struct request_queue *q)
{
//向低层提交命令
scsi_dispatch_cmd(cmd);
}
• int (prep_rq_fn) (struct request_queue *, struct request *)
在处理请求前,构建要发送到驱动器设备的命令的方法。以SCSI为例,我们可以为队列注册一个prep_rq_fn回调,在请求被传给request_fn之前被调用。这个函数的目的是准备一个用于I/O的请求,例如它可以被用来从请求数据构建一个CDB。对于SCSI磁盘,其实现为sd模块的sd_prep_fn函数,负责将块层的读/写请求(即排入该队列的request)翻译为对应的SCSICDB,翻译好的SCSI命令字符串由request描述符的special指针(将块设备驱动层的request转化成scsi命令)
void (softirq_done_fn)(struct request *)
回调函数,在BLOCK_SOFTIRQ软中断下半部调用,以继续处理低层已完成的请求。对于SCSI子系统,其实现为scsi_softirq_done方法,它查看SCSI响应结果,以及感测数据以便确定如何处理这条SCSI命令,或完成,或重试,或进入错误处理。
static void scsi_softirq_done(struct request *rq)
{
disposition = scsi_decide_disposition(cmd);
switch (disposition) {
case SUCCESS:
scsi_finish_command(cmd);
break;
case NEEDS_RETRY:
scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY);
break;
case ADD_TO_MLQUEUE:
scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
break;
default:
if (!scsi_eh_scmd_add(cmd, 0))
scsi_finish_command(cmd);
}
}