3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Linux NVMe ドライバの初期化処理

Posted at

概要

NVMeドライバの振る舞いを理解するためにLinuxのNVMeドライバのソースコードを読んでいきます。対象はlinux-4.19.4 kernelに含まれるNVMeドライバとなります。本記事では初期化処理を扱います。

初期化処理

対象ソースコードはlinux-4.19.4/drivers/nvme/host以下に存在します。今回はPCIeのNVMeデバイスを対象とします。初期化処理に主に関わるソースはcore.cpci.cとなります。core.cは共通処理をまとめたもの、pci.cはPCIeのNVMeデバイス専用の処理をまとめたソースとなります。モジュールのエントリポイントはmodule_initで指定されcore.c, pci.cそれぞれで初期化処理を行っています。core.cではnvme_init_core関数内でalloc_workqueueを用いてnvme_wq, nvme_reset_wq, nvme_delete_wqの3つのワークキューを作成し、スキャン処理などの指示が来るのを待ち受けます。pci.cではpci_register_driverを用いてPCIデバイス用のドライバとして登録をしています。

pci.cの初期化処理

pci.cではnvme_initが初期化処理になります。pci_register_driverでpciドライバを登録します。大事なのは.id_table.probeエントリです。カーネルはPCIデバイスがロードされたときに.id_tableで指定されたテーブルを探索します。追加されたPCIデバイスがマッチしたら.probeで指定した関数を実行します。NVMeデバイスが追加されたときにnvme_id_tableにマッチしたらnvme_probeを実行します。

pci.c
static struct pci_driver nvme_driver = {
    .name       = "nvme",
    .id_table   = nvme_id_table,
    .probe      = nvme_probe,
    .remove     = nvme_remove,
    .shutdown   = nvme_shutdown,
    .driver     = {  
        .pm = &nvme_dev_pm_ops,
    },   
    .sriov_configure = pci_sriov_configure_simple,
    .err_handler    = &nvme_err_handler,
};

static int __init nvme_init(void)
{
    return pci_register_driver(&nvme_driver);
}

nvme_probeではメモリの割り当てやmutexの初期化などを行います。
初期化処理のメインはnvme_init_ctrlとなります。この関数はcore.c側に定義されています。

pci.c
    result = nvme_init_ctrl(&dev->ctrl, &pdev->dev, &nvme_pci_ctrl_ops,
            quirks);

nvme_init_ctrlではワークキューへのハンドラー関数の登録やデバイスの登録などを行っています。

core.c
    INIT_WORK(&ctrl->scan_work, nvme_scan_work);
    INIT_WORK(&ctrl->async_event_work, nvme_async_event_work);
    INIT_WORK(&ctrl->fw_act_work, nvme_fw_act_work);
    INIT_WORK(&ctrl->delete_work, nvme_delete_ctrl_work);

    INIT_DELAYED_WORK(&ctrl->ka_work, nvme_keep_alive_work);

ida_simple_getでnvme0やnvme1の通し番号にあたる数字を取得し、cdev_device_add/dev/nvme0といったキャラクタデバイスを登録します。

    ret = ida_simple_get(&nvme_instance_ida, 0, 0, GFP_KERNEL);
    if (ret < 0) 
        goto out; 
    ctrl->instance = ret; 
    ret = dev_set_name(ctrl->device, "nvme%d", ctrl->instance);
    ...
    cdev_init(&ctrl->cdev, &nvme_dev_fops);
    ctrl->cdev.owner = ops->module;
    ret = cdev_device_add(&ctrl->cdev, ctrl->device);

今度はpci.c側に戻りnvme_async_probeを実行します。nvme_async_probeからreset_ctrl_sync経由でnvme_reset_ctrlを呼ぶことでnvme controllerの初期化を行います。その後flush_work(&dev->ctrl.scan_work)でscan_workの処理を実行させることで接続されているデバイスのスキャンを実施し、namespaceの設定などを行います。

参考

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?