好啦,磁盘和分区建立好了,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_device,scsi_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调度算法。下面我们就一同进入。