|
@@ -219,6 +219,30 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
|
|
|
return NVME_INVALID_FIELD | NVME_DNR;
|
|
|
}
|
|
|
|
|
|
+static uint16_t nvme_dma_write_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
|
|
|
+ uint64_t prp1, uint64_t prp2)
|
|
|
+{
|
|
|
+ QEMUSGList qsg;
|
|
|
+ QEMUIOVector iov;
|
|
|
+ uint16_t status = NVME_SUCCESS;
|
|
|
+
|
|
|
+ if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
|
|
|
+ return NVME_INVALID_FIELD | NVME_DNR;
|
|
|
+ }
|
|
|
+ if (qsg.nsg > 0) {
|
|
|
+ if (dma_buf_write(ptr, len, &qsg)) {
|
|
|
+ status = NVME_INVALID_FIELD | NVME_DNR;
|
|
|
+ }
|
|
|
+ qemu_sglist_destroy(&qsg);
|
|
|
+ } else {
|
|
|
+ if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
|
|
|
+ status = NVME_INVALID_FIELD | NVME_DNR;
|
|
|
+ }
|
|
|
+ qemu_iovec_destroy(&iov);
|
|
|
+ }
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
|
|
|
uint64_t prp1, uint64_t prp2)
|
|
|
{
|
|
@@ -678,7 +702,6 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
|
|
|
{
|
|
|
NvmeIdentify *c = (NvmeIdentify *)cmd;
|
|
@@ -696,6 +719,57 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static inline void nvme_set_timestamp(NvmeCtrl *n, uint64_t ts)
|
|
|
+{
|
|
|
+ trace_nvme_setfeat_timestamp(ts);
|
|
|
+
|
|
|
+ n->host_timestamp = le64_to_cpu(ts);
|
|
|
+ n->timestamp_set_qemu_clock_ms = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
|
|
|
+}
|
|
|
+
|
|
|
+static inline uint64_t nvme_get_timestamp(const NvmeCtrl *n)
|
|
|
+{
|
|
|
+ uint64_t current_time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
|
|
|
+ uint64_t elapsed_time = current_time - n->timestamp_set_qemu_clock_ms;
|
|
|
+
|
|
|
+ union nvme_timestamp {
|
|
|
+ struct {
|
|
|
+ uint64_t timestamp:48;
|
|
|
+ uint64_t sync:1;
|
|
|
+ uint64_t origin:3;
|
|
|
+ uint64_t rsvd1:12;
|
|
|
+ };
|
|
|
+ uint64_t all;
|
|
|
+ };
|
|
|
+
|
|
|
+ union nvme_timestamp ts;
|
|
|
+ ts.all = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the sum of the Timestamp value set by the host and the elapsed
|
|
|
+ * time exceeds 2^48, the value returned should be reduced modulo 2^48.
|
|
|
+ */
|
|
|
+ ts.timestamp = (n->host_timestamp + elapsed_time) & 0xffffffffffff;
|
|
|
+
|
|
|
+ /* If the host timestamp is non-zero, set the timestamp origin */
|
|
|
+ ts.origin = n->host_timestamp ? 0x01 : 0x00;
|
|
|
+
|
|
|
+ trace_nvme_getfeat_timestamp(ts.all);
|
|
|
+
|
|
|
+ return cpu_to_le64(ts.all);
|
|
|
+}
|
|
|
+
|
|
|
+static uint16_t nvme_get_feature_timestamp(NvmeCtrl *n, NvmeCmd *cmd)
|
|
|
+{
|
|
|
+ uint64_t prp1 = le64_to_cpu(cmd->prp1);
|
|
|
+ uint64_t prp2 = le64_to_cpu(cmd->prp2);
|
|
|
+
|
|
|
+ uint64_t timestamp = nvme_get_timestamp(n);
|
|
|
+
|
|
|
+ return nvme_dma_read_prp(n, (uint8_t *)×tamp,
|
|
|
+ sizeof(timestamp), prp1, prp2);
|
|
|
+}
|
|
|
+
|
|
|
static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
|
|
{
|
|
|
uint32_t dw10 = le32_to_cpu(cmd->cdw10);
|
|
@@ -710,6 +784,9 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
|
|
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
|
|
|
trace_nvme_getfeat_numq(result);
|
|
|
break;
|
|
|
+ case NVME_TIMESTAMP:
|
|
|
+ return nvme_get_feature_timestamp(n, cmd);
|
|
|
+ break;
|
|
|
default:
|
|
|
trace_nvme_err_invalid_getfeat(dw10);
|
|
|
return NVME_INVALID_FIELD | NVME_DNR;
|
|
@@ -719,6 +796,24 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
|
|
return NVME_SUCCESS;
|
|
|
}
|
|
|
|
|
|
+static uint16_t nvme_set_feature_timestamp(NvmeCtrl *n, NvmeCmd *cmd)
|
|
|
+{
|
|
|
+ uint16_t ret;
|
|
|
+ uint64_t timestamp;
|
|
|
+ uint64_t prp1 = le64_to_cpu(cmd->prp1);
|
|
|
+ uint64_t prp2 = le64_to_cpu(cmd->prp2);
|
|
|
+
|
|
|
+ ret = nvme_dma_write_prp(n, (uint8_t *)×tamp,
|
|
|
+ sizeof(timestamp), prp1, prp2);
|
|
|
+ if (ret != NVME_SUCCESS) {
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ nvme_set_timestamp(n, timestamp);
|
|
|
+
|
|
|
+ return NVME_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
|
|
{
|
|
|
uint32_t dw10 = le32_to_cpu(cmd->cdw10);
|
|
@@ -735,6 +830,11 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
|
|
req->cqe.result =
|
|
|
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
|
|
|
break;
|
|
|
+
|
|
|
+ case NVME_TIMESTAMP:
|
|
|
+ return nvme_set_feature_timestamp(n, cmd);
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
trace_nvme_err_invalid_setfeat(dw10);
|
|
|
return NVME_INVALID_FIELD | NVME_DNR;
|
|
@@ -907,6 +1007,8 @@ 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_set_timestamp(n, 0ULL);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1270,7 +1372,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
|
|
|
id->sqes = (0x6 << 4) | 0x6;
|
|
|
id->cqes = (0x4 << 4) | 0x4;
|
|
|
id->nn = cpu_to_le32(n->num_namespaces);
|
|
|
- id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROS);
|
|
|
+ id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROS | NVME_ONCS_TIMESTAMP);
|
|
|
id->psd[0].mp = cpu_to_le16(0x9c4);
|
|
|
id->psd[0].enlat = cpu_to_le32(0x10);
|
|
|
id->psd[0].exlat = cpu_to_le32(0x4);
|