三、块设备驱动层
块设备的关系图如图2,一个分区或一个硬盘都可能是block_device,它一个硬盘只有一个gendisk结构,且有可能有多个分区hd_struct。
![](http://blog.chinaunix.net/photo/96790_101224095823.jpg)
图2
我们来看一个IDE硬盘设备的驱动,在此我们不关心IDE总线的驱动,只是将其执行路线列出来。
static int ide_init_queue(ide_drive_t *drive)
{
request_queue_t *q;
ide_hwif_t *hwif = HWIF(drive);
int max_sectors = 256;
int max_sg_entries = PRD_ENTRIES;
//分配一个请求队列,由IDE总线去帮助完成,简化了特定块设备的工作
q = blk_init_queue_node(do_ide_request, &ide_lock, hwif_to_node(hwif));
//初始化队列中的一些参数
q->queuedata = drive;
blk_queue_segment_boundary(q, 0xffff);
……
blk_queue_max_hw_segments(q, max_sg_entries);
blk_queue_max_phys_segments(q, max_sg_entries);
/* assign drive queue */
drive->queue = q;
return 0;
}
request_queue_t *
blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
{
request_queue_t *q = blk_alloc_queue_node(GFP_KERNEL, node_id);
q->node = node_id;
if (blk_init_free_list(q)) {
kmem_cache_free(requestq_cachep, q);
return NULL;
}
q->request_fn = rfn; //由上可以看到,在ide-disk中,为do_ide_request
q->prep_rq_fn = NULL;
q->unplug_fn = generic_unplug_device;
q->queue_flags = (1 << QUEUE_FLAG_CLUSTER);
q->queue_lock = lock;
blk_queue_segment_boundary(q, 0xffffffff);
blk_queue_make_request(q, __make_request);
blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);
blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);
blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);
/*
* all done
*/
if (!elevator_init(q, NULL)) { //设置队列的IO调度算法
blk_queue_congestion_threshold(q);
return q;
}
blk_put_queue(q);
return NULL;
}
由上可见,当unplug一个块设备时,它将执行do_ide_request():
void do_ide_request(request_queue_t *q)
{
ide_drive_t *drive = q->queuedata;
ide_do_request(HWGROUP(drive), IDE_NO_IRQ);
}
static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
{
ide_drive_t *drive;
ide_hwif_t *hwif;
struct request *rq;
ide_startstop_t startstop;
int loops = 0;
/* for atari only: POSSIBLY BROKEN HERE(?) */
ide_get_lock(ide_intr, hwgroup);
/* caller must own ide_lock */
BUG_ON(!irqs_disabled());
while (!hwgroup->busy) {
hwgroup->busy = 1;
drive = choose_drive(hwgroup); //选择硬盘
……
again:
hwif = HWIF(drive);
if (hwgroup->hwif->sharing_irq &&
hwif != hwgroup->hwif &&
hwif->io_ports[IDE_CONTROL_OFFSET]) {
/* set nIEN for previous hwif */
SELECT_INTERRUPT(drive);
}
hwgroup->hwif = hwif;
hwgroup->drive = drive;
drive->sleeping = 0;
/*
* we know that the queue isn't empty, but this can happen
* if the q->prep_rq_fn() decides to kill a request
*/
rq = elv_next_request(drive->queue); //取下一个请求
if (!rq) {
hwgroup->busy = 0;
break;
}
hwgroup->rq = rq;
local_irq_enable_in_hardirq();
/* allow other IRQs while we start this request */
startstop = start_request(drive, rq); //开始向磁盘写入该请求
spin_lock_irq(&ide_lock);
if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq)
enable_irq(hwif->irq);
if (startstop == ide_stopped)
hwgroup->busy = 0;
}
}
static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
{
ide_startstop_t startstop;
sector_t block;
block = rq->sector;
if (blk_fs_request(rq) &&
(drive->media == ide_disk || drive->media == ide_floppy)) {
block += drive->sect0;
}
SELECT_DRIVE(drive);
if (!drive->special.all) {
ide_driver_t *drv;
……
if (rq->cmd_type == REQ_TYPE_ATA_CMD ||
rq->cmd_type == REQ_TYPE_ATA_TASK ||
rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
return execute_drive_cmd(drive, rq);
else if (blk_pm_request(rq)) {
……
return startstop;
}
drv = *(ide_driver_t **)rq->rq_disk->private_data;
return drv->do_request(drive, rq, block);
}
}