--- /dev/null
+nvme-phy-rx-eom-log(1)
+======================
+
+NAME
+----
+nvme-phy-rx-eom-log - Retrieves a Physical Interface Receiver Eye Opening Measurement log page from an NVMe device
+
+SYNOPSIS
+--------
+[verse]
+'nvme phy-rx-eom-log' <device> [--lsp=<field> | -s <field>]
+ [--controller=<id> | -c <id>]
+ [--output-format=<fmt> | -o <fmt>]
+
+DESCRIPTION
+-----------
+Retrieves a Physical Interface Receiver Eye Opening Measurement log page from
+an NVMe device and provides the returned structure.
+
+The <device> parameter is mandatory and may be either the NVMe character
+device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1).
+
+On success it returns 0, error code otherwise.
+
+OPTIONS
+-------
+-s <field>::
+--lsp=<field>::
+ The log specified field configuring the controller's action to take
+ during processing of the command and the measurement quality.
+
+-c <id>::
+--controller=<id>::
+ Controller ID of the controller associated wit the PCIe port to be
+ measured.
+
+-o <format>::
+--output-format=<format>::
+ Set the reporting format to 'normal', 'json', or
+ 'binary'. Only one output format can be used at a time.
+
+EXAMPLES
+--------
+* Start a best quality measurement and retrieve the log page header
++
+------------
+# nvme phy-rx-eom-log /dev/nvme0 --lsp=10
+------------
+
+* Retrieve a finished best quality measurement on controller with ID 3
++
+------------
+# nvme phy-rx-eom-log /dev/nvme0 --lsp=2 --controller=3
+------------
+
+
+NVME
+----
+Part of the nvme-user suite
ENTRY("lba-status-log", "Retrieve LBA Status Information Log, show it", get_lba_status_log)
ENTRY("resv-notif-log", "Retrieve Reservation Notification Log, show it", get_resv_notif_log)
ENTRY("boot-part-log", "Retrieve Boot Partition Log, show it", get_boot_part_log)
+ ENTRY("phy-rx-eom-log", "Retrieve Physical Interface Receiver Eye Opening Measurement, show it", get_phy_rx_eom_log)
ENTRY("get-feature", "Get feature and show the resulting value", get_feature)
ENTRY("device-self-test", "Perform the necessary tests to observe the performance", device_self_test)
ENTRY("self-test-log", "Retrieve the SELF-TEST Log, show it", self_test_log)
d_raw((unsigned char *)bp_log, size);
}
+static void binary_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log,
+ __u16 controller)
+{
+ size_t len;
+ if (log->eomip == NVME_PHY_RX_EOM_COMPLETED)
+ len = log->hsize + log->dsize * log->nd;
+ else
+ len = log->hsize;
+
+ d_raw((unsigned char *)log, len);
+}
+
static void binary_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log)
{
d_raw((unsigned char *)mus_log, sizeof(*mus_log));
static struct print_ops binary_print_ops = {
.ana_log = binary_ana_log,
.boot_part_log = binary_boot_part_log,
+ .phy_rx_eom_log = binary_phy_rx_eom_log,
.ctrl_list = binary_list_ctrl,
.ctrl_registers = binary_ctrl_registers,
.directive = binary_directive,
json_free_object(root);
}
+/* Printable Eye string is allocated and returned, caller must free */
+static char *json_eom_printable_eye(struct nvme_eom_lane_desc *lane,
+ struct json_object *root)
+{
+ char *eye = (char *)lane->eye_desc;
+
+ char *printable = malloc(lane->nrows * lane->ncols + lane->ncols);
+ char *printable_start = printable;
+ if (!printable)
+ goto exit;
+
+ int i, j;
+ for (i = 0; i < lane->nrows; i++) {
+ for (j = 0; j < lane->ncols; j++, printable++)
+ sprintf(printable, "%c", eye[i * lane->ncols + j]);
+ sprintf(printable++, "\n");
+ }
+
+ json_object_add_value_string(root, "printable_eye", printable_start);
+
+exit:
+ return printable_start;
+}
+
+
+static void json_phy_rx_eom_descs(struct nvme_phy_rx_eom_log *log,
+ struct json_object *root, char **allocated_eyes)
+{
+ void *p = log->descs;
+ uint16_t num_descs = le16_to_cpu(log->nd);
+ int i;
+ struct json_object *descs;
+
+ descs = json_create_array();
+ json_object_add_value_array(root, "descs", descs);
+
+ for (i = 0; i < num_descs; i++) {
+ struct nvme_eom_lane_desc *desc = p;
+ struct json_object *jdesc = json_create_object();
+
+ json_object_add_value_uint(jdesc, "lid", desc->mstatus);
+ json_object_add_value_uint(jdesc, "lane", desc->lane);
+ json_object_add_value_uint(jdesc, "eye", desc->eye);
+ json_object_add_value_uint(jdesc, "top", le16_to_cpu(desc->top));
+ json_object_add_value_uint(jdesc, "bottom", le16_to_cpu(desc->bottom));
+ json_object_add_value_uint(jdesc, "left", le16_to_cpu(desc->left));
+ json_object_add_value_uint(jdesc, "right", le16_to_cpu(desc->right));
+ json_object_add_value_uint(jdesc, "nrows", le16_to_cpu(desc->nrows));
+ json_object_add_value_uint(jdesc, "ncols", le16_to_cpu(desc->ncols));
+ json_object_add_value_uint(jdesc, "edlen", le16_to_cpu(desc->edlen));
+
+ if (log->odp & NVME_EOM_PRINTABLE_EYE_PRESENT)
+ allocated_eyes[i] = json_eom_printable_eye(desc, root);
+
+ /* Eye Data field is vendor specific, doesn't map to JSON */
+
+ json_array_add_value_object(descs, jdesc);
+
+ p += log->dsize;
+ }
+}
+
+static void json_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log, __u16 controller)
+{
+ char **allocated_eyes = NULL;
+ int i;
+
+ struct json_object *root;
+ root = json_create_object();
+
+ json_object_add_value_uint(root, "lid", log->lid);
+ json_object_add_value_uint(root, "eomip", log->eomip);
+ json_object_add_value_uint(root, "hsize", le16_to_cpu(log->hsize));
+ json_object_add_value_uint(root, "rsize", le32_to_cpu(log->rsize));
+ json_object_add_value_uint(root, "eomdgn", log->eomdgn);
+ json_object_add_value_uint(root, "lr", log->lr);
+ json_object_add_value_uint(root, "lanes", log->lanes);
+ json_object_add_value_uint(root, "epl", log->epl);
+ json_object_add_value_uint(root, "lspfc", log->lspfc);
+ json_object_add_value_uint(root, "li", log->li);
+ json_object_add_value_uint(root, "lsic", le16_to_cpu(log->lsic));
+ json_object_add_value_uint(root, "dsize", le32_to_cpu(log->dsize));
+ json_object_add_value_uint(root, "nd", le16_to_cpu(log->nd));
+ json_object_add_value_uint(root, "maxtb", le16_to_cpu(log->maxtb));
+ json_object_add_value_uint(root, "maxlr", le16_to_cpu(log->maxlr));
+ json_object_add_value_uint(root, "etgood", le16_to_cpu(log->etgood));
+ json_object_add_value_uint(root, "etbetter", le16_to_cpu(log->etbetter));
+ json_object_add_value_uint(root, "etbest", le16_to_cpu(log->etbest));
+
+ if (log->eomip == NVME_PHY_RX_EOM_COMPLETED) {
+ /* Save Printable Eye strings allocated to free later */
+ allocated_eyes = malloc(log->nd * sizeof(char *));
+ if (allocated_eyes)
+ json_phy_rx_eom_descs(log, root, allocated_eyes);
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ if (allocated_eyes) {
+ for (i = 0; i < log->nd; i++) {
+ /* Free any Printable Eye strings allocated */
+ if (allocated_eyes[i])
+ free(allocated_eyes[i]);
+ }
+ free(allocated_eyes);
+ }
+ json_free_object(root);
+}
+
static void json_media_unit_stat_log(struct nvme_media_unit_stat_log *mus)
{
static struct print_ops json_print_ops = {
.ana_log = json_ana_log,
.boot_part_log = json_boot_part_log,
+ .phy_rx_eom_log = json_phy_rx_eom_log,
.ctrl_list = json_nvme_list_ctrl,
.ctrl_registers = json_ctrl_registers,
.discovery_log = json_discovery_log,
printf("Active BPID: %u\n", (le32_to_cpu(hdr->bpinfo) >> 31) & 0x1);
}
+static const char *eomip_to_string(__u8 eomip)
+{
+ const char *string;
+ switch (eomip) {
+ case NVME_PHY_RX_EOM_NOT_STARTED:
+ string = "Not Started";
+ break;
+ case NVME_PHY_RX_EOM_IN_PROGRESS:
+ string = "In Progress";
+ break;
+ case NVME_PHY_RX_EOM_COMPLETED:
+ string = "Completed";
+ break;
+ default:
+ string = "Unknown";
+ }
+ return string;
+}
+
+static void stdout_phy_rx_eom_odp(uint8_t odp)
+{
+ __u8 rsvd = (odp >> 2) & 0x3F;
+ __u8 edfp = (odp >> 1) & 0x1;
+ __u8 pefp = odp & 0x1;
+
+ if (rsvd)
+ printf(" [7:2] : %#x\tReserved\n", rsvd);
+ printf(" [1:1] : %#x\tEye Data Field %sPresent\n",
+ edfp, edfp ? "" : "Not ");
+ printf(" [0:0] : %#x\tPrintable Eye Field %sPresent\n",
+ pefp, pefp ? "" : "Not ");
+}
+
+static void stdout_eom_printable_eye(struct nvme_eom_lane_desc *lane)
+{
+ char *eye = (char *)lane->eye_desc;
+ int i, j;
+ for (i = 0; i < lane->nrows; i++) {
+ for (j = 0; j < lane->ncols; j++)
+ printf("%c", eye[i * lane->ncols + j]);
+ printf("\n");
+ }
+}
+
+static void stdout_phy_rx_eom_descs(struct nvme_phy_rx_eom_log *log)
+{
+ void *p = log->descs;
+ int i;
+
+ for (i = 0; i < log->nd; i++) {
+ struct nvme_eom_lane_desc *desc = p;
+
+ printf("Measurement Status: %s\n",
+ desc->mstatus ? "Successful" : "Not Successful");
+ printf("Lane: %u\n", desc->lane);
+ printf("Eye: %u\n", desc->eye);
+ printf("Top: %u\n", le16_to_cpu(desc->top));
+ printf("Bottom: %u\n", le16_to_cpu(desc->bottom));
+ printf("Left: %u\n", le16_to_cpu(desc->left));
+ printf("Right: %u\n", le16_to_cpu(desc->right));
+ printf("Number of Rows: %u\n", le16_to_cpu(desc->nrows));
+ printf("Number of Columns: %u\n", le16_to_cpu(desc->ncols));
+ printf("Eye Data Length: %u\n", le16_to_cpu(desc->edlen));
+
+ if (log->odp & NVME_EOM_PRINTABLE_EYE_PRESENT)
+ stdout_eom_printable_eye(desc);
+
+ /* Eye Data field is vendor specific */
+
+ p += log->dsize;
+ }
+}
+
+static void stdout_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log, __u16 controller)
+{
+ int human = stdout_print_ops.flags & VERBOSE;
+
+ printf("Physical Interface Receiver Eye Opening Measurement Log for controller ID: %u\n", controller);
+ printf("Log ID: %u\n", log->lid);
+ printf("EOM In Progress: %s\n", eomip_to_string(log->eomip));
+ printf("Header Size: %u\n", le16_to_cpu(log->hsize));
+ printf("Result Size: %u\n", le32_to_cpu(log->rsize));
+ printf("EOM Data Generation Number: %u\n", log->eomdgn);
+ printf("Log Revision: %u\n", log->lr);
+ printf("Optional Data Present: %u\n", log->odp);
+ if (human)
+ stdout_phy_rx_eom_odp(log->odp);
+ printf("Lanes: %u\n", log->lanes);
+ printf("Eyes Per Lane: %u\n", log->epl);
+ printf("Log Specific Parameter Field Copy: %u\n", log->lspfc);
+ printf("Link Information: %u\n", log->li);
+ printf("Log Specific Identifier Copy: %u\n", le16_to_cpu(log->lsic));
+ printf("Descriptor Size: %u\n", le32_to_cpu(log->dsize));
+ printf("Number of Descriptors: %u\n", le16_to_cpu(log->nd));
+ printf("Maximum Top Bottom: %u\n", le16_to_cpu(log->maxtb));
+ printf("Maximum Left Right: %u\n", le16_to_cpu(log->maxlr));
+ printf("Estimated Time for Good Quality: %u\n", le16_to_cpu(log->etgood));
+ printf("Estimated Time for Better Quality: %u\n", le16_to_cpu(log->etbetter));
+ printf("Estimated Time for Best Quality: %u\n", le16_to_cpu(log->etbest));
+
+ if (log->eomip == NVME_PHY_RX_EOM_COMPLETED) {
+ stdout_phy_rx_eom_descs(log);
+ }
+}
+
static void stdout_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log)
{
int i;
static struct print_ops stdout_print_ops = {
.ana_log = stdout_ana_log,
.boot_part_log = stdout_boot_part_log,
+ .phy_rx_eom_log = stdout_phy_rx_eom_log,
.ctrl_list = stdout_list_ctrl,
.ctrl_registers = stdout_ctrl_registers,
.directive = stdout_directive_show,
nvme_print(boot_part_log, flags, bp_log, devname, size);
}
+void nvme_show_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log, __u16 controller,
+ enum nvme_print_flags flags)
+{
+ nvme_print(phy_rx_eom_log, flags, log, controller);
+}
+
void nvme_show_media_unit_stat_log(struct nvme_media_unit_stat_log *mus_log,
enum nvme_print_flags flags)
{
/* libnvme types.h print functions */
void (*ana_log)(struct nvme_ana_log *ana_log, const char *devname, size_t len);
void (*boot_part_log)(void *bp_log, const char *devname, __u32 size);
+ void (*phy_rx_eom_log)(struct nvme_phy_rx_eom_log *log, __u16 controller);
void (*ctrl_list)(struct nvme_ctrl_list *ctrl_list);
void (*ctrl_registers)(void *bar, bool fabrics);
void (*directive)(__u8 type, __u8 oper, __u16 spec, __u32 nsid, __u32 result, void *buf, __u32 len);
const char *devname, enum nvme_print_flags flags);
void nvme_show_boot_part_log(void *bp_log, const char *devname,
__u32 size, enum nvme_print_flags flags);
+void nvme_show_phy_rx_eom_log(struct nvme_phy_rx_eom_log *log,
+ __u16 controller, enum nvme_print_flags flags);
void nvme_show_fid_support_effects_log(struct nvme_fid_supported_effects_log *fid_log,
const char *devname, enum nvme_print_flags flags);
void nvme_show_mi_cmd_support_effects_log(struct nvme_mi_cmd_supported_effects_log *mi_cmd_log,
return do_admin_op(get_log_boot_partition, dev, rae, lsp, len, part);
}
+int nvme_cli_get_log_phy_rx_eom(struct nvme_dev *dev, __u8 lsp, __u16 controller,
+ __u32 len, struct nvme_phy_rx_eom_log *part)
+{
+ return do_admin_op(get_log_phy_rx_eom, dev, lsp, controller, len, part);
+}
+
int nvme_cli_get_log_discovery(struct nvme_dev *dev, bool rae,
__u32 offset, __u32 len, void *log)
{
int nvme_cli_get_log_boot_partition(struct nvme_dev *dev, bool rae, __u8 lsp,
__u32 len,
struct nvme_boot_partition *part);
+int nvme_cli_get_log_phy_rx_eom(struct nvme_dev *dev, __u8 lsp, __u16 controller,
+ __u32 len, struct nvme_phy_rx_eom_log *part);
int nvme_cli_get_log_discovery(struct nvme_dev *dev, bool rae,
__u32 offset, __u32 len, void *log);
int nvme_cli_get_log_media_unit_stat(struct nvme_dev *dev, __u16 domid,
return err;
}
+static int get_phy_rx_eom_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve Physical Interface Receiver Eye Opening\n"
+ "Measurement log for the given device in decoded format\n"
+ "(default), json or binary.";
+ const char *controller = "Target Controller ID.";
+ _cleanup_free_ struct nvme_phy_rx_eom_log *phy_rx_eom_log = NULL;
+ size_t phy_rx_eom_log_len;
+ enum nvme_print_flags flags;
+ _cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
+ int err = -1;
+ __u8 lsp_tmp;
+
+ struct config {
+ __u8 lsp;
+ __u16 controller;
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .lsp = 0,
+ .controller = NVME_LOG_LSI_NONE,
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_BYTE("lsp", 's', &cfg.lsp, lsp),
+ OPT_SHRT("controller", 'c', &cfg.controller, controller),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (err < 0) {
+ nvme_show_error("Invalid output format");
+ return err;
+ }
+
+ if (cfg.lsp > 127) {
+ nvme_show_error("invalid lsp param: %u", cfg.lsp);
+ return -1;
+ } else if ((cfg.lsp & 3) == 3) {
+ nvme_show_error("invalid measurement quality: %u", cfg.lsp & 3);
+ return -1;
+ } else if ((cfg.lsp & 12) == 12) {
+ nvme_show_error("invalid action: %u", cfg.lsp & 12);
+ return -1;
+ }
+
+ /* Fetching header to calculate total log length */
+ phy_rx_eom_log_len = sizeof(struct nvme_phy_rx_eom_log);
+ phy_rx_eom_log = nvme_alloc(phy_rx_eom_log_len);
+ if (!phy_rx_eom_log)
+ return -ENOMEM;
+
+ /* Just read measurement, take given action when fetching full log */
+ lsp_tmp = cfg.lsp & 0xf3;
+
+ err = nvme_cli_get_log_phy_rx_eom(dev, lsp_tmp, cfg.controller,
+ phy_rx_eom_log_len, phy_rx_eom_log);
+ if (err) {
+ if (err > 0)
+ nvme_show_status(err);
+ else
+ nvme_show_error("phy-rx-eom-log: %s", nvme_strerror(errno));
+
+ return err;
+ }
+
+ if (phy_rx_eom_log->eomip == NVME_PHY_RX_EOM_COMPLETED) {
+ phy_rx_eom_log_len = le16_to_cpu(phy_rx_eom_log->hsize) +
+ le32_to_cpu(phy_rx_eom_log->dsize) *
+ le16_to_cpu(phy_rx_eom_log->nd);
+ } else {
+ phy_rx_eom_log_len = le16_to_cpu(phy_rx_eom_log->hsize);
+ }
+
+ phy_rx_eom_log = nvme_realloc(phy_rx_eom_log, phy_rx_eom_log_len);
+ if (!phy_rx_eom_log)
+ return -ENOMEM;
+
+ err = nvme_cli_get_log_phy_rx_eom(dev, cfg.lsp, cfg.controller,
+ phy_rx_eom_log_len, phy_rx_eom_log);
+ if (!err)
+ nvme_show_phy_rx_eom_log(phy_rx_eom_log, cfg.controller, flags);
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ nvme_show_error("phy-rx-eom-log: %s", nvme_strerror(errno));
+
+ return err;
+}
+
static int get_media_unit_stat_log(int argc, char **argv, struct command *cmd,
struct plugin *plugin)
{