puppy居
puppy居士
posts - 41,comments - 27,trackbacks - 0

 

Linux块设备层分析(2)

R.wen

三、块设备驱动层

块设备的关系图如图2,一个分区或一个硬盘都可能是block_device,它一个硬盘只有一个gendisk结构,且有可能有多个分区hd_struct


 

 

图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);

       }

}

posted on 2008-08-22 15:19 puppy 阅读(439) 评论(0)  编辑 收藏 引用
只有注册用户登录后才能发表评论。