为设备建立请求队列

    技术2024-07-24  68

    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;

     

    sdkpstruct scsi_disk结构体指针,其device成员是struct scsi_device指针,那么这个request_queue则是struct request_queue结构体指针,表示的是一个请求队列。在scsiprobe函数,也就是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_nodeelevator_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行让qkobj.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)