hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
|
|
|
#define TYPE_NVME_NS "nvme-ns"
|
|
|
|
#define NVME_NS(obj) \
|
|
|
|
OBJECT_CHECK(NvmeNamespace, (obj), TYPE_NVME_NS)
|
|
|
|
|
2020-12-08 23:04:06 +03:00
|
|
|
typedef struct NvmeZone {
|
|
|
|
NvmeZoneDescr d;
|
|
|
|
uint64_t w_ptr;
|
|
|
|
QTAILQ_ENTRY(NvmeZone) entry;
|
|
|
|
} NvmeZone;
|
|
|
|
|
hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
typedef struct NvmeNamespaceParams {
|
|
|
|
uint32_t nsid;
|
2020-12-08 23:03:59 +03:00
|
|
|
QemuUUID uuid;
|
2020-12-08 23:04:06 +03:00
|
|
|
|
|
|
|
bool zoned;
|
|
|
|
bool cross_zone_read;
|
|
|
|
uint64_t zone_size_bs;
|
|
|
|
uint64_t zone_cap_bs;
|
2020-12-08 23:04:07 +03:00
|
|
|
uint32_t max_active_zones;
|
|
|
|
uint32_t max_open_zones;
|
2020-12-08 23:04:08 +03:00
|
|
|
uint32_t zd_extension_size;
|
hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
} NvmeNamespaceParams;
|
|
|
|
|
|
|
|
typedef struct NvmeNamespace {
|
|
|
|
DeviceState parent_obj;
|
|
|
|
BlockConf blkconf;
|
|
|
|
int32_t bootindex;
|
|
|
|
int64_t size;
|
|
|
|
NvmeIdNs id_ns;
|
2020-12-08 23:04:02 +03:00
|
|
|
const uint32_t *iocs;
|
2020-12-08 23:04:03 +03:00
|
|
|
uint8_t csi;
|
hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
|
hw/block/nvme: support for shared namespace in subsystem
nvme-ns device is registered to a nvme controller device during the
initialization in nvme_register_namespace() in case that 'bus' property
is given which means it's mapped to a single controller.
This patch introduced a new property 'subsys' just like the controller
device instance did to map a namespace to a NVMe subsystem.
If 'subsys' property is given to the nvme-ns device, it will belong to
the specified subsystem and will be attached to all controllers in that
subsystem by enabling shared namespace capability in NMIC(Namespace
Multi-path I/O and Namespace Capabilities) in Identify Namespace.
Usage:
-device nvme-subsys,id=subsys0
-device nvme,serial=foo,id=nvme0,subsys=subsys0
-device nvme,serial=bar,id=nvme1,subsys=subsys0
-device nvme,serial=baz,id=nvme2,subsys=subsys0
-device nvme-ns,id=ns1,drive=<drv>,nsid=1,subsys=subsys0 # Shared
-device nvme-ns,id=ns2,drive=<drv>,nsid=2,bus=nvme2 # Non-shared
In the above example, 'ns1' will be shared to 'nvme0' and 'nvme1' in
the same subsystem. On the other hand, 'ns2' will be attached to the
'nvme2' only as a private namespace in that subsystem.
All the namespace with 'subsys' parameter will attach all controllers in
the subsystem to the namespace by default.
Signed-off-by: Minwoo Im <minwoo.im.dev@gmail.com>
Tested-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
2021-01-24 05:54:50 +03:00
|
|
|
NvmeSubsystem *subsys;
|
|
|
|
|
2020-12-08 23:04:06 +03:00
|
|
|
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;
|
2020-12-08 23:04:08 +03:00
|
|
|
uint8_t *zd_extensions;
|
2020-12-08 23:04:07 +03:00
|
|
|
int32_t nr_open_zones;
|
|
|
|
int32_t nr_active_zones;
|
2020-12-08 23:04:06 +03:00
|
|
|
|
hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
NvmeNamespaceParams params;
|
2020-10-14 10:55:08 +03:00
|
|
|
|
|
|
|
struct {
|
|
|
|
uint32_t err_rec;
|
|
|
|
} features;
|
hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
} NvmeNamespace;
|
|
|
|
|
|
|
|
static inline uint32_t nvme_nsid(NvmeNamespace *ns)
|
|
|
|
{
|
|
|
|
if (ns) {
|
|
|
|
return ns->params.nsid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
hw/block/nvme: support for shared namespace in subsystem
nvme-ns device is registered to a nvme controller device during the
initialization in nvme_register_namespace() in case that 'bus' property
is given which means it's mapped to a single controller.
This patch introduced a new property 'subsys' just like the controller
device instance did to map a namespace to a NVMe subsystem.
If 'subsys' property is given to the nvme-ns device, it will belong to
the specified subsystem and will be attached to all controllers in that
subsystem by enabling shared namespace capability in NMIC(Namespace
Multi-path I/O and Namespace Capabilities) in Identify Namespace.
Usage:
-device nvme-subsys,id=subsys0
-device nvme,serial=foo,id=nvme0,subsys=subsys0
-device nvme,serial=bar,id=nvme1,subsys=subsys0
-device nvme,serial=baz,id=nvme2,subsys=subsys0
-device nvme-ns,id=ns1,drive=<drv>,nsid=1,subsys=subsys0 # Shared
-device nvme-ns,id=ns2,drive=<drv>,nsid=2,bus=nvme2 # Non-shared
In the above example, 'ns1' will be shared to 'nvme0' and 'nvme1' in
the same subsystem. On the other hand, 'ns2' will be attached to the
'nvme2' only as a private namespace in that subsystem.
All the namespace with 'subsys' parameter will attach all controllers in
the subsystem to the namespace by default.
Signed-off-by: Minwoo Im <minwoo.im.dev@gmail.com>
Tested-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
2021-01-24 05:54:50 +03:00
|
|
|
static inline bool nvme_ns_shared(NvmeNamespace *ns)
|
|
|
|
{
|
|
|
|
return !!ns->subsys;
|
|
|
|
}
|
|
|
|
|
hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate the number of LBAs that the namespace can accomodate */
|
|
|
|
static inline uint64_t nvme_ns_nlbas(NvmeNamespace *ns)
|
|
|
|
{
|
|
|
|
return ns->size >> nvme_ns_lbads(ns);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct NvmeCtrl NvmeCtrl;
|
|
|
|
|
2020-12-10 01:12:49 +03:00
|
|
|
static inline NvmeZoneState nvme_get_zone_state(NvmeZone *zone)
|
2020-12-08 23:04:06 +03:00
|
|
|
{
|
|
|
|
return zone->d.zs >> 4;
|
|
|
|
}
|
|
|
|
|
2020-12-10 01:12:49 +03:00
|
|
|
static inline void nvme_set_zone_state(NvmeZone *zone, NvmeZoneState state)
|
2020-12-08 23:04:06 +03:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-12-08 23:04:08 +03:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2020-12-08 23:04:07 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-01-17 17:53:35 +03:00
|
|
|
int nvme_ns_setup(NvmeNamespace *ns, Error **errp);
|
hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
void nvme_ns_drain(NvmeNamespace *ns);
|
2020-12-08 23:03:58 +03:00
|
|
|
void nvme_ns_shutdown(NvmeNamespace *ns);
|
2020-12-08 23:04:06 +03:00
|
|
|
void nvme_ns_cleanup(NvmeNamespace *ns);
|
hw/block/nvme: support multiple namespaces
This adds support for multiple namespaces by introducing a new 'nvme-ns'
device model. The nvme device creates a bus named from the device name
('id'). The nvme-ns devices then connect to this and registers
themselves with the nvme device.
This changes how an nvme device is created. Example with two namespaces:
-drive file=nvme0n1.img,if=none,id=disk1
-drive file=nvme0n2.img,if=none,id=disk2
-device nvme,serial=deadbeef,id=nvme0
-device nvme-ns,drive=disk1,bus=nvme0,nsid=1
-device nvme-ns,drive=disk2,bus=nvme0,nsid=2
The drive property is kept on the nvme device to keep the change
backward compatible, but the property is now optional. Specifying a
drive for the nvme device will always create the namespace with nsid 1.
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Minwoo Im <minwoo.im.dev@gmail.com>
2019-06-26 09:51:06 +03:00
|
|
|
|
|
|
|
#endif /* NVME_NS_H */
|