Format NVM admin command can make a namespace or namespaces to be with different LBA size and metadata size with protection information types. This patch introduces Format NVM command with LBA format, Metadata, and Protection Information for the device. The secure erase operation things and support for formatting zoned namespaces are yet to be added. The parameter checks inside of this patch has been referred from Keith's old branch. Signed-off-by: Minwoo Im <minwoo.im@samsung.com> [anaidu.gollu: rebased on e2e] Signed-off-by: Gollu Appalanaidu <anaidu.gollu@samsung.com> [k.jensen: rebased for reworked aio tracking] Signed-off-by: Klaus Jensen <k.jensen@samsung.com> Reviewed-by: Keith Busch <kbusch@kernel.org>
234 lines
5.3 KiB
C
234 lines
5.3 KiB
C
/*
|
|
* QEMU NVM Express Virtual Namespace
|
|
*
|
|
* Copyright (c) 2019 CNEX Labs
|
|
* Copyright (c) 2020 Samsung Electronics
|
|
*
|
|
* Authors:
|
|
* Klaus Jensen <k.jensen@samsung.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2. See the
|
|
* COPYING file in the top-level directory.
|
|
*
|
|
*/
|
|
|
|
#ifndef NVME_NS_H
|
|
#define NVME_NS_H
|
|
|
|
#include "qemu/uuid.h"
|
|
|
|
#define TYPE_NVME_NS "nvme-ns"
|
|
#define NVME_NS(obj) \
|
|
OBJECT_CHECK(NvmeNamespace, (obj), TYPE_NVME_NS)
|
|
|
|
typedef struct NvmeZone {
|
|
NvmeZoneDescr d;
|
|
uint64_t w_ptr;
|
|
QTAILQ_ENTRY(NvmeZone) entry;
|
|
} NvmeZone;
|
|
|
|
typedef struct NvmeNamespaceParams {
|
|
bool detached;
|
|
uint32_t nsid;
|
|
QemuUUID uuid;
|
|
|
|
uint16_t ms;
|
|
uint8_t mset;
|
|
uint8_t pi;
|
|
uint8_t pil;
|
|
|
|
uint16_t mssrl;
|
|
uint32_t mcl;
|
|
uint8_t msrc;
|
|
|
|
bool zoned;
|
|
bool cross_zone_read;
|
|
uint64_t zone_size_bs;
|
|
uint64_t zone_cap_bs;
|
|
uint32_t max_active_zones;
|
|
uint32_t max_open_zones;
|
|
uint32_t zd_extension_size;
|
|
} NvmeNamespaceParams;
|
|
|
|
typedef struct NvmeNamespace {
|
|
DeviceState parent_obj;
|
|
BlockConf blkconf;
|
|
int32_t bootindex;
|
|
int64_t size;
|
|
int64_t mdata_offset;
|
|
NvmeIdNs id_ns;
|
|
const uint32_t *iocs;
|
|
uint8_t csi;
|
|
uint16_t status;
|
|
|
|
NvmeSubsystem *subsys;
|
|
QTAILQ_ENTRY(NvmeNamespace) entry;
|
|
|
|
NvmeIdNsZoned *id_ns_zoned;
|
|
NvmeZone *zone_array;
|
|
QTAILQ_HEAD(, NvmeZone) exp_open_zones;
|
|
QTAILQ_HEAD(, NvmeZone) imp_open_zones;
|
|
QTAILQ_HEAD(, NvmeZone) closed_zones;
|
|
QTAILQ_HEAD(, NvmeZone) full_zones;
|
|
uint32_t num_zones;
|
|
uint64_t zone_size;
|
|
uint64_t zone_capacity;
|
|
uint32_t zone_size_log2;
|
|
uint8_t *zd_extensions;
|
|
int32_t nr_open_zones;
|
|
int32_t nr_active_zones;
|
|
|
|
NvmeNamespaceParams params;
|
|
|
|
struct {
|
|
uint32_t err_rec;
|
|
} features;
|
|
} NvmeNamespace;
|
|
|
|
static inline uint16_t nvme_ns_status(NvmeNamespace *ns)
|
|
{
|
|
return ns->status;
|
|
}
|
|
|
|
static inline uint32_t nvme_nsid(NvmeNamespace *ns)
|
|
{
|
|
if (ns) {
|
|
return ns->params.nsid;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static inline bool nvme_ns_shared(NvmeNamespace *ns)
|
|
{
|
|
return !!ns->subsys;
|
|
}
|
|
|
|
static inline NvmeLBAF *nvme_ns_lbaf(NvmeNamespace *ns)
|
|
{
|
|
NvmeIdNs *id_ns = &ns->id_ns;
|
|
return &id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(id_ns->flbas)];
|
|
}
|
|
|
|
static inline uint8_t nvme_ns_lbads(NvmeNamespace *ns)
|
|
{
|
|
return nvme_ns_lbaf(ns)->ds;
|
|
}
|
|
|
|
/* convert an LBA to the equivalent in bytes */
|
|
static inline size_t nvme_l2b(NvmeNamespace *ns, uint64_t lba)
|
|
{
|
|
return lba << nvme_ns_lbads(ns);
|
|
}
|
|
|
|
static inline size_t nvme_lsize(NvmeNamespace *ns)
|
|
{
|
|
return 1 << nvme_ns_lbads(ns);
|
|
}
|
|
|
|
static inline uint16_t nvme_msize(NvmeNamespace *ns)
|
|
{
|
|
return nvme_ns_lbaf(ns)->ms;
|
|
}
|
|
|
|
static inline size_t nvme_m2b(NvmeNamespace *ns, uint64_t lba)
|
|
{
|
|
return nvme_msize(ns) * lba;
|
|
}
|
|
|
|
static inline bool nvme_ns_ext(NvmeNamespace *ns)
|
|
{
|
|
return !!NVME_ID_NS_FLBAS_EXTENDED(ns->id_ns.flbas);
|
|
}
|
|
|
|
/* calculate the number of LBAs that the namespace can accomodate */
|
|
static inline uint64_t nvme_ns_nlbas(NvmeNamespace *ns)
|
|
{
|
|
if (nvme_msize(ns)) {
|
|
return ns->size / (nvme_lsize(ns) + nvme_msize(ns));
|
|
}
|
|
return ns->size >> nvme_ns_lbads(ns);
|
|
}
|
|
|
|
typedef struct NvmeCtrl NvmeCtrl;
|
|
|
|
static inline NvmeZoneState nvme_get_zone_state(NvmeZone *zone)
|
|
{
|
|
return zone->d.zs >> 4;
|
|
}
|
|
|
|
static inline void nvme_set_zone_state(NvmeZone *zone, NvmeZoneState state)
|
|
{
|
|
zone->d.zs = state << 4;
|
|
}
|
|
|
|
static inline uint64_t nvme_zone_rd_boundary(NvmeNamespace *ns, NvmeZone *zone)
|
|
{
|
|
return zone->d.zslba + ns->zone_size;
|
|
}
|
|
|
|
static inline uint64_t nvme_zone_wr_boundary(NvmeZone *zone)
|
|
{
|
|
return zone->d.zslba + zone->d.zcap;
|
|
}
|
|
|
|
static inline bool nvme_wp_is_valid(NvmeZone *zone)
|
|
{
|
|
uint8_t st = nvme_get_zone_state(zone);
|
|
|
|
return st != NVME_ZONE_STATE_FULL &&
|
|
st != NVME_ZONE_STATE_READ_ONLY &&
|
|
st != NVME_ZONE_STATE_OFFLINE;
|
|
}
|
|
|
|
static inline uint8_t *nvme_get_zd_extension(NvmeNamespace *ns,
|
|
uint32_t zone_idx)
|
|
{
|
|
return &ns->zd_extensions[zone_idx * ns->params.zd_extension_size];
|
|
}
|
|
|
|
static inline void nvme_aor_inc_open(NvmeNamespace *ns)
|
|
{
|
|
assert(ns->nr_open_zones >= 0);
|
|
if (ns->params.max_open_zones) {
|
|
ns->nr_open_zones++;
|
|
assert(ns->nr_open_zones <= ns->params.max_open_zones);
|
|
}
|
|
}
|
|
|
|
static inline void nvme_aor_dec_open(NvmeNamespace *ns)
|
|
{
|
|
if (ns->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(NvmeNamespace *ns)
|
|
{
|
|
assert(ns->nr_active_zones >= 0);
|
|
if (ns->params.max_active_zones) {
|
|
ns->nr_active_zones++;
|
|
assert(ns->nr_active_zones <= ns->params.max_active_zones);
|
|
}
|
|
}
|
|
|
|
static inline void nvme_aor_dec_active(NvmeNamespace *ns)
|
|
{
|
|
if (ns->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);
|
|
}
|
|
|
|
void nvme_ns_init_format(NvmeNamespace *ns);
|
|
int nvme_ns_setup(NvmeNamespace *ns, Error **errp);
|
|
void nvme_ns_drain(NvmeNamespace *ns);
|
|
void nvme_ns_shutdown(NvmeNamespace *ns);
|
|
void nvme_ns_cleanup(NvmeNamespace *ns);
|
|
|
|
#endif /* NVME_NS_H */
|