@@ -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"
@@ -163,6 +164,21 @@ static int nvme_ns_zoned_check_calc_geometry(NvmeNamespace *ns, Error **errp)
}
}
+ if (ns->params.max_open_zones < ns->num_zones) {
+ if (ns->params.nr_offline_zones >
+ ns->num_zones - 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 + ns->params.nr_offline_zones >
+ ns->num_zones - ns->params.max_open_zones) {
+ error_setg(errp, "rdonly_zones value %u is too large",
+ ns->params.nr_rdonly_zones);
+ return -1;
+ }
+ }
+
return 0;
}
@@ -171,7 +187,9 @@ static void nvme_ns_zoned_init_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_new0(NvmeZone, ns->num_zones);
if (ns->params.zd_extension_size) {
@@ -203,6 +221,37 @@ static void nvme_ns_zoned_init_state(NvmeNamespace *ns)
if (is_power_of_2(ns->zone_size)) {
ns->zone_size_log2 = 63 - clz64(ns->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 void nvme_ns_init_zoned(NvmeCtrl *n, NvmeNamespace *ns, int lba_index)
@@ -368,6 +417,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(),
};