日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux NVMe Driver学习笔记之6:Admin Queue与Blk-mq初始化

發(fā)布時(shí)間:2024/8/1 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux NVMe Driver学习笔记之6:Admin Queue与Blk-mq初始化 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這篇文章緊接上回分解,在nvme_probe函數(shù)的最后一步調(diào)用nvme_reset_work進(jìn)行reset操作,nvme_reset_work的主要工作可以概括如下幾個(gè)步驟:

  • 進(jìn)入nvme_reset_work函數(shù)后先檢查NVME_CTRL_RESETTING標(biāo)志,來(lái)確保nvme_reset_work不會(huì)被重復(fù)進(jìn)入。

  • 調(diào)用nvme_pci_enable

  • 調(diào)用nvme_configure_admin_queue

  • 調(diào)用nvme_init_queue

  • 調(diào)用nvme_alloc_admin_tags

  • 調(diào)用nvme_init_identify

  • 調(diào)用nvme_setup_io_queues

  • 調(diào)用nvme_start_queues/nvme_dev_add之后,接著調(diào)用nvme_queue_scan

  • 上篇文章中,我們解析了nvme_configure_admin_queue的內(nèi)容,本文我們接著介紹nvme_reset_work中的其他函數(shù)。

    1. 先來(lái)看看nvme_init_queue:

    static void?nvme_init_queue(struct nvme_queue *nvmeq, u16 qid)

    {

    struct nvme_dev *dev = nvmeq->dev;

    spin_lock_irq(&nvmeq->q_lock);

    nvmeq->sq_tail?= 0;

    nvmeq->cq_head?= 0;

    nvmeq->cq_phase?= 1;

    nvmeq->q_db?= &dev->dbs[qid * 2 * dev->db_stride];

    memset((void *)nvmeq->cqes, 0, CQ_SIZE(nvmeq->q_depth));

    dev->online_queues++;

    spin_unlock_irq(&nvmeq->q_lock);

    }

    nvme_init_queue做的事情比較簡(jiǎn)單,就是對(duì)之前nvme_configure_admin_queue函數(shù)中申請(qǐng)的queue進(jìn)行初始化操作。在這個(gè)過(guò)程中,對(duì)SQ Tail, CQ Head以及CQ phase變量進(jìn)行初始化賦值,然后通過(guò)q_db指向Doorbell寄存器。

    有關(guān)SQ、CQ、Phase、Doorbell的詳細(xì)解釋請(qǐng)參考:

    NVMe系列專題之二:隊(duì)列(Queue)管理

    2.?看完nvme_init_queue, 我們?cè)俳又虺?/strong>nvme_alloc_admin_tags:?

    static int?nvme_alloc_admin_tags(struct nvme_dev *dev)

    {

    if (!dev->ctrl.admin_q) {

    dev->admin_tagset.ops = &nvme_mq_admin_ops;

    dev->admin_tagset.nr_hw_queues = 1;

    dev->admin_tagset.queue_depth = NVME_AQ_BLKMQ_DEPTH - 1;

    dev->admin_tagset.timeout = ADMIN_TIMEOUT;

    dev->admin_tagset.numa_node = dev_to_node(dev->dev);

    dev->admin_tagset.cmd_size = nvme_cmd_size(dev);

    dev->admin_tagset.driver_data = dev;

    if (blk_mq_alloc_tag_set(&dev->admin_tagset))

    return -ENOMEM;

    dev->ctrl.admin_q =?blk_mq_init_queue(&dev->admin_tagset);

    if (IS_ERR(dev->ctrl.admin_q)) {

    blk_mq_free_tag_set(&dev->admin_tagset);

    return -ENOMEM;

    }

    if (!blk_get_queue(dev->ctrl.admin_q)) {

    nvme_dev_remove_admin(dev);

    dev->ctrl.admin_q = NULL;

    return -ENODEV;

    }

    } else

    blk_mq_start_stopped_hw_queues(dev->ctrl.admin_q, true);

    return 0;

    }

    這個(gè)函數(shù)是NVMe設(shè)備采用Multi-Queue(MQ)的核心函數(shù),所以在展開解析這個(gè)函數(shù)之前,我們先聊聊Linux Multi-Queue Block Layer.?

    如之前NVME文章(NVMe系列專題之二:隊(duì)列(Queue)管理)中介紹,多隊(duì)列、原生異步、無(wú)鎖是NVMe的最大特色,這些為高性能而生的設(shè)計(jì)迫使Linux Kernel在3.19拋棄了老的單隊(duì)列Block Layer而轉(zhuǎn)向Multi-Queue Block Layer. 這個(gè)Multi-Queue Block Layer的架構(gòu)直接對(duì)應(yīng)于NVMe的多隊(duì)列設(shè)計(jì),如下圖:

    所謂的Multi-Queue機(jī)制就是在多核CPU的情況下,將不同的block層提交隊(duì)列分配到不同的CPU核上,以更好的平衡IO的工作負(fù)載,大幅提高SSD等存儲(chǔ)設(shè)備的IO效率。Multi-Queue Block Layer長(zhǎng)啥樣子呢?畫了個(gè)圖,看一下:

    • Multi-Queue Block Layer分為兩層,Software Queues和Hardware Dispatch Queues.?

    • Softeware Queues是per core的,Queue的數(shù)目與協(xié)議有關(guān)系,比如NVMe協(xié)議,可以有最多64K對(duì) IO SQ/CQ。Software Queues層做的事情如上圖標(biāo)識(shí)部分。

    • Hardware Queues數(shù)目由底層設(shè)備驅(qū)動(dòng)決定,可以1個(gè)或者多個(gè)。最大支持?jǐn)?shù)目一般會(huì)與MSI-X中斷最大數(shù)目一樣,支持2K。設(shè)備驅(qū)動(dòng)通過(guò)map_queue維護(hù)Software Queues和Hardware Queues之間的對(duì)接關(guān)系。

    • 需要強(qiáng)調(diào)一點(diǎn),Hardware Queues與Software Queues的數(shù)目不一定相等,上圖1:1 Mapping的情況屬于最理想的情況。

    到這里,Multi-Queue Block Layer基本理論我們就算回顧完畢了,我回過(guò)頭來(lái)在看看nvme_alloc_admin_tags這個(gè)函數(shù)。

    從上面的代碼來(lái)看,主要分為三步:

    • 對(duì)admin_tagset結(jié)構(gòu)體初始化,在這個(gè)過(guò)程中特別提一下ops的賦值(后續(xù)會(huì)用到)。

      static struct blk_mq_ops?nvme_mq_admin_ops?= {

      .queue_rq = nvme_queue_rq,

      .complete = nvme_complete_rq,

      .init_hctx =?nvme_admin_init_hctx,

      .exit_hctx ? ? ?= nvme_admin_exit_hctx,

      .init_request = nvme_admin_init_request,

      .timeout = nvme_timeout,

      };

    • 接著調(diào)用blk_mq_alloc_tag_set分配tag set并與request queue關(guān)聯(lián),

    • 然后調(diào)用blk_mq_init_allocated_queue對(duì)hardware queue和software queues進(jìn)行初始化,并配置兩者之間的mapping關(guān)系,最后將返回值傳遞給dev->ctrl.admin_q。

    blk_mq_init_allocated_queue調(diào)用blk_mq_realloc_hw_ctxs,然調(diào)用blk_mq_init_hctx,最后調(diào)用set->ops->init_hctx,也就是nvme_admin_init_hctx。

    也就是說(shuō),blk_mq_init_allocated_queue初始化最終調(diào)用的是nvme_admin_init_hctx

    static int?nvme_admin_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,

    unsigned int hctx_idx)

    {

    struct nvme_dev *dev = data;

    struct nvme_queue *nvmeq = dev->queues[0];

    WARN_ON(hctx_idx != 0);

    WARN_ON(dev->admin_tagset.tags[0] != hctx->tags);

    WARN_ON(nvmeq->tags);

    hctx->driver_data =?nvmeq;

    nvmeq->tags = &dev->admin_tagset.tags[0];

    return 0;

    }

    從上面的code,可以發(fā)現(xiàn),Hardware Queue初始化時(shí),會(huì)將nvme_configure_admin_queue函數(shù)中申請(qǐng)的NVMe Queue(nvmeq)賦值給Hardware Queue的driver_data. 由此可知,NVMe Queue與Hardware Queue是一一對(duì)應(yīng)的關(guān)系,這也是NVMe與Linux Multi-Queue Block Layer默契配合的關(guān)鍵之處。

    總結(jié)

    以上是生活随笔為你收集整理的Linux NVMe Driver学习笔记之6:Admin Queue与Blk-mq初始化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。