为设备建立请求队列

1.5.4 为设备建立请求队列

好啦,磁盘和分区建立好了,block_device数据结构也关联起来了,回到add_disk中,我们需要调用第三个函数了,也就是blk_register_queue(disk),来建立请求队列与bio等数据结构了,让我们来仔细分析。

 

   4079 int blk_register_queue(struct gendisk *disk)

   4080 {

   4081         int ret;

   4082

   4083         request_queue_t *q = disk->queue;

   4084

   4085         if (!q || !q->request_fn)

   4086                 return -ENXIO;

   4087

   4088         q->kobj.parent = kobject_get(&disk->kobj);

   4089

   4090         ret = kobject_add(&q->kobj);

   4091         if (ret < 0)

   4092                 return ret;

   4093

   4094         kobject_uevent(&q->kobj, KOBJ_ADD);

   4095

   4096         ret = elv_register_queue(q);

   4097         if (ret) {

   4098                 kobject_uevent(&q->kobj, KOBJ_REMOVE);

   4099                 kobject_del(&q->kobj);

   4100                 return ret;

   4101         }

   4102

   4103         return 0;

   4104 }

 

首先,4090行这个kobject_add很好解释,在/sys/block/sda/目录下面又多一个子目录而已,但问题是,这个q究竟是什么?这里我们把disk->queue赋给了它,而disk->queue又是什么呢?回过头去看sd_probe(),当时我们有这么一句:

gd->queue = sdkp->device->request_queue;

 

而sdkp是struct scsi_disk结构体指针,其device成员是struct scsi_device指针,那么这个request_queue则是struct request_queue结构体指针,表示的是一个请求队列。在scsi的probe函数,也就是sd_probe的被调用之前,核心层实际上已经为它们做了许多工作了。这里涉及到scsi总线驱动的概念,我们将在“块设备驱动层的处理”一节中详细介绍这一过程,这里只提一下,这个过程中会调用scsi_probe_and_add_lun为对应scsi磁盘申请一个scsi_device结构体变量,为它的一些成员赋好了值,这其中就包括了这个请求队列。

 

准确地说,scsi总线扫描的时候,每当探测到一个设备,scsi_probe_and_add_lun函数会通过scsi_alloc_sdev函数申请一个scsi_devicescsi_alloc_sdev会调用scsi_alloc_queue初始化这个块设备的request_queue而这个函数涉及到很多block层提供的函数,所以我们不得不从这里开始看起,来自drivers/scsi/scsi_lib.c:

 

   1569 struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost,

   1570                                          request_fn_proc *request_fn)

   1571 {

   1572         struct request_queue *q;

   1573

   1574         q = blk_init_queue(request_fn, NULL);

   1575         if (!q)

   1576                 return NULL;

   1577

   1578         blk_queue_max_hw_segments(q, shost->sg_tablesize);

   1579         blk_queue_max_phys_segments(q, SCSI_MAX_PHYS_SEGMENTS);

   1580         blk_queue_max_sectors(q, shost->max_sectors);

   1581         blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost));

   1582         blk_queue_segment_boundary(q, shost->dma_boundary);

   1583

   1584         if (!shost->use_clustering)

   1585                 clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);

   1586         return q;

   1587 }

   1588 EXPORT_SYMBOL(__scsi_alloc_queue);

   1589

   1590 struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)

   1591 {

   1592         struct request_queue *q;

   1593

   1594         q = __scsi_alloc_queue(sdev->host, scsi_request_fn);

   1595         if (!q)

   1596                 return NULL;

   1597

   1598         blk_queue_prep_rq(q, scsi_prep_fn);

   1599         blk_queue_issue_flush_fn(q, scsi_issue_flush_fn);

   1600         blk_queue_softirq_done(q, scsi_softirq_done);

   1601         return q;

   1602 }

 

