Re: [PATCH v2 11/18] hw/block/nvme: Introduce max active and open zone limits
On Jun 18 06:34, Dmitry Fomichev wrote: > Added two module properties, "max_active" and "max_open" to control > the maximum number of zones that can be active or open. Once these > variables are set to non-default values, the driver checks these > limits during I/O and returns Too Many Active or Too Many Open > command status if they are exceeded. > > Signed-off-by: Hans Holmberg > Signed-off-by: Dmitry Fomichev > --- > hw/block/nvme.c | 183 +++- > hw/block/nvme.h | 4 ++ > 2 files changed, 185 insertions(+), 2 deletions(-) > > diff --git a/hw/block/nvme.c b/hw/block/nvme.c > index 2e03b0b6ed..05a7cbcfcc 100644 > --- a/hw/block/nvme.c > +++ b/hw/block/nvme.c > @@ -120,6 +120,87 @@ static void nvme_remove_zone(NvmeCtrl *n, NvmeNamespace > *ns, NvmeZoneList *zl, > zone->prev = zone->next = 0; > } > > +/* > + * Take the first zone out from a list, return NULL if the list is empty. > + */ > +static NvmeZone *nvme_remove_zone_head(NvmeCtrl *n, NvmeNamespace *ns, > +NvmeZoneList *zl) > +{ > +NvmeZone *zone = nvme_peek_zone_head(ns, zl); > + > +if (zone) { > +--zl->size; > +if (zl->size == 0) { > +zl->head = NVME_ZONE_LIST_NIL; > +zl->tail = NVME_ZONE_LIST_NIL; > +} else { > +zl->head = zone->next; > +ns->zone_array[zl->head].prev = NVME_ZONE_LIST_NIL; > +} > +zone->prev = zone->next = 0; > +} > + > +return zone; > +} > + > +/* > + * Check if we can open a zone without exceeding open/active limits. > + * AOR stands for "Active and Open Resources" (see TP 4053 section 2.5). > + */ > +static int nvme_aor_check(NvmeCtrl *n, NvmeNamespace *ns, > + uint32_t act, uint32_t opn) > +{ > +if (n->params.max_active_zones != 0 && > +ns->nr_active_zones + act > n->params.max_active_zones) { > +trace_pci_nvme_err_insuff_active_res(n->params.max_active_zones); > +return NVME_ZONE_TOO_MANY_ACTIVE | NVME_DNR; > +} > +if (n->params.max_open_zones != 0 && > +ns->nr_open_zones + opn > n->params.max_open_zones) { > +trace_pci_nvme_err_insuff_open_res(n->params.max_open_zones); > +return NVME_ZONE_TOO_MANY_OPEN | NVME_DNR; > +} > + > +return NVME_SUCCESS; > +} > + > +static inline void nvme_aor_inc_open(NvmeCtrl *n, NvmeNamespace *ns) > +{ > +assert(ns->nr_open_zones >= 0); > +if (n->params.max_open_zones) { > +ns->nr_open_zones++; > +assert(ns->nr_open_zones <= n->params.max_open_zones); > +} > +} > + > +static inline void nvme_aor_dec_open(NvmeCtrl *n, NvmeNamespace *ns) > +{ > +if (n->params.max_open_zones) { > +assert(ns->nr_open_zones > 0); > +ns->nr_open_zones--; > +} > +assert(ns->nr_open_zones >= 0); > +} > + > +static inline void nvme_aor_inc_active(NvmeCtrl *n, NvmeNamespace *ns) > +{ > +assert(ns->nr_active_zones >= 0); > +if (n->params.max_active_zones) { > +ns->nr_active_zones++; > +assert(ns->nr_active_zones <= n->params.max_active_zones); > +} > +} > + > +static inline void nvme_aor_dec_active(NvmeCtrl *n, NvmeNamespace *ns) > +{ > +if (n->params.max_active_zones) { > +assert(ns->nr_active_zones > 0); > +ns->nr_active_zones--; > +assert(ns->nr_active_zones >= ns->nr_open_zones); > +} > +assert(ns->nr_active_zones >= 0); > +} > + > static void nvme_assign_zone_state(NvmeCtrl *n, NvmeNamespace *ns, > NvmeZone *zone, uint8_t state) > { > @@ -454,6 +535,24 @@ static void nvme_enqueue_req_completion(NvmeCQueue *cq, > NvmeRequest *req) > timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); > } > > +static void nvme_auto_transition_zone(NvmeCtrl *n, NvmeNamespace *ns, > +bool implicit, bool adding_active) > +{ > +NvmeZone *zone; > + > +if (implicit && n->params.max_open_zones && > +ns->nr_open_zones == n->params.max_open_zones) { > +zone = nvme_remove_zone_head(n, ns, ns->imp_open_zones); > +if (zone) { > +/* > + * Automatically close this implicitly open zone. > + */ > +nvme_aor_dec_open(n, ns); > +nvme_assign_zone_state(n, ns, zone, NVME_ZONE_STATE_CLOSED); > +} > +} > +} > + > static uint16_t nvme_check_zone_write(NvmeZone *zone, uint64_t slba, > uint32_t nlb) > { > @@ -531,6 +630,23 @@ static uint16_t nvme_check_zone_read(NvmeCtrl *n, > NvmeZone *zone, uint64_t slba, > return status; > } > > +static uint16_t nvme_auto_open_zone(NvmeCtrl *n, NvmeNamespace *ns, > +NvmeZone *zone) > +{ > +uint16_t status = NVME_SUCCESS; > +uint8_t zs = nvme_get_zone_state(zone); > + > +if (zs == NVME_ZONE_STATE_EMPTY) { > +nvme_auto_transition_zone(n, ns, true, true); > +status = nvme_aor_check(n, ns, 1, 1); > +} else if (zs == NVME_ZONE_STATE_CLOSED) { > +nvme_auto_transition_zone(n, ns, true,
Re: [PATCH v2 11/18] hw/block/nvme: Introduce max active and open zone limits
On Wed, Jun 17, 2020 at 3:07 PM Dmitry Fomichev wrote: > > Added two module properties, "max_active" and "max_open" to control > the maximum number of zones that can be active or open. Once these > variables are set to non-default values, the driver checks these > limits during I/O and returns Too Many Active or Too Many Open > command status if they are exceeded. > > Signed-off-by: Hans Holmberg > Signed-off-by: Dmitry Fomichev > --- > hw/block/nvme.c | 183 +++- > hw/block/nvme.h | 4 ++ > 2 files changed, 185 insertions(+), 2 deletions(-) > > diff --git a/hw/block/nvme.c b/hw/block/nvme.c > index 2e03b0b6ed..05a7cbcfcc 100644 > --- a/hw/block/nvme.c > +++ b/hw/block/nvme.c > @@ -120,6 +120,87 @@ static void nvme_remove_zone(NvmeCtrl *n, NvmeNamespace > *ns, NvmeZoneList *zl, > zone->prev = zone->next = 0; > } > > +/* > + * Take the first zone out from a list, return NULL if the list is empty. > + */ > +static NvmeZone *nvme_remove_zone_head(NvmeCtrl *n, NvmeNamespace *ns, > +NvmeZoneList *zl) > +{ > +NvmeZone *zone = nvme_peek_zone_head(ns, zl); > + > +if (zone) { > +--zl->size; > +if (zl->size == 0) { > +zl->head = NVME_ZONE_LIST_NIL; > +zl->tail = NVME_ZONE_LIST_NIL; > +} else { > +zl->head = zone->next; > +ns->zone_array[zl->head].prev = NVME_ZONE_LIST_NIL; > +} > +zone->prev = zone->next = 0; > +} > + > +return zone; > +} > + > +/* > + * Check if we can open a zone without exceeding open/active limits. > + * AOR stands for "Active and Open Resources" (see TP 4053 section 2.5). > + */ > +static int nvme_aor_check(NvmeCtrl *n, NvmeNamespace *ns, > + uint32_t act, uint32_t opn) > +{ > +if (n->params.max_active_zones != 0 && > +ns->nr_active_zones + act > n->params.max_active_zones) { > +trace_pci_nvme_err_insuff_active_res(n->params.max_active_zones); > +return NVME_ZONE_TOO_MANY_ACTIVE | NVME_DNR; > +} > +if (n->params.max_open_zones != 0 && > +ns->nr_open_zones + opn > n->params.max_open_zones) { > +trace_pci_nvme_err_insuff_open_res(n->params.max_open_zones); > +return NVME_ZONE_TOO_MANY_OPEN | NVME_DNR; > +} > + > +return NVME_SUCCESS; > +} > + > +static inline void nvme_aor_inc_open(NvmeCtrl *n, NvmeNamespace *ns) > +{ > +assert(ns->nr_open_zones >= 0); > +if (n->params.max_open_zones) { > +ns->nr_open_zones++; > +assert(ns->nr_open_zones <= n->params.max_open_zones); > +} > +} > + > +static inline void nvme_aor_dec_open(NvmeCtrl *n, NvmeNamespace *ns) > +{ > +if (n->params.max_open_zones) { > +assert(ns->nr_open_zones > 0); > +ns->nr_open_zones--; > +} > +assert(ns->nr_open_zones >= 0); > +} > + > +static inline void nvme_aor_inc_active(NvmeCtrl *n, NvmeNamespace *ns) > +{ > +assert(ns->nr_active_zones >= 0); > +if (n->params.max_active_zones) { > +ns->nr_active_zones++; > +assert(ns->nr_active_zones <= n->params.max_active_zones); > +} > +} > + > +static inline void nvme_aor_dec_active(NvmeCtrl *n, NvmeNamespace *ns) > +{ > +if (n->params.max_active_zones) { > +assert(ns->nr_active_zones > 0); > +ns->nr_active_zones--; > +assert(ns->nr_active_zones >= ns->nr_open_zones); > +} > +assert(ns->nr_active_zones >= 0); > +} > + > static void nvme_assign_zone_state(NvmeCtrl *n, NvmeNamespace *ns, > NvmeZone *zone, uint8_t state) > { > @@ -454,6 +535,24 @@ static void nvme_enqueue_req_completion(NvmeCQueue *cq, > NvmeRequest *req) > timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); > } > > +static void nvme_auto_transition_zone(NvmeCtrl *n, NvmeNamespace *ns, > +bool implicit, bool adding_active) > +{ > +NvmeZone *zone; > + > +if (implicit && n->params.max_open_zones && > +ns->nr_open_zones == n->params.max_open_zones) { > +zone = nvme_remove_zone_head(n, ns, ns->imp_open_zones); > +if (zone) { > +/* > + * Automatically close this implicitly open zone. > + */ > +nvme_aor_dec_open(n, ns); > +nvme_assign_zone_state(n, ns, zone, NVME_ZONE_STATE_CLOSED); > +} > +} > +} > + > static uint16_t nvme_check_zone_write(NvmeZone *zone, uint64_t slba, > uint32_t nlb) > { > @@ -531,6 +630,23 @@ static uint16_t nvme_check_zone_read(NvmeCtrl *n, > NvmeZone *zone, uint64_t slba, > return status; > } > > +static uint16_t nvme_auto_open_zone(NvmeCtrl *n, NvmeNamespace *ns, > +NvmeZone *zone) > +{ > +uint16_t status = NVME_SUCCESS; > +uint8_t zs = nvme_get_zone_state(zone); > + > +if (zs == NVME_ZONE_STATE_EMPTY) { > +nvme_auto_transition_zone(n, ns, true, true); > +status = nvme_aor_check(n, ns, 1, 1); > +} else if (zs == NVME_ZONE_STATE_CLOSED) { > +nvme_auto_transition_zone(n,
[PATCH v2 11/18] hw/block/nvme: Introduce max active and open zone limits
Added two module properties, "max_active" and "max_open" to control the maximum number of zones that can be active or open. Once these variables are set to non-default values, the driver checks these limits during I/O and returns Too Many Active or Too Many Open command status if they are exceeded. Signed-off-by: Hans Holmberg Signed-off-by: Dmitry Fomichev --- hw/block/nvme.c | 183 +++- hw/block/nvme.h | 4 ++ 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 2e03b0b6ed..05a7cbcfcc 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -120,6 +120,87 @@ static void nvme_remove_zone(NvmeCtrl *n, NvmeNamespace *ns, NvmeZoneList *zl, zone->prev = zone->next = 0; } +/* + * Take the first zone out from a list, return NULL if the list is empty. + */ +static NvmeZone *nvme_remove_zone_head(NvmeCtrl *n, NvmeNamespace *ns, +NvmeZoneList *zl) +{ +NvmeZone *zone = nvme_peek_zone_head(ns, zl); + +if (zone) { +--zl->size; +if (zl->size == 0) { +zl->head = NVME_ZONE_LIST_NIL; +zl->tail = NVME_ZONE_LIST_NIL; +} else { +zl->head = zone->next; +ns->zone_array[zl->head].prev = NVME_ZONE_LIST_NIL; +} +zone->prev = zone->next = 0; +} + +return zone; +} + +/* + * Check if we can open a zone without exceeding open/active limits. + * AOR stands for "Active and Open Resources" (see TP 4053 section 2.5). + */ +static int nvme_aor_check(NvmeCtrl *n, NvmeNamespace *ns, + uint32_t act, uint32_t opn) +{ +if (n->params.max_active_zones != 0 && +ns->nr_active_zones + act > n->params.max_active_zones) { +trace_pci_nvme_err_insuff_active_res(n->params.max_active_zones); +return NVME_ZONE_TOO_MANY_ACTIVE | NVME_DNR; +} +if (n->params.max_open_zones != 0 && +ns->nr_open_zones + opn > n->params.max_open_zones) { +trace_pci_nvme_err_insuff_open_res(n->params.max_open_zones); +return NVME_ZONE_TOO_MANY_OPEN | NVME_DNR; +} + +return NVME_SUCCESS; +} + +static inline void nvme_aor_inc_open(NvmeCtrl *n, NvmeNamespace *ns) +{ +assert(ns->nr_open_zones >= 0); +if (n->params.max_open_zones) { +ns->nr_open_zones++; +assert(ns->nr_open_zones <= n->params.max_open_zones); +} +} + +static inline void nvme_aor_dec_open(NvmeCtrl *n, NvmeNamespace *ns) +{ +if (n->params.max_open_zones) { +assert(ns->nr_open_zones > 0); +ns->nr_open_zones--; +} +assert(ns->nr_open_zones >= 0); +} + +static inline void nvme_aor_inc_active(NvmeCtrl *n, NvmeNamespace *ns) +{ +assert(ns->nr_active_zones >= 0); +if (n->params.max_active_zones) { +ns->nr_active_zones++; +assert(ns->nr_active_zones <= n->params.max_active_zones); +} +} + +static inline void nvme_aor_dec_active(NvmeCtrl *n, NvmeNamespace *ns) +{ +if (n->params.max_active_zones) { +assert(ns->nr_active_zones > 0); +ns->nr_active_zones--; +assert(ns->nr_active_zones >= ns->nr_open_zones); +} +assert(ns->nr_active_zones >= 0); +} + static void nvme_assign_zone_state(NvmeCtrl *n, NvmeNamespace *ns, NvmeZone *zone, uint8_t state) { @@ -454,6 +535,24 @@ static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req) timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); } +static void nvme_auto_transition_zone(NvmeCtrl *n, NvmeNamespace *ns, +bool implicit, bool adding_active) +{ +NvmeZone *zone; + +if (implicit && n->params.max_open_zones && +ns->nr_open_zones == n->params.max_open_zones) { +zone = nvme_remove_zone_head(n, ns, ns->imp_open_zones); +if (zone) { +/* + * Automatically close this implicitly open zone. + */ +nvme_aor_dec_open(n, ns); +nvme_assign_zone_state(n, ns, zone, NVME_ZONE_STATE_CLOSED); +} +} +} + static uint16_t nvme_check_zone_write(NvmeZone *zone, uint64_t slba, uint32_t nlb) { @@ -531,6 +630,23 @@ static uint16_t nvme_check_zone_read(NvmeCtrl *n, NvmeZone *zone, uint64_t slba, return status; } +static uint16_t nvme_auto_open_zone(NvmeCtrl *n, NvmeNamespace *ns, +NvmeZone *zone) +{ +uint16_t status = NVME_SUCCESS; +uint8_t zs = nvme_get_zone_state(zone); + +if (zs == NVME_ZONE_STATE_EMPTY) { +nvme_auto_transition_zone(n, ns, true, true); +status = nvme_aor_check(n, ns, 1, 1); +} else if (zs == NVME_ZONE_STATE_CLOSED) { +nvme_auto_transition_zone(n, ns, true, false); +status = nvme_aor_check(n, ns, 0, 1); +} + +return status; +} + static uint64_t nvme_finalize_zone_write(NvmeCtrl *n, NvmeNamespace *ns, NvmeZone *zone, uint32_t nlb) { @@ -543,7 +659,11 @@ static uint64_t nvme_finalize_zone_write(NvmeCtrl *n, NvmeNamespace *ns, switch (zs) { case NVM