[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v3 08/15] hw/nvme: Implement the Function Level Reset
From: |
Lukasz Maniak |
Subject: |
[PATCH v3 08/15] hw/nvme: Implement the Function Level Reset |
Date: |
Tue, 21 Dec 2021 15:32:39 +0100 |
From: Łukasz Gieryk <lukasz.gieryk@linux.intel.com>
This patch implements the Function Level Reset, a feature currently not
implemented for the Nvme device, while listed as a mandatory ("shall")
in the 1.4 spec.
The implementation reuses FLR-related building blocks defined for the
pci-bridge module, and follows the same logic:
- FLR capability is advertised in the PCIE config,
- custom pci_write_config callback detects a write to the trigger
register and performs the PCI reset,
- which, eventually, calls the custom dc->reset handler.
Depending on reset type, parts of the state should (or should not) be
cleared. To distinguish the type of reset, an additional parameter is
passed to the reset function.
This patch also enables advertisement of the Power Management PCI
capability. The main reason behind it is to announce the no_soft_reset=1
bit, to signal SR-IOV support where each VF can be reset individually.
The implementation purposedly ignores writes to the PMCS.PS register,
as even such naïve behavior is enough to correctly handle the D3->D0
transition.
It’s worth to note, that the power state transition back to to D3, with
all the corresponding side effects, wasn't and stil isn't handled
properly.
Signed-off-by: Łukasz Gieryk <lukasz.gieryk@linux.intel.com>
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
---
hw/nvme/ctrl.c | 52 ++++++++++++++++++++++++++++++++++++++++----
hw/nvme/nvme.h | 5 +++++
hw/nvme/trace-events | 1 +
3 files changed, 54 insertions(+), 4 deletions(-)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index eaca12df57..9e83b4dd76 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -5602,7 +5602,7 @@ static void nvme_process_sq(void *opaque)
}
}
-static void nvme_ctrl_reset(NvmeCtrl *n)
+static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst)
{
NvmeNamespace *ns;
int i;
@@ -5634,7 +5634,9 @@ static void nvme_ctrl_reset(NvmeCtrl *n)
}
if (!pci_is_vf(&n->parent_obj) && n->params.sriov_max_vfs) {
- pcie_sriov_pf_disable_vfs(&n->parent_obj);
+ if (rst != NVME_RESET_CONTROLLER) {
+ pcie_sriov_pf_disable_vfs(&n->parent_obj);
+ }
}
n->aer_queued = 0;
@@ -5868,7 +5870,7 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset,
uint64_t data,
}
} else if (!NVME_CC_EN(data) && NVME_CC_EN(cc)) {
trace_pci_nvme_mmio_stopped();
- nvme_ctrl_reset(n);
+ nvme_ctrl_reset(n, NVME_RESET_CONTROLLER);
cc = 0;
csts &= ~NVME_CSTS_READY;
}
@@ -6426,6 +6428,28 @@ static void nvme_init_sriov(NvmeCtrl *n, PCIDevice
*pci_dev, uint16_t offset,
PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size);
}
+static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset)
+{
+ Error *err = NULL;
+ int ret;
+
+ ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, offset,
+ PCI_PM_SIZEOF, &err);
+ if (err) {
+ error_report_err(err);
+ return ret;
+ }
+
+ pci_set_word(pci_dev->config + offset + PCI_PM_PMC,
+ PCI_PM_CAP_VER_1_2);
+ pci_set_word(pci_dev->config + offset + PCI_PM_CTRL,
+ PCI_PM_CTRL_NO_SOFT_RESET);
+ pci_set_word(pci_dev->wmask + offset + PCI_PM_CTRL,
+ PCI_PM_CTRL_STATE_MASK);
+
+ return 0;
+}
+
static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
{
uint8_t *pci_conf = pci_dev->config;
@@ -6447,7 +6471,9 @@ static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev,
Error **errp)
}
pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_EXPRESS);
+ nvme_add_pm_capability(pci_dev, 0x60);
pcie_endpoint_cap_init(pci_dev, 0x80);
+ pcie_cap_flr_init(pci_dev);
if (n->params.sriov_max_vfs) {
pcie_ari_init(pci_dev, 0x100, 1);
}
@@ -6696,7 +6722,7 @@ static void nvme_exit(PCIDevice *pci_dev)
NvmeNamespace *ns;
int i;
- nvme_ctrl_reset(n);
+ nvme_ctrl_reset(n, NVME_RESET_FUNCTION);
if (n->subsys) {
for (i = 1; i <= NVME_MAX_NAMESPACES; i++) {
@@ -6795,6 +6821,22 @@ static void nvme_set_smart_warning(Object *obj, Visitor
*v, const char *name,
}
}
+static void nvme_pci_reset(DeviceState *qdev)
+{
+ PCIDevice *pci_dev = PCI_DEVICE(qdev);
+ NvmeCtrl *n = NVME(pci_dev);
+
+ trace_pci_nvme_pci_reset();
+ nvme_ctrl_reset(n, NVME_RESET_FUNCTION);
+}
+
+static void nvme_pci_write_config(PCIDevice *dev, uint32_t address,
+ uint32_t val, int len)
+{
+ pci_default_write_config(dev, address, val, len);
+ pcie_cap_flr_write_config(dev, address, val, len);
+}
+
static const VMStateDescription nvme_vmstate = {
.name = "nvme",
.unmigratable = 1,
@@ -6806,6 +6848,7 @@ static void nvme_class_init(ObjectClass *oc, void *data)
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
pc->realize = nvme_realize;
+ pc->config_write = nvme_pci_write_config;
pc->exit = nvme_exit;
pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
pc->revision = 2;
@@ -6814,6 +6857,7 @@ static void nvme_class_init(ObjectClass *oc, void *data)
dc->desc = "Non-Volatile Memory Express";
device_class_set_props(dc, nvme_props);
dc->vmsd = &nvme_vmstate;
+ dc->reset = nvme_pci_reset;
}
static void nvme_instance_init(Object *obj)
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 2157a7b95f..6713493380 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -471,6 +471,11 @@ typedef struct NvmeCtrl {
NvmeSecCtrlList sec_ctrl_list;
} NvmeCtrl;
+typedef enum NvmeResetType {
+ NVME_RESET_FUNCTION = 0,
+ NVME_RESET_CONTROLLER = 1,
+} NvmeResetType;
+
static inline NvmeNamespace *nvme_ns(NvmeCtrl *n, uint32_t nsid)
{
if (!nsid || nsid > NVME_MAX_NAMESPACES) {
diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events
index dd2aac3418..88678fc21e 100644
--- a/hw/nvme/trace-events
+++ b/hw/nvme/trace-events
@@ -105,6 +105,7 @@ pci_nvme_set_descriptor_extension(uint64_t slba, uint32_t
zone_idx) "set zone de
pci_nvme_zd_extension_set(uint32_t zone_idx) "set descriptor extension for
zone_idx=%"PRIu32""
pci_nvme_clear_ns_close(uint32_t state, uint64_t slba) "zone state=%"PRIu32",
slba=%"PRIu64" transitioned to Closed state"
pci_nvme_clear_ns_reset(uint32_t state, uint64_t slba) "zone state=%"PRIu32",
slba=%"PRIu64" transitioned to Empty state"
+pci_nvme_pci_reset(void) "PCI Function Level Reset"
# error conditions
pci_nvme_err_mdts(size_t len) "len %zu"
--
2.25.1
- [PATCH v3 00/15] hw/nvme: SR-IOV with Virtualization Enhancements, Lukasz Maniak, 2021/12/21
- [PATCH v3 01/15] pcie: Add support for Single Root I/O Virtualization (SR/IOV), Lukasz Maniak, 2021/12/21
- [PATCH v3 02/15] pcie: Add some SR/IOV API documentation in docs/pcie_sriov.txt, Lukasz Maniak, 2021/12/21
- [PATCH v3 03/15] pcie: Add a helper to the SR/IOV API, Lukasz Maniak, 2021/12/21
- [PATCH v3 04/15] pcie: Add 1.2 version token for the Power Management Capability, Lukasz Maniak, 2021/12/21
- [PATCH v3 05/15] hw/nvme: Add support for SR-IOV, Lukasz Maniak, 2021/12/21
- [PATCH v3 06/15] hw/nvme: Add support for Primary Controller Capabilities, Lukasz Maniak, 2021/12/21
- [PATCH v3 07/15] hw/nvme: Add support for Secondary Controller List, Lukasz Maniak, 2021/12/21
- [PATCH v3 08/15] hw/nvme: Implement the Function Level Reset,
Lukasz Maniak <=
- [PATCH v3 09/15] hw/nvme: Make max_ioqpairs and msix_qsize configurable in runtime, Lukasz Maniak, 2021/12/21
- [PATCH v3 11/15] hw/nvme: Calculate BAR attributes in a function, Lukasz Maniak, 2021/12/21
- [PATCH v3 12/15] hw/nvme: Initialize capability structures for primary/secondary controllers, Lukasz Maniak, 2021/12/21
- [PATCH v3 13/15] hw/nvme: Add support for the Virtualization Management command, Lukasz Maniak, 2021/12/21
- [PATCH v3 10/15] hw/nvme: Remove reg_size variable and update BAR0 size calculation, Lukasz Maniak, 2021/12/21
- [PATCH v3 14/15] docs: Add documentation for SR-IOV and Virtualization Enhancements, Lukasz Maniak, 2021/12/21
- [PATCH v3 15/15] hw/nvme: Update the initalization place for the AER queue, Lukasz Maniak, 2021/12/21