diff --git a/src/libnvme.map b/src/libnvme.map index a6870458..faabd721 100644 --- a/src/libnvme.map +++ b/src/libnvme.map @@ -167,6 +167,8 @@ LIBNVME_1_0 { nvme_get_path_attr; nvme_get_property; nvme_get_subsys_attr; + nvme_get_telemetry_log; + nvme_get_telemetry_max; nvme_host_get_dhchap_key; nvme_host_get_hostid; nvme_host_get_hostnqn; diff --git a/src/nvme/linux.c b/src/nvme/linux.c index c6eedc2a..adbc4cdb 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -122,17 +122,44 @@ int nvme_fw_download_seq(int fd, __u32 size, __u32 xfer, __u32 offset, return err; } -static int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, - struct nvme_telemetry_log **buf, enum nvme_telemetry_da da, - size_t *size) +int nvme_get_telemetry_max(int fd, enum nvme_telemetry_da *da, size_t *data_tx) +{ + struct nvme_id_ctrl id_ctrl; + int err = nvme_identify_ctrl(fd, &id_ctrl); + + if (err) + return err; + + if (data_tx) { + *data_tx = id_ctrl.mdts; + if (id_ctrl.mdts) { + /* assuming CAP.MPSMIN is zero minimum Memory Page Size is at least + * 4096 bytes + */ + *data_tx = (1 << id_ctrl.mdts) * 4096; + } + } + if (da) { + if (id_ctrl.lpa & 0x8) + *da = NVME_TELEMETRY_DA_3; + if (id_ctrl.lpa & 0x40) + *da = NVME_TELEMETRY_DA_4; + + } + return err; +} + +int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_data_tx, + enum nvme_telemetry_da da, struct nvme_telemetry_log **buf, + size_t *size) { static const __u32 xfer = NVME_LOG_TELEM_BLOCK_SIZE; struct nvme_telemetry_log *telem; enum nvme_cmd_get_log_lid lid; - struct nvme_id_ctrl id_ctrl; void *log, *tmp; int err; + size_t dalb; struct nvme_get_log_args args = { .args_size = sizeof(args), .fd = fd, @@ -178,35 +205,31 @@ static int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, switch (da) { case NVME_TELEMETRY_DA_1: + dalb = le16_to_cpu(telem->dalb1); + break; case NVME_TELEMETRY_DA_2: + dalb = le16_to_cpu(telem->dalb2); + break; case NVME_TELEMETRY_DA_3: /* dalb3 >= dalb2 >= dalb1 */ - *size = (le16_to_cpu(telem->dalb3) + 1) * xfer; + dalb = le16_to_cpu(telem->dalb3); break; case NVME_TELEMETRY_DA_4: - err = nvme_identify_ctrl(fd, &id_ctrl); - if (err) { - perror("identify-ctrl"); - errno = EINVAL; - goto free; - } - - if (id_ctrl.lpa & 0x40) { - *size = (le32_to_cpu(telem->dalb4) + 1) * xfer; - } else { - fprintf(stderr, "Data area 4 unsupported, bit 6 of Log Page Attributes not set\n"); - errno = EINVAL; - err = -1; - goto free; - } + dalb = le32_to_cpu(telem->dalb4); break; default: - fprintf(stderr, "Invalid data area parameter - %d\n", da); errno = EINVAL; err = -1; goto free; } + if (dalb == 0) { + errno = ENOENT; + err = -1; + goto free; + } + + *size = (dalb + 1) * xfer; tmp = realloc(log, *size); if (!tmp) { errno = ENOMEM; @@ -218,7 +241,7 @@ static int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, args.lid = lid; args.log = log; args.len = *size; - err = nvme_get_log_page(fd, 4096, &args); + err = nvme_get_log_page(fd, max_data_tx, &args); if (!err) { *buf = log; return 0; @@ -228,22 +251,40 @@ static int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, return err; } + +static int nvme_check_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, + struct nvme_telemetry_log **log, enum nvme_telemetry_da da, + size_t *size) +{ + enum nvme_telemetry_da max_da = 0; + int err = nvme_get_telemetry_max(fd, &max_da, NULL); + + if (err) + return err; + if (da > max_da) { + errno = ENOENT; + return -1; + } + return nvme_get_telemetry_log(fd, create, ctrl, rae, 4096, da, log, size); +} + + int nvme_get_ctrl_telemetry(int fd, bool rae, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size) { - return nvme_get_telemetry_log(fd, false, true, rae, log, da, size); + return nvme_check_get_telemetry_log(fd, false, true, rae, log, da, size); } int nvme_get_host_telemetry(int fd, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size) { - return nvme_get_telemetry_log(fd, false, false, false, log, da, size); + return nvme_check_get_telemetry_log(fd, false, false, false, log, da, size); } int nvme_get_new_host_telemetry(int fd, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size) { - return nvme_get_telemetry_log(fd, true, false, false, log, da, size); + return nvme_check_get_telemetry_log(fd, true, false, false, log, da, size); } int nvme_get_lba_status_log(int fd, bool rae, struct nvme_lba_status_log **log) diff --git a/src/nvme/linux.h b/src/nvme/linux.h index 37ba9d4c..be918199 100644 --- a/src/nvme/linux.h +++ b/src/nvme/linux.h @@ -48,6 +48,37 @@ enum nvme_telemetry_da { NVME_TELEMETRY_DA_4 = 4, }; +/** + * nvme_get_telemetry_max() - Get telemetry limits + * @fd: File descriptor of nvme device + * @da: On success return max supported data area + * @max_data_tx: On success set to max transfer chunk supported by the controller + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_telemetry_max(int fd, enum nvme_telemetry_da *da, size_t *max_data_tx); + +/** + * nvme_get_telemetry_log() - Get specified telemetry log + * @fd: File descriptor of nvme device + * @create: Generate new host initated telemetry capture + * @ctrl: Get controller Initiated log + * @rae: Retain asynchronous events + * @max_data_tx: Set the max data transfer size to be used retrieving telemetry. + * @da: Log page data area, valid values: &enum nvme_telemetry_da. + * @log: On success, set to the value of the allocated and retrieved log. + * @size: Ptr to the telemetry log size, so it can be returned + * + * The total size allocated can be calculated as: + * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_data_tx, + enum nvme_telemetry_da da, struct nvme_telemetry_log **log, + size_t *size); /** * nvme_get_ctrl_telemetry() - Get controller telemetry log * @fd: File descriptor of nvme device