这两个函数因为调用关系所以一并贴了出来。我们首先要看的很自然就是blk_init_queue(),它来自block/ll_rw_blk.c:

 

   1893 request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)

   1894 {

   1895         return blk_init_queue_node(rfn, lock, -1);

   1896 }

   1897 EXPORT_SYMBOL(blk_init_queue);

   1898

   1899 request_queue_t *

   1900 blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)

   1901 {

   1902         request_queue_t *q = blk_alloc_queue_node(GFP_KERNEL, node_id);

   1903

   1904         if (!q)

   1905                 return NULL;

   1906

   1907         q->node = node_id;

   1908         if (blk_init_free_list(q)) {

   1909                 kmem_cache_free(requestq_cachep, q);

   1910                 return NULL;

   1911         }

   1912

   1913         /*

   1914          * if caller didn't supply a lock, they get per-queue locking with

   1915          * our embedded lock

   1916          */

   1917         if (!lock) {

   1918                 spin_lock_init(&q->__queue_lock);

   1919                 lock = &q->__queue_lock;

   1920         }

   1921

   1922         q->request_fn           = rfn;

   1923         q->prep_rq_fn           = NULL;

   1924         q->unplug_fn            = generic_unplug_device;

   1925         q->queue_flags          = (1 << QUEUE_FLAG_CLUSTER);

   1926         q->queue_lock           = lock;

   1927

   1928         blk_queue_segment_boundary(q, 0xffffffff);

   1929

   1930         blk_queue_make_request(q, __make_request);

   1931         blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);

   1932

   1933         blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);

   1934         blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);

   1935

   1936         q->sg_reserved_size = INT_MAX;

   1937

   1938         /*

   1939          * all done

   1940          */

   1941         if (!elevator_init(q, NULL)) {

   1942                 blk_queue_congestion_threshold(q);

   1943                 return q;

   1944         }

   1945

   1946         blk_put_queue(q);

   1947         return NULL;

   1948 }

 

别看这些函数都很可怕,正我们目前需要关注的其实只是其中的某几个而已。它们这个blk_alloc_queue_node和elevator_init()。前者来自block/ll_rw_blk.c,后者则来自block/elevator.c:

 

   1836 request_queue_t *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)

   1837 {

   1838         request_queue_t *q;

   1839

   1840         q = kmem_cache_alloc_node(requestq_cachep, gfp_mask, node_id);

   1841         if (!q)

   1842                 return NULL;

   1843

   1844         memset(q, 0, sizeof(*q));

   1845         init_timer(&q->unplug_timer);

   1846

   1847         snprintf(q->kobj.name, KOBJ_NAME_LEN, "%s", "queue");

   1848         q->kobj.ktype = &queue_ktype;

   1849         kobject_init(&q->kobj);

   1850

   1851         q->backing_dev_info.unplug_io_fn = blk_backing_dev_unplug;

   1852         q->backing_dev_info.unplug_io_data = q;

   1853

   1854         mutex_init(&q->sysfs_lock);

   1855

   1856         return q;

   1857 }

 

还记得块设备初始化的那个blk_dev_init吧,当时我们调用kmem_cache_create()申请了一个slab内存分配器request_cachep,现在就该用它了。从这个分配器里申请了一个struct request_queue_t结构体的空间,给了指针q,然后1844行初始化为0。而1847行让q的kobj.name等于“queue”,这就是为什么今后我们在/sys/block/sda/目录下面能看到一个叫做“queue”的目录。

 

而这个queue目录下面的内容是什么呢?

[root@localhost ~]# ls /sys/block/sda/queue/

iosched  max_hw_sectors_kb  max_sectors_kb  nr_requests  read_ahead_kb  scheduler

 

这几个文件从哪来的?注意1848行那个queue_ktype:

 

static struct kobj_type queue_ktype = {

         .sysfs_ops      = &queue_sysfs_ops,

         .default_attrs   = default_attrs,

         .release        = blk_release_queue,

};

 

这些就是定义了一些属性,kobject的属性。不过有一个东西例外,它就是iosched,这不是一个文件,这是一个目录:

 

[root@localhost ~]# ls /sys/block/sdf/queue/iosched/

back_seek_max     

fifo_expire_async 

quantum     

slice_async_rq 

slice_sync

back_seek_penalty 

fifo_expire_sync  

slice_async 

slice_idle

 

关于这个目录,我们需要分析blk_init_queue_node函数中的另一个函数,elevator_init()。要弄清这个函数,需要学习块设备I/O调度层的核心——I/O调度算法。下面我们就一同进入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值