nvme: improve namespace allocation

Instead of allocating a big array upfront go probe the namespaces and
only allocate an nvme_namespace struct for those namespaces which are
actually active.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
diff --git a/src/hw/nvme-int.h b/src/hw/nvme-int.h
index 7947b29..a4c1555 100644
--- a/src/hw/nvme-int.h
+++ b/src/hw/nvme-int.h
@@ -103,7 +103,6 @@
     struct nvme_cq admin_cq;
 
     u32 ns_count;
-    struct nvme_namespace *ns;
 
     struct nvme_sq io_sq;
     struct nvme_cq io_cq;
diff --git a/src/hw/nvme.c b/src/hw/nvme.c
index f26b811..f64eba8 100644
--- a/src/hw/nvme.c
+++ b/src/hw/nvme.c
@@ -234,11 +234,9 @@
 }
 
 static void
-nvme_probe_ns(struct nvme_ctrl *ctrl, struct nvme_namespace *ns, u32 ns_id,
-              u8 mdts)
+nvme_probe_ns(struct nvme_ctrl *ctrl, u32 ns_idx, u8 mdts)
 {
-    ns->ctrl  = ctrl;
-    ns->ns_id = ns_id;
+    u32 ns_id = ns_idx + 1;
 
     struct nvme_identify_ns *id = nvme_admin_identify_ns(ctrl, ns_id);
     if (!id) {
@@ -254,12 +252,21 @@
         goto free_buffer;
     }
 
-    ns->lba_count = id->nsze;
-    if (!ns->lba_count) {
+    if (!id->nsze) {
         dprintf(2, "NVMe NS %u is inactive.\n", ns_id);
         goto free_buffer;
     }
 
+    struct nvme_namespace *ns = malloc_fseg(sizeof(*ns));
+    if (!ns) {
+        warn_noalloc();
+        goto free_buffer;
+    }
+    memset(ns, 0, sizeof(*ns));
+    ns->ctrl  = ctrl;
+    ns->ns_id = ns_id;
+    ns->lba_count = id->nsze;
+
     struct nvme_lba_format *fmt = &id->lbaf[current_lba_format];
 
     ns->block_size    = 1U << fmt->lbads;
@@ -269,10 +276,11 @@
         /* If we see devices that trigger this path, we need to increase our
            buffer size. */
         warn_internalerror();
+        free(ns);
         goto free_buffer;
     }
 
-    ns->drive.cntl_id   = ns - ctrl->ns;
+    ns->drive.cntl_id   = ns_idx;
     ns->drive.removable = 0;
     ns->drive.type      = DTYPE_NVME;
     ns->drive.blksize   = ns->block_size;
@@ -527,13 +535,6 @@
     return -1;
 }
 
-static void
-nvme_destroy_io_queues(struct nvme_ctrl *ctrl)
-{
-    nvme_destroy_sq(&ctrl->io_sq);
-    nvme_destroy_cq(&ctrl->io_cq);
-}
-
 /* Waits for CSTS.RDY to match rdy. Returns 0 on success. */
 static int
 nvme_wait_csts_rdy(struct nvme_ctrl *ctrl, unsigned rdy)
@@ -627,24 +628,15 @@
         goto err_destroy_admin_sq;
     }
 
-    ctrl->ns = malloc_fseg(sizeof(*ctrl->ns) * ctrl->ns_count);
-    if (!ctrl->ns) {
-        warn_noalloc();
-        goto err_destroy_ioq;
-    }
-    memset(ctrl->ns, 0, sizeof(*ctrl->ns) * ctrl->ns_count);
-
     /* Populate namespace IDs */
     int ns_idx;
     for (ns_idx = 0; ns_idx < ctrl->ns_count; ns_idx++) {
-        nvme_probe_ns(ctrl, &ctrl->ns[ns_idx], ns_idx + 1, identify->mdts);
+        nvme_probe_ns(ctrl, ns_idx, identify->mdts);
     }
 
     dprintf(3, "NVMe initialization complete!\n");
     return 0;
 
- err_destroy_ioq:
-    nvme_destroy_io_queues(ctrl);
  err_destroy_admin_sq:
     nvme_destroy_sq(&ctrl->admin_sq);
  err_destroy_admin_cq: