Linux-Kernel-Notes

Thinking in linux kernel

View on GitHub

nvme_reset_work()

static void nvme_reset_work(struct work_struct *work)
{
    struct nvme_dev *dev =
        container_of(work, struct nvme_dev, ctrl.reset_work);
    bool was_suspend = !!(dev->ctrl.ctrl_config & NVME_CC_SHN_NORMAL);
    int result;

    if (dev->ctrl.state != NVME_CTRL_RESETTING) {
        dev_warn(dev->ctrl.device, "ctrl state %d is not RESETTING\n",
            dev->ctrl.state);
        result = -ENODEV;
        goto out;
    }

    /*
    * If we're called to reset a live controller first shut it down before
    * moving on.
    */
    if (dev->ctrl.ctrl_config & NVME_CC_ENABLE)
        nvme_dev_disable(dev, false);
    nvme_sync_queues(&dev->ctrl);

    mutex_lock(&dev->shutdown_lock);
    result = nvme_pci_enable(dev);
    if (result)
        goto out_unlock;

    result = nvme_pci_configure_admin_queue(dev);
    if (result)
        goto out_unlock;

    result = nvme_alloc_admin_tags(dev);
    if (result)
        goto out_unlock;

    /*
    * Limit the max command size to prevent iod->sg allocations going
    * over a single page.
    */
    dev->ctrl.max_hw_sectors = min_t(u32,
        NVME_MAX_KB_SZ << 1, dma_max_mapping_size(dev->dev) >> 9);
    dev->ctrl.max_segments = NVME_MAX_SEGS;

    /*
    * Don't limit the IOMMU merged segment size.
    */
    dma_set_max_seg_size(dev->dev, 0xffffffff);
    dma_set_min_align_mask(dev->dev, NVME_CTRL_PAGE_SIZE - 1);

    mutex_unlock(&dev->shutdown_lock);

    /*
    * Introduce CONNECTING state from nvme-fc/rdma transports to mark the
    * initializing procedure here.
    */
    if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_CONNECTING)) {
        dev_warn(dev->ctrl.device,
            "failed to mark controller CONNECTING\n");
        result = -EBUSY;
        goto out;
    }

    /*
    * We do not support an SGL for metadata (yet), so we are limited to a
    * single integrity segment for the separate metadata pointer.
    */
    dev->ctrl.max_integrity_segments = 1;

    result = nvme_init_ctrl_finish(&dev->ctrl);
    if (result)
        goto out;

    if (dev->ctrl.oacs & NVME_CTRL_OACS_SEC_SUPP) {
        if (!dev->ctrl.opal_dev)
            dev->ctrl.opal_dev =
                init_opal_dev(&dev->ctrl, &nvme_sec_submit);
        else if (was_suspend)
            opal_unlock_from_suspend(dev->ctrl.opal_dev);
    } else {
        free_opal_dev(dev->ctrl.opal_dev);
        dev->ctrl.opal_dev = NULL;
    }

    if (dev->ctrl.oacs & NVME_CTRL_OACS_DBBUF_SUPP) {
        result = nvme_dbbuf_dma_alloc(dev);
        if (result)
            dev_warn(dev->dev,
                "unable to allocate dma for dbbuf\n");
    }

    if (dev->ctrl.hmpre) {
        result = nvme_setup_host_mem(dev);
        if (result < 0)
            goto out;
    }

    result = nvme_setup_io_queues(dev);
    if (result)
        goto out;

    /*
    * Keep the controller around but remove all namespaces if we don't have
    * any working I/O queue.
    */
    if (dev->online_queues < 2) {
        dev_warn(dev->ctrl.device, "IO queues not created\n");
        nvme_kill_queues(&dev->ctrl);
        nvme_remove_namespaces(&dev->ctrl);
        nvme_free_tagset(dev);
    } else {
        nvme_start_queues(&dev->ctrl);
        nvme_wait_freeze(&dev->ctrl);
        nvme_dev_add(dev);
        nvme_unfreeze(&dev->ctrl);
    }

    /*
    * If only admin queue live, keep it to do further investigation or
    * recovery.
    */
    if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_LIVE)) {
        dev_warn(dev->ctrl.device,
            "failed to mark controller live state\n");
        result = -ENODEV;
        goto out;
    }

    if (!dev->attrs_added && !sysfs_create_group(&dev->ctrl.device->kobj,
            &nvme_pci_attr_group))
        dev->attrs_added = true;

    nvme_start_ctrl(&dev->ctrl);
    return;

out_unlock:
    mutex_unlock(&dev->shutdown_lock);
out:
    if (result)
        dev_warn(dev->ctrl.device,
            "Removing after probe failure status: %d\n", result);
    nvme_remove_dead_ctrl(dev);
}