@@ -25,6 +25,7 @@
#include "qemu/osdep.h"
#include "block/qdict.h"
+#include "block/nvme.h"
#include "sysemu/block-backend.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
@@ -197,6 +198,17 @@ qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp)
#define QCOW2_ZT_IS_CONV(wp) (wp & 1ULL << 59)
+static inline void qcow2_set_za(uint64_t *wp, uint8_t za)
+{
+ /*
+ * The zone attribute takes up one byte. Store it after the zoned
+ * bit.
+ */
+ uint64_t addr = *wp;
+ addr |= ((uint64_t)za << 51);
+ *wp = addr;
+}
+
/*
* To emulate a real zoned device, closed, empty and full states are
* preserved after a power cycle. The open states are in-memory and will
@@ -5053,6 +5065,36 @@ unlock:
return ret;
}
+static int coroutine_fn GRAPH_RDLOCK
+qcow2_zns_set_zded(BlockDriverState *bs, uint32_t index)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int ret;
+
+ qemu_co_mutex_lock(&bs->wps->colock);
+ uint64_t *wp = &bs->wps->wp[index];
+ BlockZoneState zs = qcow2_get_zone_state(bs, index);
+ if (zs == BLK_ZS_EMPTY) {
+ if (!qcow2_can_activate_zone(bs)) {
+ goto unlock;
+ }
+
+ qcow2_set_za(wp, NVME_ZA_ZD_EXT_VALID);
+ ret = qcow2_write_wp_at(bs, wp, index);
+ if (ret < 0) {
+ error_report("Failed to set zone extension at 0x%" PRIx64 "", *wp);
+ goto unlock;
+ }
+ s->nr_zones_closed++;
+ qemu_co_mutex_unlock(&bs->wps->colock);
+ return ret;
+ }
+
+unlock:
+ qemu_co_mutex_unlock(&bs->wps->colock);
+ return NVME_ZONE_INVAL_TRANSITION;
+}
+
static int coroutine_fn GRAPH_RDLOCK
qcow2_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op,
int64_t offset, int64_t len)
@@ -5110,6 +5152,9 @@ qcow2_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op,
case BLK_ZO_OFFLINE:
/* There are no transitions from the offline state to any other state */
break;
+ case BLK_ZO_SET_ZDED:
+ ret = qcow2_zns_set_zded(bs, index);
+ break;
default:
error_report("Unsupported zone op: 0x%x", op);
ret = -ENOTSUP;
@@ -3465,6 +3465,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
break;
case NVME_ZONE_ACTION_SET_ZD_EXT:
+ op = BLK_ZO_SET_ZDED;
int zd_ext_size = blk_get_zd_ext_size(blk);
trace_pci_nvme_set_descriptor_extension(slba, zone_idx);
if (all || !zd_ext_size) {
@@ -88,6 +88,7 @@ typedef enum BlockZoneOp {
BLK_ZO_FINISH,
BLK_ZO_RESET,
BLK_ZO_OFFLINE,
+ BLK_ZO_SET_ZDED,
} BlockZoneOp;
typedef enum BlockZoneModel {
Zone descriptor extension data (ZDED) is not persistent across QEMU restarts. The zone descriptor extension valid bit (ZDEV) is part of zone attributes, which sets to one when the ZDED is associated with the zone. With the qcow2 img as the backing file, the NVMe ZNS device stores the zone attributes at the following eight bit of zone type bit of write pointers for each zone. The ZDED is stored as part of zoned metadata as write pointers. Signed-off-by: Sam Li <faithilikerun@gmail.com> --- block/qcow2.c | 45 ++++++++++++++++++++++++++++++++++++ hw/nvme/ctrl.c | 1 + include/block/block-common.h | 1 + 3 files changed, 47 insertions(+)