@@ -36,6 +36,8 @@ typedef struct NvmeNamespaceParams {
uint32_t max_active_zones;
uint32_t max_open_zones;
uint32_t zd_extension_size;
+ uint32_t nr_offline_zones;
+ uint32_t nr_rdonly_zones;
} NvmeNamespaceParams;
typedef struct NvmeNamespace {
@@ -21,6 +21,7 @@
#include "sysemu/sysemu.h"
#include "sysemu/block-backend.h"
#include "qapi/error.h"
+#include "crypto/random.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-core.h"
@@ -145,6 +146,20 @@ static int nvme_calc_zone_geometry(NvmeNamespace *ns, Error **errp)
}
}
+ if (ns->params.max_open_zones < nz) {
+ if (ns->params.nr_offline_zones > nz - ns->params.max_open_zones) {
+ error_setg(errp, "offline_zones value %u is too large",
+ ns->params.nr_offline_zones);
+ return -1;
+ }
+ if (ns->params.nr_rdonly_zones >
+ nz - ns->params.max_open_zones - ns->params.nr_offline_zones) {
+ error_setg(errp, "rdonly_zones value %u is too large",
+ ns->params.nr_rdonly_zones);
+ return -1;
+ }
+ }
+
return 0;
}
@@ -153,7 +168,9 @@ static void nvme_init_zone_state(NvmeNamespace *ns)
uint64_t start = 0, zone_size = ns->zone_size;
uint64_t capacity = ns->num_zones * zone_size;
NvmeZone *zone;
+ uint32_t rnd;
int i;
+ uint16_t zs;
ns->zone_array = g_malloc0(ns->zone_array_size);
if (ns->params.zd_extension_size) {
@@ -180,6 +197,37 @@ static void nvme_init_zone_state(NvmeNamespace *ns)
zone->w_ptr = start;
start += zone_size;
}
+
+ /* If required, make some zones Offline or Read Only */
+
+ for (i = 0; i < ns->params.nr_offline_zones; i++) {
+ do {
+ qcrypto_random_bytes(&rnd, sizeof(rnd), NULL);
+ rnd %= ns->num_zones;
+ } while (rnd < ns->params.max_open_zones);
+ zone = &ns->zone_array[rnd];
+ zs = nvme_get_zone_state(zone);
+ if (zs != NVME_ZONE_STATE_OFFLINE) {
+ nvme_set_zone_state(zone, NVME_ZONE_STATE_OFFLINE);
+ } else {
+ i--;
+ }
+ }
+
+ for (i = 0; i < ns->params.nr_rdonly_zones; i++) {
+ do {
+ qcrypto_random_bytes(&rnd, sizeof(rnd), NULL);
+ rnd %= ns->num_zones;
+ } while (rnd < ns->params.max_open_zones);
+ zone = &ns->zone_array[rnd];
+ zs = nvme_get_zone_state(zone);
+ if (zs != NVME_ZONE_STATE_OFFLINE &&
+ zs != NVME_ZONE_STATE_READ_ONLY) {
+ nvme_set_zone_state(zone, NVME_ZONE_STATE_READ_ONLY);
+ } else {
+ i--;
+ }
+ }
}
static int nvme_zoned_init_ns(NvmeCtrl *n, NvmeNamespace *ns, int lba_index,
@@ -353,6 +401,10 @@ static Property nvme_ns_props[] = {
params.max_open_zones, 0),
DEFINE_PROP_UINT32("zoned.descr_ext_size", NvmeNamespace,
params.zd_extension_size, 0),
+ DEFINE_PROP_UINT32("zoned.offline_zones", NvmeNamespace,
+ params.nr_offline_zones, 0),
+ DEFINE_PROP_UINT32("zoned.rdonly_zones", NvmeNamespace,
+ params.nr_rdonly_zones, 0),
DEFINE_PROP_END_OF_LIST(),
};