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