Skip to content

Commit

Permalink
nvme: Spport Phy Rx Eye Opening Measurement Log
Browse files Browse the repository at this point in the history
This implements support for TP4119a, adding a new command nvme
phy-rx-eom-log for issuing Get Log Page with the new Log Identifier and
allowing for configuration/validation of the Log Specific Parameters.

Signed-off-by: Brandon Paupore <[email protected]>
  • Loading branch information
bpaupore-wdc committed Sep 22, 2023
1 parent 5a57078 commit d860837
Show file tree
Hide file tree
Showing 10 changed files with 405 additions and 0 deletions.
59 changes: 59 additions & 0 deletions Documentation/nvme-phy-rx-eom-log.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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
1 change: 1 addition & 0 deletions nvme-builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ COMMAND_LIST(
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)
Expand Down
13 changes: 13 additions & 0 deletions nvme-print-binary.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ static void binary_boot_part_log(void *bp_log, const char *devname,
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));
Expand Down Expand Up @@ -288,6 +300,7 @@ static void binary_discovery_log(struct nvmf_discovery_log *log, int numrec)
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,
Expand Down
111 changes: 111 additions & 0 deletions nvme-print-json.c
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,116 @@ static void json_boot_part_log(void *bp_log, const char *devname,
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)
{

Expand Down Expand Up @@ -2921,6 +3031,7 @@ static void json_output_perror(const char *msg)
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,
Expand Down
106 changes: 106 additions & 0 deletions nvme-print-stdout.c
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,111 @@ static void stdout_boot_part_log(void *bp_log, const char *devname,
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;
Expand Down Expand Up @@ -4942,6 +5047,7 @@ static void stdout_connect_msg(nvme_ctrl_t c)
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,
Expand Down
6 changes: 6 additions & 0 deletions nvme-print.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,12 @@ void nvme_show_boot_part_log(void *bp_log, const char *devname,
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)
{
Expand Down
3 changes: 3 additions & 0 deletions nvme-print.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct print_ops {
/* 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);
Expand Down Expand Up @@ -166,6 +167,8 @@ void nvme_show_resv_notif_log(struct nvme_resv_notification_log *resv,
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,
Expand Down
6 changes: 6 additions & 0 deletions nvme-wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,12 @@ int nvme_cli_get_log_boot_partition(struct nvme_dev *dev, bool rae, __u8 lsp,
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)
{
Expand Down
2 changes: 2 additions & 0 deletions nvme-wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ int nvme_cli_get_log_mi_cmd_supported_effects(struct nvme_dev *dev, bool rae,
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,
Expand Down
Loading

0 comments on commit d860837

Please sign in to comment.