Re: [PATCH 06/17] hw/block/nvme: add support for the get log page command
On Jul 3 00:45, Dmitry Fomichev wrote: > On Mon, 2020-06-29 at 20:26 +0200, Klaus Jensen wrote: > > From: Klaus Jensen > > > > Add support for the Get Log Page command and basic implementations of > > the mandatory Error Information, SMART / Health Information and Firmware > > Slot Information log pages. > > > > In violation of the specification, the SMART / Health Information log > > page does not persist information over the lifetime of the controller > > because the device has no place to store such persistent state. > > > > Note that the LPA field in the Identify Controller data structure > > intentionally has bit 0 cleared because there is no namespace specific > > information in the SMART / Health information log page. > > > > Required for compliance with NVMe revision 1.3d. See NVM Express 1.3d, > > Section 5.14 ("Get Log Page command"). > > > > Signed-off-by: Klaus Jensen > > Signed-off-by: Klaus Jensen > > Acked-by: Keith Busch > > --- > > hw/block/nvme.c | 141 +- > > hw/block/nvme.h | 2 + > > hw/block/trace-events | 2 + > > include/block/nvme.h | 4 ++ > > 4 files changed, 148 insertions(+), 1 deletion(-) > > > > diff --git a/hw/block/nvme.c b/hw/block/nvme.c > > index f8e91a6965ed..fe5d052ab159 100644 > > --- a/hw/block/nvme.c > > +++ b/hw/block/nvme.c > > @@ -592,6 +592,141 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd > > *cmd) > > return NVME_SUCCESS; > > } > > > > +static uint16_t nvme_smart_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t > > buf_len, > > +uint64_t off, NvmeRequest *req) > > +{ > > +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); > > +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); > > +uint32_t nsid = le32_to_cpu(cmd->nsid); > > + > > +uint32_t trans_len; > > +time_t current_ms; > > +uint64_t units_read = 0, units_written = 0; > > +uint64_t read_commands = 0, write_commands = 0; > > +NvmeSmartLog smart; > > +BlockAcctStats *s; > > + > > +if (nsid && nsid != 0x) { > > +return NVME_INVALID_FIELD | NVME_DNR; > > +} > > + > > +s = blk_get_stats(n->conf.blk); > > + > > +units_read = s->nr_bytes[BLOCK_ACCT_READ] >> BDRV_SECTOR_BITS; > > +units_written = s->nr_bytes[BLOCK_ACCT_WRITE] >> BDRV_SECTOR_BITS; > > +read_commands = s->nr_ops[BLOCK_ACCT_READ]; > > +write_commands = s->nr_ops[BLOCK_ACCT_WRITE]; > > + > > +if (off > sizeof(smart)) { > > +return NVME_INVALID_FIELD | NVME_DNR; > > +} > > + > > +trans_len = MIN(sizeof(smart) - off, buf_len); > > + > > +memset(&smart, 0x0, sizeof(smart)); > > + > > +smart.data_units_read[0] = cpu_to_le64(units_read / 1000); > > +smart.data_units_written[0] = cpu_to_le64(units_written / 1000); > > +smart.host_read_commands[0] = cpu_to_le64(read_commands); > > +smart.host_write_commands[0] = cpu_to_le64(write_commands); > > + > > +smart.temperature[0] = n->temperature & 0xff; > > +smart.temperature[1] = (n->temperature >> 8) & 0xff; > > Why not change temperature[2] in NvmeSmartLog to uint16_t and use > cpu_to_le16() here? > It's because of the wierd alignment. But you are right and I changed it to uint16_t and added the QEMU_PACKED attribute to the struct. It should be there anyway. > > +if ((n->temperature >= n->features.temp_thresh_hi) || > > +(n->temperature <= n->features.temp_thresh_low)) { > > +smart.critical_warning |= NVME_SMART_TEMPERATURE; > > +} > > + > > +current_ms = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); > > +smart.power_on_hours[0] = > > +cpu_to_le64current_ms - n->starttime_ms) / 1000) / 60) / 60); > > + > > +return nvme_dma_read_prp(n, (uint8_t *) &smart + off, trans_len, prp1, > > + prp2); > > +} > > + > > +static uint16_t nvme_fw_log_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t > > buf_len, > > + uint64_t off, NvmeRequest *req) > > +{ > > +uint32_t trans_len; > > +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); > > +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); > > +NvmeFwSlotInfoLog fw_log = { > > +.afi = 0x1, > > +}; > > + > > +strpadcpy((char *)&fw_log.frs1, sizeof(fw_log.frs1), "1.0", ' '); > > + > > +if (off > sizeof(fw_log)) { > > +return NVME_INVALID_FIELD | NVME_DNR; > > +} > > + > > +trans_len = MIN(sizeof(fw_log) - off, buf_len); > > + > > +return nvme_dma_read_prp(n, (uint8_t *) &fw_log + off, trans_len, prp1, > > + prp2); > > +} > > + > > +static uint16_t nvme_error_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t > > buf_len, > > +uint64_t off, NvmeRequest *req) > > +{ > > +uint32_t trans_len; > > +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); > > +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); > > +NvmeErrorLog errlog; > > + > > +if (off > sizeof(err
Re: [PATCH 06/17] hw/block/nvme: add support for the get log page command
On Mon, 2020-06-29 at 20:26 +0200, Klaus Jensen wrote: > From: Klaus Jensen > > Add support for the Get Log Page command and basic implementations of > the mandatory Error Information, SMART / Health Information and Firmware > Slot Information log pages. > > In violation of the specification, the SMART / Health Information log > page does not persist information over the lifetime of the controller > because the device has no place to store such persistent state. > > Note that the LPA field in the Identify Controller data structure > intentionally has bit 0 cleared because there is no namespace specific > information in the SMART / Health information log page. > > Required for compliance with NVMe revision 1.3d. See NVM Express 1.3d, > Section 5.14 ("Get Log Page command"). > > Signed-off-by: Klaus Jensen > Signed-off-by: Klaus Jensen > Acked-by: Keith Busch > --- > hw/block/nvme.c | 141 +- > hw/block/nvme.h | 2 + > hw/block/trace-events | 2 + > include/block/nvme.h | 4 ++ > 4 files changed, 148 insertions(+), 1 deletion(-) > > diff --git a/hw/block/nvme.c b/hw/block/nvme.c > index f8e91a6965ed..fe5d052ab159 100644 > --- a/hw/block/nvme.c > +++ b/hw/block/nvme.c > @@ -592,6 +592,141 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd > *cmd) > return NVME_SUCCESS; > } > > +static uint16_t nvme_smart_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t buf_len, > +uint64_t off, NvmeRequest *req) > +{ > +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); > +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); > +uint32_t nsid = le32_to_cpu(cmd->nsid); > + > +uint32_t trans_len; > +time_t current_ms; > +uint64_t units_read = 0, units_written = 0; > +uint64_t read_commands = 0, write_commands = 0; > +NvmeSmartLog smart; > +BlockAcctStats *s; > + > +if (nsid && nsid != 0x) { > +return NVME_INVALID_FIELD | NVME_DNR; > +} > + > +s = blk_get_stats(n->conf.blk); > + > +units_read = s->nr_bytes[BLOCK_ACCT_READ] >> BDRV_SECTOR_BITS; > +units_written = s->nr_bytes[BLOCK_ACCT_WRITE] >> BDRV_SECTOR_BITS; > +read_commands = s->nr_ops[BLOCK_ACCT_READ]; > +write_commands = s->nr_ops[BLOCK_ACCT_WRITE]; > + > +if (off > sizeof(smart)) { > +return NVME_INVALID_FIELD | NVME_DNR; > +} > + > +trans_len = MIN(sizeof(smart) - off, buf_len); > + > +memset(&smart, 0x0, sizeof(smart)); > + > +smart.data_units_read[0] = cpu_to_le64(units_read / 1000); > +smart.data_units_written[0] = cpu_to_le64(units_written / 1000); > +smart.host_read_commands[0] = cpu_to_le64(read_commands); > +smart.host_write_commands[0] = cpu_to_le64(write_commands); > + > +smart.temperature[0] = n->temperature & 0xff; > +smart.temperature[1] = (n->temperature >> 8) & 0xff; Why not change temperature[2] in NvmeSmartLog to uint16_t and use cpu_to_le16() here? > +if ((n->temperature >= n->features.temp_thresh_hi) || > +(n->temperature <= n->features.temp_thresh_low)) { > +smart.critical_warning |= NVME_SMART_TEMPERATURE; > +} > + > +current_ms = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); > +smart.power_on_hours[0] = > +cpu_to_le64current_ms - n->starttime_ms) / 1000) / 60) / 60); > + > +return nvme_dma_read_prp(n, (uint8_t *) &smart + off, trans_len, prp1, > + prp2); > +} > + > +static uint16_t nvme_fw_log_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t buf_len, > + uint64_t off, NvmeRequest *req) > +{ > +uint32_t trans_len; > +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); > +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); > +NvmeFwSlotInfoLog fw_log = { > +.afi = 0x1, > +}; > + > +strpadcpy((char *)&fw_log.frs1, sizeof(fw_log.frs1), "1.0", ' '); > + > +if (off > sizeof(fw_log)) { > +return NVME_INVALID_FIELD | NVME_DNR; > +} > + > +trans_len = MIN(sizeof(fw_log) - off, buf_len); > + > +return nvme_dma_read_prp(n, (uint8_t *) &fw_log + off, trans_len, prp1, > + prp2); > +} > + > +static uint16_t nvme_error_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t buf_len, > +uint64_t off, NvmeRequest *req) > +{ > +uint32_t trans_len; > +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); > +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); > +NvmeErrorLog errlog; > + > +if (off > sizeof(errlog)) { > +return NVME_INVALID_FIELD | NVME_DNR; > +} > + > +memset(&errlog, 0x0, sizeof(errlog)); > + > +trans_len = MIN(sizeof(errlog) - off, buf_len); > + > +return nvme_dma_read_prp(n, (uint8_t *)&errlog, trans_len, prp1, prp2); > +} > + > +static uint16_t nvme_get_log(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) > +{ > +uint32_t dw10 = le32_to_cpu(cmd->cdw10); > +uint32_t dw11 = le32_to_cpu(cmd->cdw11); > +uint32_t dw12 = le
[PATCH 06/17] hw/block/nvme: add support for the get log page command
From: Klaus Jensen Add support for the Get Log Page command and basic implementations of the mandatory Error Information, SMART / Health Information and Firmware Slot Information log pages. In violation of the specification, the SMART / Health Information log page does not persist information over the lifetime of the controller because the device has no place to store such persistent state. Note that the LPA field in the Identify Controller data structure intentionally has bit 0 cleared because there is no namespace specific information in the SMART / Health information log page. Required for compliance with NVMe revision 1.3d. See NVM Express 1.3d, Section 5.14 ("Get Log Page command"). Signed-off-by: Klaus Jensen Signed-off-by: Klaus Jensen Acked-by: Keith Busch --- hw/block/nvme.c | 141 +- hw/block/nvme.h | 2 + hw/block/trace-events | 2 + include/block/nvme.h | 4 ++ 4 files changed, 148 insertions(+), 1 deletion(-) diff --git a/hw/block/nvme.c b/hw/block/nvme.c index f8e91a6965ed..fe5d052ab159 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -592,6 +592,141 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd) return NVME_SUCCESS; } +static uint16_t nvme_smart_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t buf_len, +uint64_t off, NvmeRequest *req) +{ +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); +uint32_t nsid = le32_to_cpu(cmd->nsid); + +uint32_t trans_len; +time_t current_ms; +uint64_t units_read = 0, units_written = 0; +uint64_t read_commands = 0, write_commands = 0; +NvmeSmartLog smart; +BlockAcctStats *s; + +if (nsid && nsid != 0x) { +return NVME_INVALID_FIELD | NVME_DNR; +} + +s = blk_get_stats(n->conf.blk); + +units_read = s->nr_bytes[BLOCK_ACCT_READ] >> BDRV_SECTOR_BITS; +units_written = s->nr_bytes[BLOCK_ACCT_WRITE] >> BDRV_SECTOR_BITS; +read_commands = s->nr_ops[BLOCK_ACCT_READ]; +write_commands = s->nr_ops[BLOCK_ACCT_WRITE]; + +if (off > sizeof(smart)) { +return NVME_INVALID_FIELD | NVME_DNR; +} + +trans_len = MIN(sizeof(smart) - off, buf_len); + +memset(&smart, 0x0, sizeof(smart)); + +smart.data_units_read[0] = cpu_to_le64(units_read / 1000); +smart.data_units_written[0] = cpu_to_le64(units_written / 1000); +smart.host_read_commands[0] = cpu_to_le64(read_commands); +smart.host_write_commands[0] = cpu_to_le64(write_commands); + +smart.temperature[0] = n->temperature & 0xff; +smart.temperature[1] = (n->temperature >> 8) & 0xff; + +if ((n->temperature >= n->features.temp_thresh_hi) || +(n->temperature <= n->features.temp_thresh_low)) { +smart.critical_warning |= NVME_SMART_TEMPERATURE; +} + +current_ms = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); +smart.power_on_hours[0] = +cpu_to_le64current_ms - n->starttime_ms) / 1000) / 60) / 60); + +return nvme_dma_read_prp(n, (uint8_t *) &smart + off, trans_len, prp1, + prp2); +} + +static uint16_t nvme_fw_log_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t buf_len, + uint64_t off, NvmeRequest *req) +{ +uint32_t trans_len; +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); +NvmeFwSlotInfoLog fw_log = { +.afi = 0x1, +}; + +strpadcpy((char *)&fw_log.frs1, sizeof(fw_log.frs1), "1.0", ' '); + +if (off > sizeof(fw_log)) { +return NVME_INVALID_FIELD | NVME_DNR; +} + +trans_len = MIN(sizeof(fw_log) - off, buf_len); + +return nvme_dma_read_prp(n, (uint8_t *) &fw_log + off, trans_len, prp1, + prp2); +} + +static uint16_t nvme_error_info(NvmeCtrl *n, NvmeCmd *cmd, uint32_t buf_len, +uint64_t off, NvmeRequest *req) +{ +uint32_t trans_len; +uint64_t prp1 = le64_to_cpu(cmd->dptr.prp1); +uint64_t prp2 = le64_to_cpu(cmd->dptr.prp2); +NvmeErrorLog errlog; + +if (off > sizeof(errlog)) { +return NVME_INVALID_FIELD | NVME_DNR; +} + +memset(&errlog, 0x0, sizeof(errlog)); + +trans_len = MIN(sizeof(errlog) - off, buf_len); + +return nvme_dma_read_prp(n, (uint8_t *)&errlog, trans_len, prp1, prp2); +} + +static uint16_t nvme_get_log(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) +{ +uint32_t dw10 = le32_to_cpu(cmd->cdw10); +uint32_t dw11 = le32_to_cpu(cmd->cdw11); +uint32_t dw12 = le32_to_cpu(cmd->cdw12); +uint32_t dw13 = le32_to_cpu(cmd->cdw13); +uint8_t lid = dw10 & 0xff; +uint8_t lsp = (dw10 >> 8) & 0xf; +uint8_t rae = (dw10 >> 15) & 0x1; +uint32_t numdl, numdu; +uint64_t off, lpol, lpou; +size_t len; + +numdl = (dw10 >> 16); +numdu = (dw11 & 0x); +lpol = dw12; +lpou = dw13; + +len = (((numdu << 16) | numdl) + 1) << 2; +off =