From: Clay Mayers <clay.may...@kioxia.com>

If a namespace's param.zoned.finish_time is non-zero,
controllers register with the namespace to be notified
when entries are added to its zone-descriptor-changed
log page.  If the zone-descriptor-changed aen is enabled,
this will cause an AEN to be sent from that controller.

Signed-off-by: Clay Mayers <clay.may...@kioxia.com>
---
 hw/nvme/ctrl.c       | 62 +++++++++++++++++++++++++++++++++++++++++++-
 hw/nvme/ns.c         |  1 +
 hw/nvme/nvme.h       |  9 +++++++
 include/block/nvme.h |  2 ++
 4 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index c7ee54ef5e..f1cfa272b4 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -1519,6 +1519,52 @@ static void nvme_clear_events(NvmeCtrl *n, uint8_t 
event_type)
     }
 }
 
+static void nvme_zdc_watch(NvmeCtrl *n, NvmeNamespace *ns, NvmeNotifyFnc fnc)
+{
+    NvmeZdcNotify *watcher = g_malloc0(sizeof(*watcher));
+
+    watcher->n = n;
+    watcher->notify = fnc;
+    QTAILQ_INSERT_TAIL(&ns->zdc_watchers, watcher, entry);
+}
+
+static void nvme_zdc_unwatch(NvmeCtrl *n, NvmeNamespace *ns)
+{
+    NvmeZdcNotify *watcher;
+
+    QTAILQ_FOREACH(watcher, &ns->zdc_watchers, entry) {
+        if (watcher->n == n) {
+            QTAILQ_REMOVE(&ns->zdc_watchers, watcher, entry);
+            break;
+        }
+    }
+}
+
+static void nvme_zdc_notify(NvmeNamespace *ns)
+{
+    NvmeZdcNotify *watcher;
+
+    QTAILQ_FOREACH(watcher, &ns->zdc_watchers, entry) {
+        (*watcher->notify)(watcher->n, ns);
+    }
+}
+
+static void nvme_zdc_aen(NvmeCtrl *n, NvmeNamespace *ns)
+{
+    g_assert(n->id_ctrl.oaes & (1 << 27));
+
+    if (!NVME_AEC_ZONE_CHANGED(n->features.async_config)) {
+        return;
+    }
+
+    if (!n->zdc_event_queued) {
+        n->zdc_event_queued = true;
+        nvme_enqueue_event(n, NVME_AER_TYPE_NOTICE,
+                            NVME_AER_INFO_NOTICE_ZONE_DESC_CHANGED,
+                            NVME_LOG_CHANGED_ZONE, ns->params.nsid);
+    }
+}
+
 static void nvme_zdc_list(NvmeNamespace *ns, NvmeZoneIdList *zlist, bool reset)
 {
     NvmeZdc *zdc;
@@ -1554,6 +1600,7 @@ static void nvme_check_finish(NvmeNamespace *ns, 
NvmeZoneListHead *list)
                 zdc->zone = zone;
                 zone->zdc_entry = zdc;
                 QTAILQ_INSERT_TAIL(&ns->zdc_list, zdc, entry);
+                nvme_zdc_notify(ns);
             }
         } else if (zone->finish_ms != INT64_MAX) {
             timer_mod_anticipate(ns->active_timer, zone->finish_ms);
@@ -4722,6 +4769,14 @@ static uint16_t nvme_changed_zones(NvmeCtrl *n, uint8_t 
rae, uint32_t buf_len,
         return NVME_INVALID_NSID | NVME_DNR;
     }
     nvme_zdc_list(ns, &zlist, !rae);
+    if (!rae) {
+        n->zdc_event_queued = false;
+        nvme_clear_events(n, NVME_AER_TYPE_NOTICE);
+        /* send a new aen if there are still zdc entries */
+        if (!QTAILQ_EMPTY(&ns->zdc_list)) {
+            nvme_zdc_notify(ns);
+        }
+    }
 
     return nvme_c2h(n, ((uint8_t *)&zlist) + off, trans_len, req);
 }
@@ -5808,6 +5863,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, 
NvmeRequest *req)
                 return NVME_NS_NOT_ATTACHED | NVME_DNR;
             }
 
+            nvme_zdc_unwatch(n, ns);
             ctrl->namespaces[nsid] = NULL;
             ns->attached--;
 
@@ -7535,7 +7591,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice 
*pci_dev)
 
     id->cntlid = cpu_to_le16(n->cntlid);
 
-    id->oaes = cpu_to_le32(NVME_OAES_NS_ATTR);
+    id->oaes = cpu_to_le32(NVME_OAES_NS_ATTR | NVME_OAES_ZDC);
     id->ctratt |= cpu_to_le32(NVME_CTRATT_ELBAS);
 
     id->rab = 6;
@@ -7652,6 +7708,10 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns)
 
     n->dmrsl = MIN_NON_ZERO(n->dmrsl,
                             BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1));
+
+    if (ns->params.fto) {
+        nvme_zdc_watch(n, ns, nvme_zdc_aen);
+    }
 }
 
 static void nvme_realize(PCIDevice *pci_dev, Error **errp)
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 25cd490c99..5629b61302 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -241,6 +241,7 @@ static void nvme_ns_zoned_init_state(NvmeNamespace *ns)
     QTAILQ_INIT(&ns->closed_zones);
     QTAILQ_INIT(&ns->full_zones);
     QTAILQ_INIT(&ns->zdc_list);
+    QTAILQ_INIT(&ns->zdc_watchers);
 
     zone = ns->zone_array;
     for (i = 0; i < ns->num_zones; i++, zone++) {
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 2b7997e4a7..5499105e7b 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -91,6 +91,14 @@ static inline NvmeNamespace *nvme_subsys_ns(NvmeSubsystem 
*subsys,
 #define NVME_NS(obj) \
     OBJECT_CHECK(NvmeNamespace, (obj), TYPE_NVME_NS)
 
+typedef void (*NvmeNotifyFnc)(NvmeCtrl *n, NvmeNamespace *ns);
+
+typedef struct NvmeZdcNotify {
+    QTAILQ_ENTRY(NvmeZdcNotify) entry;
+    NvmeNotifyFnc notify;
+    NvmeCtrl *n;
+} NvmeZdcNotify;
+
 typedef struct NvmeZdc {
     QTAILQ_ENTRY(NvmeZdc) entry;
     NvmeZone *zone;
@@ -179,6 +187,7 @@ typedef struct NvmeNamespace {
 
     int64_t         fto_ms;
     QEMUTimer       *active_timer;
+    QTAILQ_HEAD(, NvmeZdcNotify) zdc_watchers;
     QTAILQ_HEAD(, NvmeZdc) zdc_list;
 
     NvmeNamespaceParams params;
diff --git a/include/block/nvme.h b/include/block/nvme.h
index 9467d4b939..1662046c0d 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -830,6 +830,7 @@ enum NvmeAsyncEventRequest {
     NVME_AER_INFO_SMART_TEMP_THRESH         = 1,
     NVME_AER_INFO_SMART_SPARE_THRESH        = 2,
     NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED    = 0,
+    NVME_AER_INFO_NOTICE_ZONE_DESC_CHANGED  = 0xef,
 };
 
 typedef struct QEMU_PACKED NvmeAerResult {
@@ -1133,6 +1134,7 @@ typedef struct NvmeIdCtrlNvm {
 
 enum NvmeIdCtrlOaes {
     NVME_OAES_NS_ATTR   = 1 << 8,
+    NVME_OAES_ZDC       = 1 << 27,
 };
 
 enum NvmeIdCtrlCtratt {
-- 
2.27.0


Reply via email to