[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 04/11] QEMU NVMe: Implement additional admin command
From: |
Keith Busch |
Subject: |
[Qemu-devel] [PATCH 04/11] QEMU NVMe: Implement additional admin commands |
Date: |
Tue, 26 Feb 2013 17:47:07 -0700 |
This adds support for format, asynch event, abort, and format admin
commands, and helper routines to make it easier to put interesting data
in the log pages.
Signed-off-by: Keith Busch <address@hidden>
---
hw/nvme.c | 420 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
hw/nvme.h | 21 +++
2 files changed, 440 insertions(+), 1 deletions(-)
diff --git a/hw/nvme.c b/hw/nvme.c
index f1e0792..eeff762 100644
--- a/hw/nvme.c
+++ b/hw/nvme.c
@@ -46,6 +46,8 @@
#include "hw.h"
#include "pci/msix.h"
#include "pci/pci.h"
+#include "qemu/bitmap.h"
+#include "qemu/bitops.h"
#include "nvme.h"
@@ -56,6 +58,9 @@
#define NVME_MAX_QUEUE_ES 0xf
#define NVME_MIN_CQUEUE_ES 0x4
#define NVME_MIN_SQUEUE_ES 0x6
+#define NVME_SPARE_THRESHOLD 20
+#define NVME_TEMPERATURE 0x143
+#define NVME_OP_ABORTED 0xff
static int instance;
static void nvme_sq_process(void *opaque);
@@ -229,16 +234,106 @@ static void nvme_enqueue_req_completion(NvmeCQueue *cq,
NvmeRequest *req)
qemu_mod_timer(cq->timer, qemu_get_clock_ns(vm_clock) + 500);
}
+static void nvme_enqueue_event(NvmeCtrl *n, uint8_t event_type,
+ uint8_t event_info, uint8_t log_page)
+{
+ AsyncEvent *event = (AsyncEvent *)g_malloc(sizeof(*event));
+ event->result.event_type = event_type;
+ event->result.event_info = event_info;
+ event->result.log_page = log_page;
+ QSIMPLEQ_INSERT_TAIL(&(n->aer_queue), event, entry);
+ qemu_mod_timer(n->aer_timer, qemu_get_clock_ns(vm_clock) + 10000);
+}
+
+static void nvme_aer_process_cb(void *param)
+{
+ NvmeCtrl *n = param;
+ NvmeRequest *req;
+ AerResult *result;
+ AsyncEvent *event, *next;;
+ QSIMPLEQ_FOREACH_SAFE(event, &n->aer_queue, entry, next) {
+ if (n->outstanding_aers <= 0) {
+ break;
+ }
+ if (n->aer_mask & (1 << event->result.event_type)) {
+ continue;
+ }
+
+ QSIMPLEQ_REMOVE_HEAD(&n->aer_queue, entry);
+ n->aer_mask |= 1 << event->result.event_type;
+ n->outstanding_aers--;
+
+ req = n->aer_reqs[n->outstanding_aers];
+ result = (AerResult *)&req->cqe.result;
+ result->event_type = event->result.event_type;
+ result->event_info = event->result.event_info;
+ result->log_page = event->result.log_page;
+ g_free(event);
+
+ req->cqe.status = NVME_SUCCESS << 1;
+ nvme_enqueue_req_completion(n->cq[0], req);
+ }
+}
+
+static void nvme_update_stats(NvmeNamespace *ns, uint16_t nlb, int rw)
+{
+ uint64_t tmp;
+ if (!rw) {
+ if (++ns->host_write_commands[0] == 0) {
+ ++ns->host_write_commands[1];
+ }
+ tmp = ns->data_units_written[0];
+ ns->write_data_counter += nlb + 1;
+ ns->data_units_written[0] += (ns->write_data_counter / 1000);
+ ns->write_data_counter %= 1000;
+ if (tmp > ns->data_units_written[0]) {
+ ++ns->data_units_written[1];
+ }
+ } else {
+ if (++ns->host_read_commands[0] == 0) {
+ ++ns->host_read_commands[1];
+ }
+ tmp = ns->data_units_read[0];
+ ns->read_data_counter += nlb + 1;
+ ns->data_units_read[0] += (ns->read_data_counter / 1000);
+ ns->read_data_counter %= 1000;
+ if (tmp > ns->data_units_read[0]) {
+ ++ns->data_units_read[1];
+ }
+ }
+}
+
+static void nvme_update_ns_util(NvmeNamespace *ns, uint64_t slba, uint16_t nlb)
+{
+ uint64_t nr;
+ uint64_t elba = slba + nlb;
+ unsigned long *addr = ns->util;
+
+ for (nr = slba; nr <= elba; nr++) {
+ if (!test_and_set_bit(nr, addr)) {
+ assert(ns->id_ns.nuse < ns->id_ns.nsze);
+ ++ns->id_ns.nuse;
+ }
+ }
+}
+
static void nvme_rw_cb(void *opaque, int ret)
{
NvmeRequest *req = opaque;
NvmeSQueue *sq = req->sq;
NvmeCtrl *n = sq->ctrl;
NvmeCQueue *cq = n->cq[sq->cqid];
+ NvmeNamespace *ns = req->ns;
n = sq->ctrl;
cq = n->cq[sq->cqid];
qemu_sglist_destroy(&req->qsg);
req->aiocb = NULL;
+
+ nvme_update_stats(ns, req->nlb, req->rw);
+ if (!req->rw) {
+ nvme_update_ns_util(ns, req->slba, req->nlb);
+ }
+
if (!ret) {
req->cqe.status = NVME_SUCCESS << 1;
} else {
@@ -270,6 +365,10 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns,
NvmeCmd *cmd,
uint32_t nlb = (rw->nlb + 1) << (data_shift - 9);
assert(nlb * BDRV_SECTOR_SIZE == req->qsg.size);
+ req->slba = rw->slba;
+ req->nlb = rw->nlb;
+ req->ns = ns;
+ req->rw = data_dir;
req->aiocb = data_dir ?
dma_bdrv_read(n->conf.bs, &req->qsg, slba, nvme_rw_cb, req) :
dma_bdrv_write(n->conf.bs, &req->qsg, slba, nvme_rw_cb, req);
@@ -599,6 +698,276 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd
*cmd, NvmeRequest *req)
return NVME_SUCCESS;
}
+static uint16_t nvme_fw_log_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t buf_len)
+{
+ uint32_t trans_len;
+ NvmeFwSlotInfoLog fw_log;
+ trans_len = MIN(sizeof(fw_log), buf_len);
+ return nvme_dma_prp(cmd->prp1, cmd->prp2, trans_len, n, (uint8_t *)&fw_log,
+ DMA_DIRECTION_FROM_DEVICE);
+}
+
+static uint16_t nvme_error_log_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t
buf_len)
+{
+ uint32_t trans_len;
+ trans_len = MIN(sizeof(*n->elpes) * n->elpe, buf_len);
+ n->aer_mask &= ~(1 << NVME_AER_TYPE_ERROR);
+ if (!QSIMPLEQ_EMPTY(&n->aer_queue)) {
+ qemu_mod_timer(n->aer_timer, qemu_get_clock_ns(vm_clock) + 10000);
+ }
+ return nvme_dma_prp(cmd->prp1, cmd->prp2, trans_len, n, (uint8_t
*)n->elpes,
+ DMA_DIRECTION_FROM_DEVICE);
+}
+
+static uint16_t nvme_async_req(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
+{
+ if (n->outstanding_aers > n->aerl + 1) {
+ return NVME_AER_LIMIT_EXCEEDED;
+ }
+ n->aer_reqs[n->outstanding_aers] = req;
+ qemu_mod_timer(n->aer_timer, qemu_get_clock_ns(vm_clock) + 10000);
+ n->outstanding_aers++;
+ return NVME_NO_COMPLETE;
+}
+
+static uint16_t nvme_smart_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t buf_len)
+{
+ uint32_t trans_len;
+ time_t current_seconds;
+ NvmeSmartLog smart_log;
+
+ trans_len = MIN(sizeof(smart_log), buf_len);
+ memset(&smart_log, 0x0, sizeof(smart_log));
+ if (cmd->nsid == 0xffffffff || !(n->id_ctrl.lpa & 0x1)) {
+ int i;
+ uint64_t dur[2] = {0, 0};
+ uint64_t duw[2] = {0, 0};
+ uint64_t hrc[2] = {0, 0};
+ uint64_t hwc[2] = {0, 0};
+ uint64_t total_use = 0;
+ uint64_t total_size = 0;
+ for (i = 0; i < n->num_namespaces; ++i) {
+ uint64_t tmp;
+ NvmeNamespace *ns = &n->namespaces[i];
+ if (ns == NULL) {
+ continue;
+ }
+
+ tmp = dur[0];
+ dur[0] += ns->data_units_read[0];
+ dur[1] += ns->data_units_read[1];
+ if (tmp > dur[0]) {
+ ++dur[1];
+ }
+
+ tmp = duw[0];
+ duw[0] += ns->data_units_written[0];
+ duw[1] += ns->data_units_written[1];
+ if (tmp > duw[0]) {
+ ++duw[1];
+ }
+
+ tmp = hrc[0];
+ hrc[0] += ns->host_read_commands[0];
+ hrc[1] += ns->host_read_commands[1];
+ if (tmp > hrc[0]) {
+ ++hrc[1];
+ }
+
+ tmp = hwc[0];
+ hwc[0] += ns->host_write_commands[0];
+ hwc[1] += ns->host_write_commands[1];
+ if (tmp > hwc[0]) {
+ ++hwc[1];
+ }
+
+ total_size += ns->id_ns.nsze;
+ total_use += ns->id_ns.nuse;
+ }
+
+ smart_log.data_units_read[0] = dur[0];
+ smart_log.data_units_read[1] = dur[1];
+ smart_log.data_units_written[0] = duw[0];
+ smart_log.data_units_written[1] = duw[1];
+ smart_log.host_read_commands[0] = hrc[0];
+ smart_log.host_read_commands[1] = hrc[1];
+ smart_log.host_write_commands[0] = hwc[0];
+ smart_log.host_write_commands[1] = hwc[1];
+ smart_log.available_spare = 100 - (uint32_t)((((double)total_use) /
+ total_size) * 100);
+ } else if (cmd->nsid > 0 && cmd->nsid <= n->num_namespaces &&
+ (n->id_ctrl.lpa & 0x1)) {
+ NvmeNamespace *ns = &n->namespaces[cmd->nsid - 1];
+ smart_log.data_units_read[0] = ns->data_units_read[0];
+ smart_log.data_units_read[1] = ns->data_units_read[1];
+ smart_log.data_units_written[0] = ns->data_units_written[0];
+ smart_log.data_units_written[1] = ns->data_units_written[1];
+ smart_log.host_read_commands[0] = ns->host_read_commands[0];
+ smart_log.host_read_commands[1] = ns->host_read_commands[1];
+ smart_log.host_write_commands[0] = ns->host_write_commands[0];
+ smart_log.host_write_commands[1] = ns->host_write_commands[1];
+ smart_log.available_spare = 100 - (uint32_t)
+ ((((double)ns->id_ns.nuse) / ns->id_ns.nsze) * 100);
+ } else {
+ return NVME_INVALID_NSID | NVME_DNR;
+ }
+
+ smart_log.temperature[0] = n->temperature & 0xff;
+ smart_log.temperature[1] = (n->temperature >> 8) & 0xff;
+ smart_log.percentage_used = 0;
+
+ current_seconds = time(NULL);
+ smart_log.power_on_hours[0] = ((current_seconds - n->start_time) / 60) /
60;
+
+ smart_log.available_spare_threshold = NVME_SPARE_THRESHOLD;
+ if (smart_log.available_spare <= NVME_SPARE_THRESHOLD) {
+ smart_log.critical_warning |= NVME_SMART_SPARE;
+ }
+ if (n->features.temp_thresh <= n->temperature) {
+ smart_log.critical_warning |= NVME_SMART_TEMPERATURE;
+ }
+
+ n->aer_mask &= ~(1 << NVME_AER_TYPE_SMART);
+ if (!QSIMPLEQ_EMPTY(&n->aer_queue)) {
+ qemu_mod_timer(n->aer_timer, qemu_get_clock_ns(vm_clock) + 10000);
+ }
+ return nvme_dma_prp(cmd->prp1, cmd->prp2, trans_len, n, (uint8_t
*)&smart_log,
+ DMA_DIRECTION_FROM_DEVICE);
+}
+
+static uint16_t nvme_get_log(NvmeCtrl *n, NvmeCmd *cmd)
+{
+ uint16_t lid = cmd->cdw10 & 0xffff;
+ uint32_t len = ((cmd->cdw10 >> 16) & 0xff) << 2;
+ switch (lid) {
+ case NVME_LOG_ERROR_INFO:
+ return nvme_error_log_info(n, cmd, len);
+ case NVME_LOG_SMART_INFO:
+ return nvme_smart_info(n, cmd, len);
+ case NVME_LOG_FW_SLOT_INFO:
+ return nvme_fw_log_info(n, cmd, len);
+ default:
+ return NVME_INVALID_LOG_ID | NVME_DNR;
+ }
+}
+
+static uint16_t nvme_abort_req(NvmeCtrl *n, NvmeCmd *cmd, uint32_t *result)
+{
+ uint32_t index = 0;
+ uint16_t sqid = cmd->cdw10 & 0xffff;
+ uint16_t cid = (cmd->cdw10 >> 16) & 0xffff;
+ NvmeSQueue *sq;
+
+ *result = 1;
+ if (nvme_check_sqid(n, sqid)) {
+ return NVME_SUCCESS;
+ }
+
+ sq = n->sq[sqid];
+ while ((sq->head + index) % sq->size != sq->tail) {
+ NvmeCmd abort_cmd;
+ hwaddr addr = sq->dma_addr + ((sq->head + index) % sq->size) *
+ n->sqe_size;
+ pci_dma_read(&n->dev, addr, (void *)&abort_cmd, sizeof(abort_cmd));
+ if (abort_cmd.cid == cid) {
+ NvmeRequest *req = QTAILQ_FIRST(&sq->req_list);
+ QTAILQ_REMOVE(&sq->req_list, req, entry);
+ QTAILQ_INSERT_TAIL(&sq->out_req_list, req, entry);
+ memset(&req->cqe, 0, sizeof(req->cqe));
+ req->cqe.cid = cid;
+ req->cqe.status = NVME_CMD_ABORT_REQ << 1;
+ abort_cmd.opcode = NVME_OP_ABORTED;
+ pci_dma_write(&n->dev, addr, (void *)&abort_cmd,
sizeof(abort_cmd));
+ nvme_enqueue_req_completion(n->cq[sq->cqid], req);
+ *result = 0;
+ break;
+ }
+ ++index;
+ }
+ return NVME_SUCCESS;
+}
+
+static uint16_t nvme_format_namespace(NvmeNamespace *ns, uint8_t lba_idx,
+ uint8_t meta_loc, uint8_t pil, uint8_t pi, uint8_t sec_erase)
+{
+ uint64_t old_size;
+ uint8_t lbaf = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
+
+ if (lba_idx > ns->id_ns.nlbaf) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+ if (pi) {
+ if (pil && !NVME_ID_NS_DPC_LAST_EIGHT(ns->id_ns.dpc)) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+ if (!pil && !NVME_ID_NS_DPC_FIRST_EIGHT(ns->id_ns.dpc)) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+ if (!((ns->id_ns.dpc & 0x7) & (1 << (pi - 1)))) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+ }
+ if (meta_loc && ns->id_ns.lbaf[lba_idx].ms &&
+ !NVME_ID_NS_MC_EXTENDED(ns->id_ns.mc)) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+ if (!meta_loc && ns->id_ns.lbaf[lba_idx].ms &&
+ !NVME_ID_NS_MC_SEPARATE(ns->id_ns.mc)) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+
+ g_free(ns->util);
+ old_size = ns->id_ns.nsze * (1 << ns->id_ns.lbaf[lbaf].ds);
+ ns->id_ns.nuse = 0;
+ ns->id_ns.flbas = lba_idx | meta_loc;
+ ns->id_ns.nsze = old_size >> ns->id_ns.lbaf[lba_idx].ds;
+ ns->id_ns.ncap = ns->id_ns.nsze;
+ ns->id_ns.dps = pil | pi;
+ ns->util = bitmap_new(ns->id_ns.nsze);
+
+ if (sec_erase) {
+ /* TODO: write zeros, complete asynchronously */
+ ;
+ }
+
+ return NVME_SUCCESS;
+}
+
+static uint16_t nvme_format(NvmeCtrl *n, NvmeCmd *cmd)
+{
+ NvmeNamespace *ns;
+ uint32_t dw10 = cmd->cdw10;
+ uint32_t nsid = cmd->nsid;
+ uint8_t lba_idx = dw10 & 0xf;
+ uint8_t meta_loc = dw10 & 0x10;
+ uint8_t pil = (dw10 >> 5) & 0x8;
+ uint8_t pi = (dw10 >> 5) & 0x7;
+ uint8_t sec_erase = (dw10 >> 8) & 0x7;
+
+ if (nsid == 0xffffffff) {
+ uint32_t i;
+ uint16_t ret;
+
+ for (i = 0; i < n->num_namespaces; ++i) {
+ ns = &n->namespaces[i];
+ ret = nvme_format_namespace(ns, lba_idx, meta_loc, pil, pi,
+ sec_erase);
+ if (ret != NVME_SUCCESS) {
+ return ret;
+ }
+ }
+ return ret;
+ }
+
+ if (nsid == 0 || nsid > n->num_namespaces) {
+ return NVME_INVALID_NSID | NVME_DNR;
+ }
+
+ ns = &n->namespaces[cmd->nsid - 1];
+ return nvme_format_namespace(ns, lba_idx, meta_loc, pil, pi,
+ sec_erase);
+}
+
static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
{
switch (cmd->opcode) {
@@ -616,6 +985,18 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd,
NvmeRequest *req)
return nvme_set_feature(n, cmd, req);
case NVME_ADM_CMD_GET_FEATURES:
return nvme_get_feature(n, cmd, req);
+ case NVME_ADM_CMD_GET_LOG_PAGE:
+ return nvme_get_log(n, cmd);
+ case NVME_ADM_CMD_ASYNC_EV_REQ:
+ return nvme_async_req(n, cmd, req);
+ case NVME_ADM_CMD_FORMAT_NVM:
+ return nvme_format(n, cmd);
+ case NVME_ADM_CMD_ABORT:
+ return nvme_abort_req(n, cmd, &req->cqe.result);
+ case NVME_ADM_CMD_ACTIVATE_FW:
+ case NVME_ADM_CMD_DOWNLOAD_FW:
+ case NVME_ADM_CMD_SECURITY_SEND:
+ case NVME_ADM_CMD_SECURITY_RECV:
default:
return NVME_INVALID_OPCODE | NVME_DNR;
}
@@ -661,6 +1042,8 @@ static void nvme_sq_process(void *opaque)
static void nvme_clear_ctrl(NvmeCtrl *n)
{
int i;
+ AsyncEvent *event;
+
for (i = 0; i < n->num_queues; i++) {
if (n->sq[i] != NULL) {
nvme_free_sq(n->sq[i], n);
@@ -671,6 +1054,15 @@ static void nvme_clear_ctrl(NvmeCtrl *n)
nvme_free_cq(n->cq[i], n);
}
}
+ if (n->aer_timer) {
+ qemu_del_timer(n->aer_timer);
+ qemu_free_timer(n->aer_timer);
+ n->aer_timer = NULL;
+ }
+ while ((event = QSIMPLEQ_FIRST(&n->aer_queue)) != NULL) {
+ QSIMPLEQ_REMOVE_HEAD(&n->aer_queue, entry);
+ g_free(event);
+ }
n->bar.cc = 0;
}
@@ -718,6 +1110,9 @@ static int nvme_start_ctrl(NvmeCtrl *n)
nvme_init_sq(&n->admin_sq, n, n->bar.asq, 0, 0,
NVME_AQA_ASQS(n->bar.aqa) + 1, NVME_Q_PRIO_HIGH);
+ n->aer_timer = qemu_new_timer_ns(vm_clock, nvme_aer_process_cb, n);
+ QSIMPLEQ_INIT(&n->aer_queue);
+
return 0;
}
@@ -777,7 +1172,6 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr,
unsigned size)
NvmeCtrl *n = (NvmeCtrl *)opaque;
uint8_t *ptr = (uint8_t *)&n->bar;
uint64_t val = 0;
-
if (addr < sizeof(n->bar)) {
memcpy(&val, ptr + addr, size);
}
@@ -788,6 +1182,8 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int
val)
{
uint32_t qid;
if (addr & ((1 << (2 + n->db_stride)) - 1)) {
+ nvme_enqueue_event(n, NVME_AER_TYPE_ERROR,
+ NVME_AER_INFO_ERR_INVALID_DB, NVME_LOG_ERROR_INFO);
return;
}
@@ -799,11 +1195,15 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr,
int val)
qid = (addr - (0x1000 + (1 << (2 + n->db_stride)))) >>
(3 + n->db_stride);
if (nvme_check_cqid(n, qid)) {
+ nvme_enqueue_event(n, NVME_AER_TYPE_ERROR,
+ NVME_AER_INFO_ERR_INVALID_DB, NVME_LOG_ERROR_INFO);
return;
}
cq = n->cq[qid];
if (new_head >= cq->size) {
+ nvme_enqueue_event(n, NVME_AER_TYPE_ERROR,
+ NVME_AER_INFO_ERR_INVALID_DB, NVME_LOG_ERROR_INFO);
return;
}
@@ -825,11 +1225,15 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr,
int val)
qid = (addr - 0x1000) >> (3 + n->db_stride);
if (nvme_check_sqid(n, qid)) {
+ nvme_enqueue_event(n, NVME_AER_TYPE_ERROR,
+ NVME_AER_INFO_ERR_INVALID_SQ, NVME_LOG_ERROR_INFO);
return;
}
sq = n->sq[qid];
if (new_tail >= sq->size) {
+ nvme_enqueue_event(n, NVME_AER_TYPE_ERROR,
+ NVME_AER_INFO_ERR_INVALID_DB, NVME_LOG_ERROR_INFO);
return;
}
sq->tail = new_tail;
@@ -905,6 +1309,7 @@ static int nvme_init(PCIDevice *pci_dev)
pci_config_set_prog_interface(pci_dev->config, 0x2);
pci_config_set_class(pci_dev->config, PCI_CLASS_STORAGE_EXPRESS);
+ n->start_time = time(NULL);
n->reg_size = 1 << qemu_fls(0x1004 + 2 * (n->num_queues + 1) * 4);
n->ns_size = bs_size / n->num_namespaces;
n->instance = instance++;
@@ -913,6 +1318,9 @@ static int nvme_init(PCIDevice *pci_dev)
n->cq = g_malloc0(sizeof(*n->cq)*n->num_queues);
n->features.int_vector_config = g_malloc(n->num_queues *
sizeof(*n->features.int_vector_config));
+ n->aer_reqs = g_malloc0((n->aerl + 1) * sizeof(*n->aer_reqs));
+ n->elpes = g_malloc0((n->elpe + 1) * sizeof(*n->elpes));
+ n->temperature = NVME_TEMPERATURE;
memory_region_init_io(&n->iomem, &nvme_mmio_ops, n, "nvme-mmio",
n->reg_size);
@@ -928,8 +1336,11 @@ static int nvme_init(PCIDevice *pci_dev)
id->ieee[1] = 0x02;
id->ieee[2] = 0xb3;
id->mdts = n->mdts;
+ id->oacs = NVME_OACS_FORMAT;
id->acl = n->acl;
id->aerl = n->aerl;
+ id->frmw = 7 << 1;
+ id->lpa = 1 << 0;
id->elpe = n->elpe;
id->sqes = n->max_sqes << 4 | 0x6;
id->cqes = n->max_cqes << 4 | 0x4;
@@ -983,6 +1394,7 @@ static int nvme_init(PCIDevice *pci_dev)
ns->id = i + 1;
ns->ctrl = n;
ns->start_block = id_ns->nsze * i;
+ ns->util = bitmap_new(id_ns->nsze);
}
return 0;
@@ -990,12 +1402,18 @@ static int nvme_init(PCIDevice *pci_dev)
static void nvme_exit(PCIDevice *pci_dev)
{
+ int i;
NvmeCtrl *n = DO_UPCAST(NvmeCtrl, dev, pci_dev);
nvme_clear_ctrl(n);
+ for (i = 0; i < n->num_namespaces; i++) {
+ g_free(n->namespaces[i].util);
+ }
g_free(n->namespaces);
g_free(n->cq);
g_free(n->sq);
g_free(n->features.int_vector_config);
+ g_free(n->aer_reqs);
+ g_free(n->elpes);
msix_uninit_exclusive_bar(pci_dev);
memory_region_destroy(&n->iomem);
}
diff --git a/hw/nvme.h b/hw/nvme.h
index 6296f04..964e91d 100644
--- a/hw/nvme.h
+++ b/hw/nvme.h
@@ -589,7 +589,11 @@ static inline void _nvme_check_size(void)
typedef struct NvmeRequest {
struct NvmeSQueue *sq;
+ struct NvmeNamespace *ns;
BlockDriverAIOCB *aiocb;
+ uint64_t slba;
+ uint16_t rw;
+ uint16_t nlb;
NvmeCqe cqe;
QEMUSGList qsg;
QTAILQ_ENTRY(NvmeRequest)entry;
@@ -634,6 +638,14 @@ typedef struct NvmeNamespace {
NvmeIdNs id_ns;
NvmeRangeType lba_range[64];
uint32_t id;
+ uint32_t write_data_counter;
+ uint32_t read_data_counter;
+ uint64_t data_units_read[2];
+ uint64_t data_units_written[2];
+ uint64_t host_read_commands[2];
+ uint64_t host_write_commands[2];
+ unsigned long *util;
+ unsigned long *uncorrectable;
uint64_t start_block;
} NvmeNamespace;
@@ -643,7 +655,9 @@ typedef struct NvmeCtrl {
NvmeBar bar;
BlockConf conf;
+ time_t start_time;
int instance;
+ uint16_t temperature;
uint16_t page_size;
uint16_t page_bits;
uint16_t max_prp_ents;
@@ -665,7 +679,10 @@ typedef struct NvmeCtrl {
uint8_t meta;
uint8_t vwc;
uint8_t lba_index;
+ uint8_t outstanding_aers;
+ NvmeErrorLog *elpes;
+ NvmeRequest **aer_reqs;
NvmeNamespace *namespaces;
NvmeSQueue **sq;
NvmeCQueue **cq;
@@ -673,6 +690,10 @@ typedef struct NvmeCtrl {
NvmeSQueue admin_sq;
NvmeCQueue admin_cq;
NvmeIdCtrl id_ctrl;
+
+ QSIMPLEQ_HEAD(aer_queue, AsyncEvent) aer_queue;
+ QEMUTimer *aer_timer;
+ uint8_t aer_mask;
} NvmeCtrl;
#endif
--
1.7.0.4
- [Qemu-devel] [PATCH 00/11] *** SUBJECT HERE ***, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 08/11] QEMU NVMe: Enqueue critial temperature event, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 03/11] QEMU NVMe: Implement NVMe features, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 02/11] QEMU NVMe: Add command line options, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 06/11] QEMU NVMe: Implement flush and dsm, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 07/11] QEMU NVMe: Set error pages with error data, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 05/11] QEMU NVMe: Add DSM command support, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 09/11] QEMU NVMe: Implement discontiguous queues, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 04/11] QEMU NVMe: Implement additional admin commands,
Keith Busch <=
- [Qemu-devel] [PATCH 10/11] QEMU NVMe: Add logging, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 11/11] QEMU NVMe: Support NVMe DIF and Meta-data, Keith Busch, 2013/02/26
- [Qemu-devel] [PATCH 01/11] NVMe: Initial commit for NVM Express device, Keith Busch, 2013/02/26