2013-06-04 17:17:10 +02:00
|
|
|
#ifndef HW_NVME_H
|
|
|
|
#define HW_NVME_H
|
2020-06-09 21:03:15 +02:00
|
|
|
|
2018-01-16 07:08:59 +01:00
|
|
|
#include "block/nvme.h"
|
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 08:51:06 +02:00
|
|
|
#include "nvme-ns.h"
|
|
|
|
|
|
|
|
#define NVME_MAX_NAMESPACES 256
|
2013-06-04 17:17:10 +02:00
|
|
|
|
2020-12-08 21:04:06 +01:00
|
|
|
#define NVME_DEFAULT_ZONE_SIZE (128 * MiB)
|
|
|
|
#define NVME_DEFAULT_MAX_ZA_SIZE (128 * KiB)
|
|
|
|
|
2020-06-09 21:03:15 +02:00
|
|
|
typedef struct NvmeParams {
|
|
|
|
char *serial;
|
2020-06-09 21:03:19 +02:00
|
|
|
uint32_t num_queues; /* deprecated since 5.1 */
|
|
|
|
uint32_t max_ioqpairs;
|
2020-06-09 21:03:32 +02:00
|
|
|
uint16_t msix_qsize;
|
2020-06-09 21:03:15 +02:00
|
|
|
uint32_t cmb_size_mb;
|
2020-07-06 08:12:53 +02:00
|
|
|
uint8_t aerl;
|
|
|
|
uint32_t aer_max_queued;
|
2020-02-23 17:38:22 +01:00
|
|
|
uint8_t mdts;
|
2019-09-27 11:43:12 +02:00
|
|
|
bool use_intel_id;
|
2020-12-08 21:04:06 +01:00
|
|
|
uint32_t zasl_bs;
|
2020-12-18 00:32:16 +01:00
|
|
|
bool legacy_cmb;
|
2020-06-09 21:03:15 +02:00
|
|
|
} NvmeParams;
|
|
|
|
|
2013-06-04 17:17:10 +02:00
|
|
|
typedef struct NvmeAsyncEvent {
|
2020-07-06 08:12:53 +02:00
|
|
|
QTAILQ_ENTRY(NvmeAsyncEvent) entry;
|
2013-06-04 17:17:10 +02:00
|
|
|
NvmeAerResult result;
|
|
|
|
} NvmeAsyncEvent;
|
|
|
|
|
|
|
|
typedef struct NvmeRequest {
|
|
|
|
struct NvmeSQueue *sq;
|
2020-07-20 12:44:01 +02:00
|
|
|
struct NvmeNamespace *ns;
|
2014-10-07 13:59:14 +02:00
|
|
|
BlockAIOCB *aiocb;
|
2013-06-04 17:17:10 +02:00
|
|
|
uint16_t status;
|
2020-10-21 14:03:19 +02:00
|
|
|
void *opaque;
|
2013-06-04 17:17:10 +02:00
|
|
|
NvmeCqe cqe;
|
2020-07-20 12:44:01 +02:00
|
|
|
NvmeCmd cmd;
|
2013-06-04 17:17:10 +02:00
|
|
|
BlockAcctCookie acct;
|
|
|
|
QEMUSGList qsg;
|
2017-06-13 12:08:35 +02:00
|
|
|
QEMUIOVector iov;
|
2013-06-04 17:17:10 +02:00
|
|
|
QTAILQ_ENTRY(NvmeRequest)entry;
|
|
|
|
} NvmeRequest;
|
|
|
|
|
2020-08-24 22:11:33 +02:00
|
|
|
static inline const char *nvme_adm_opc_str(uint8_t opc)
|
|
|
|
{
|
|
|
|
switch (opc) {
|
|
|
|
case NVME_ADM_CMD_DELETE_SQ: return "NVME_ADM_CMD_DELETE_SQ";
|
|
|
|
case NVME_ADM_CMD_CREATE_SQ: return "NVME_ADM_CMD_CREATE_SQ";
|
|
|
|
case NVME_ADM_CMD_GET_LOG_PAGE: return "NVME_ADM_CMD_GET_LOG_PAGE";
|
|
|
|
case NVME_ADM_CMD_DELETE_CQ: return "NVME_ADM_CMD_DELETE_CQ";
|
|
|
|
case NVME_ADM_CMD_CREATE_CQ: return "NVME_ADM_CMD_CREATE_CQ";
|
|
|
|
case NVME_ADM_CMD_IDENTIFY: return "NVME_ADM_CMD_IDENTIFY";
|
|
|
|
case NVME_ADM_CMD_ABORT: return "NVME_ADM_CMD_ABORT";
|
|
|
|
case NVME_ADM_CMD_SET_FEATURES: return "NVME_ADM_CMD_SET_FEATURES";
|
|
|
|
case NVME_ADM_CMD_GET_FEATURES: return "NVME_ADM_CMD_GET_FEATURES";
|
|
|
|
case NVME_ADM_CMD_ASYNC_EV_REQ: return "NVME_ADM_CMD_ASYNC_EV_REQ";
|
|
|
|
default: return "NVME_ADM_CMD_UNKNOWN";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const char *nvme_io_opc_str(uint8_t opc)
|
|
|
|
{
|
|
|
|
switch (opc) {
|
|
|
|
case NVME_CMD_FLUSH: return "NVME_NVM_CMD_FLUSH";
|
|
|
|
case NVME_CMD_WRITE: return "NVME_NVM_CMD_WRITE";
|
|
|
|
case NVME_CMD_READ: return "NVME_NVM_CMD_READ";
|
2020-12-09 23:55:06 +01:00
|
|
|
case NVME_CMD_COMPARE: return "NVME_NVM_CMD_COMPARE";
|
2020-08-24 22:11:33 +02:00
|
|
|
case NVME_CMD_WRITE_ZEROES: return "NVME_NVM_CMD_WRITE_ZEROES";
|
2020-10-21 14:03:19 +02:00
|
|
|
case NVME_CMD_DSM: return "NVME_NVM_CMD_DSM";
|
2020-12-09 23:55:06 +01:00
|
|
|
case NVME_CMD_ZONE_MGMT_SEND: return "NVME_ZONED_CMD_MGMT_SEND";
|
|
|
|
case NVME_CMD_ZONE_MGMT_RECV: return "NVME_ZONED_CMD_MGMT_RECV";
|
|
|
|
case NVME_CMD_ZONE_APPEND: return "NVME_ZONED_CMD_ZONE_APPEND";
|
2020-08-24 22:11:33 +02:00
|
|
|
default: return "NVME_NVM_CMD_UNKNOWN";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-04 17:17:10 +02:00
|
|
|
typedef struct NvmeSQueue {
|
|
|
|
struct NvmeCtrl *ctrl;
|
|
|
|
uint16_t sqid;
|
|
|
|
uint16_t cqid;
|
|
|
|
uint32_t head;
|
|
|
|
uint32_t tail;
|
|
|
|
uint32_t size;
|
|
|
|
uint64_t dma_addr;
|
|
|
|
QEMUTimer *timer;
|
|
|
|
NvmeRequest *io_req;
|
2018-12-06 11:58:10 +01:00
|
|
|
QTAILQ_HEAD(, NvmeRequest) req_list;
|
|
|
|
QTAILQ_HEAD(, NvmeRequest) out_req_list;
|
2013-06-04 17:17:10 +02:00
|
|
|
QTAILQ_ENTRY(NvmeSQueue) entry;
|
|
|
|
} NvmeSQueue;
|
|
|
|
|
|
|
|
typedef struct NvmeCQueue {
|
|
|
|
struct NvmeCtrl *ctrl;
|
|
|
|
uint8_t phase;
|
|
|
|
uint16_t cqid;
|
|
|
|
uint16_t irq_enabled;
|
|
|
|
uint32_t head;
|
|
|
|
uint32_t tail;
|
|
|
|
uint32_t vector;
|
|
|
|
uint32_t size;
|
|
|
|
uint64_t dma_addr;
|
|
|
|
QEMUTimer *timer;
|
2018-12-06 11:58:10 +01:00
|
|
|
QTAILQ_HEAD(, NvmeSQueue) sq_list;
|
|
|
|
QTAILQ_HEAD(, NvmeRequest) req_list;
|
2013-06-04 17:17:10 +02:00
|
|
|
} NvmeCQueue;
|
|
|
|
|
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 08:51:06 +02:00
|
|
|
#define TYPE_NVME_BUS "nvme-bus"
|
|
|
|
#define NVME_BUS(obj) OBJECT_CHECK(NvmeBus, (obj), TYPE_NVME_BUS)
|
2020-06-09 21:03:24 +02: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 08:51:06 +02:00
|
|
|
typedef struct NvmeBus {
|
|
|
|
BusState parent_bus;
|
|
|
|
} NvmeBus;
|
2020-08-24 08:59:41 +02:00
|
|
|
|
2013-06-04 17:17:10 +02:00
|
|
|
#define TYPE_NVME "nvme"
|
|
|
|
#define NVME(obj) \
|
|
|
|
OBJECT_CHECK(NvmeCtrl, (obj), TYPE_NVME)
|
|
|
|
|
2020-07-06 08:12:54 +02:00
|
|
|
typedef struct NvmeFeatureVal {
|
|
|
|
struct {
|
|
|
|
uint16_t temp_thresh_hi;
|
|
|
|
uint16_t temp_thresh_low;
|
|
|
|
};
|
|
|
|
uint32_t async_config;
|
|
|
|
} NvmeFeatureVal;
|
|
|
|
|
2013-06-04 17:17:10 +02:00
|
|
|
typedef struct NvmeCtrl {
|
|
|
|
PCIDevice parent_obj;
|
2020-11-13 09:50:33 +01:00
|
|
|
MemoryRegion bar0;
|
2013-06-04 17:17:10 +02:00
|
|
|
MemoryRegion iomem;
|
|
|
|
NvmeBar bar;
|
2020-06-09 21:03:15 +02:00
|
|
|
NvmeParams params;
|
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 08:51:06 +02:00
|
|
|
NvmeBus bus;
|
|
|
|
BlockConf conf;
|
2013-06-04 17:17:10 +02:00
|
|
|
|
2020-07-06 08:13:01 +02:00
|
|
|
bool qs_created;
|
2014-11-27 04:39:21 +01:00
|
|
|
uint32_t page_size;
|
2013-06-04 17:17:10 +02:00
|
|
|
uint16_t page_bits;
|
|
|
|
uint16_t max_prp_ents;
|
|
|
|
uint16_t cqe_size;
|
|
|
|
uint16_t sqe_size;
|
|
|
|
uint32_t reg_size;
|
|
|
|
uint32_t num_namespaces;
|
|
|
|
uint32_t max_q_ents;
|
2020-07-06 08:12:53 +02:00
|
|
|
uint8_t outstanding_aers;
|
2020-06-09 21:03:18 +02:00
|
|
|
uint32_t irq_status;
|
2019-05-20 19:40:30 +02:00
|
|
|
uint64_t host_timestamp; /* Timestamp sent by the host */
|
|
|
|
uint64_t timestamp_set_qemu_clock_ms; /* QEMU clock time */
|
2020-07-06 08:12:52 +02:00
|
|
|
uint64_t starttime_ms;
|
|
|
|
uint16_t temperature;
|
2021-01-15 04:27:01 +01:00
|
|
|
uint8_t smart_critical_warning;
|
2013-06-04 17:17:10 +02:00
|
|
|
|
2020-12-18 00:32:16 +01:00
|
|
|
struct {
|
|
|
|
MemoryRegion mem;
|
|
|
|
uint8_t *buf;
|
|
|
|
bool cmse;
|
|
|
|
hwaddr cba;
|
|
|
|
} cmb;
|
|
|
|
|
2020-11-13 06:30:05 +01:00
|
|
|
struct {
|
|
|
|
HostMemoryBackend *dev;
|
|
|
|
bool cmse;
|
|
|
|
hwaddr cba;
|
|
|
|
} pmr;
|
2020-03-30 18:46:56 +02:00
|
|
|
|
2020-07-06 08:12:53 +02:00
|
|
|
uint8_t aer_mask;
|
|
|
|
NvmeRequest **aer_reqs;
|
|
|
|
QTAILQ_HEAD(, NvmeAsyncEvent) aer_queue;
|
|
|
|
int aer_queued;
|
|
|
|
|
2020-12-08 21:04:06 +01:00
|
|
|
uint8_t zasl;
|
|
|
|
|
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 08:51:06 +02:00
|
|
|
NvmeNamespace namespace;
|
|
|
|
NvmeNamespace *namespaces[NVME_MAX_NAMESPACES];
|
2013-06-04 17:17:10 +02:00
|
|
|
NvmeSQueue **sq;
|
|
|
|
NvmeCQueue **cq;
|
|
|
|
NvmeSQueue admin_sq;
|
|
|
|
NvmeCQueue admin_cq;
|
|
|
|
NvmeIdCtrl id_ctrl;
|
2020-07-06 08:12:50 +02:00
|
|
|
NvmeFeatureVal features;
|
2013-06-04 17:17:10 +02:00
|
|
|
} NvmeCtrl;
|
|
|
|
|
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 08:51:06 +02:00
|
|
|
static inline NvmeNamespace *nvme_ns(NvmeCtrl *n, uint32_t nsid)
|
2020-06-09 21:03:24 +02: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 08:51:06 +02:00
|
|
|
if (!nsid || nsid > n->num_namespaces) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return n->namespaces[nsid - 1];
|
2020-06-09 21:03:24 +02:00
|
|
|
}
|
|
|
|
|
2020-08-24 12:43:38 +02:00
|
|
|
static inline NvmeCQueue *nvme_cq(NvmeRequest *req)
|
|
|
|
{
|
|
|
|
NvmeSQueue *sq = req->sq;
|
|
|
|
NvmeCtrl *n = sq->ctrl;
|
|
|
|
|
|
|
|
return n->cq[sq->cqid];
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline NvmeCtrl *nvme_ctrl(NvmeRequest *req)
|
|
|
|
{
|
|
|
|
NvmeSQueue *sq = req->sq;
|
|
|
|
return sq->ctrl;
|
|
|
|
}
|
|
|
|
|
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 08:51:06 +02:00
|
|
|
int nvme_register_namespace(NvmeCtrl *n, NvmeNamespace *ns, Error **errp);
|
|
|
|
|
2013-06-04 17:17:10 +02:00
|
|
|
#endif /* HW_NVME_H */
|