@@ -2980,8 +2980,11 @@ static int nvme_init_zone_meta(NvmeCtrl *n, NvmeNamespace *ns,
uint64_t capacity)
{
NvmeZone *zone;
+ Error *err;
uint64_t start = 0, zone_size = n->params.zone_size;
+ uint32_t rnd;
int i;
+ uint16_t zs;
ns->zone_array = g_malloc0(n->zone_array_size);
ns->exp_open_zones = g_malloc0(sizeof(NvmeZoneList));
@@ -3011,6 +3014,37 @@ static int nvme_init_zone_meta(NvmeCtrl *n, NvmeNamespace *ns,
start += zone_size;
}
+ /* If required, make some zones Offline or Read Only */
+
+ for (i = 0; i < n->params.nr_offline_zones; i++) {
+ do {
+ qcrypto_random_bytes(&rnd, sizeof(rnd), &err);
+ rnd %= n->num_zones;
+ } while (rnd < n->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 < n->params.nr_rdonly_zones; i++) {
+ do {
+ qcrypto_random_bytes(&rnd, sizeof(rnd), &err);
+ rnd %= n->num_zones;
+ } while (rnd < n->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--;
+ }
+ }
+
return 0;
}
@@ -3063,6 +3097,16 @@ static void nvme_zoned_init_ctrl(NvmeCtrl *n, Error **errp)
if (n->params.max_active_zones > nz) {
n->params.max_active_zones = nz;
}
+ if (n->params.max_open_zones < nz) {
+ if (n->params.nr_offline_zones > nz - n->params.max_open_zones) {
+ n->params.nr_offline_zones = nz - n->params.max_open_zones;
+ }
+ if (n->params.nr_rdonly_zones >
+ nz - n->params.max_open_zones - n->params.nr_offline_zones) {
+ n->params.nr_rdonly_zones =
+ nz - n->params.max_open_zones - n->params.nr_offline_zones;
+ }
+ }
if (n->params.zd_extension_size) {
if (n->params.zd_extension_size & 0x3f) {
error_setg(errp,
@@ -3471,6 +3515,8 @@ static Property nvme_props[] = {
DEFINE_PROP_UINT64("finish_rcmnd_delay", NvmeCtrl,
params.fzr_delay_usec, 0),
DEFINE_PROP_UINT64("finish_rcmnd_limit", NvmeCtrl, params.frl_usec, 0),
+ DEFINE_PROP_UINT32("offline_zones", NvmeCtrl, params.nr_offline_zones, 0),
+ DEFINE_PROP_UINT32("rdonly_zones", NvmeCtrl, params.nr_rdonly_zones, 0),
DEFINE_PROP_BOOL("zone_async_events", NvmeCtrl, params.zone_async_events,
true),
DEFINE_PROP_BOOL("cross_zone_read", NvmeCtrl, params.cross_zone_read, true),
@@ -24,6 +24,8 @@ typedef struct NvmeParams {
uint64_t zone_capacity;
int32_t max_active_zones;
int32_t max_open_zones;
+ uint32_t nr_offline_zones;
+ uint32_t nr_rdonly_zones;
uint32_t zd_extension_size;
uint64_t rzr_delay_usec;
uint64_t rrl_usec;
ZNS specification defines two zone conditions for the zones that no longer can function properly, possibly because of flash wear or other internal fault. It is useful to be able to "inject" a small number of such zones for testing purposes. This commit defines two optional driver properties, "offline_zones" and "rdonly_zones". User can assign non-zero values to these variables to specify the number of zones to be initialized as Offline or Read-Only. The actual number of injected zones may be smaller than the requested amount - Read-Only and Offline counts are expected to be much smaller than the total number of drive zones. Signed-off-by: Dmitry Fomichev <dmitry.fomichev@wdc.com> --- hw/block/nvme.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ hw/block/nvme.h | 2 ++ 2 files changed, 48 insertions(+)