]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
nvme: Spport Phy Rx Eye Opening Measurement Log
authorBrandon Paupore <brandon.paupore@wdc.com>
Tue, 12 Sep 2023 21:46:09 +0000 (16:46 -0500)
committerDaniel Wagner <wagi@monom.org>
Mon, 25 Sep 2023 08:57:34 +0000 (10:57 +0200)
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 <brandon.paupore@wdc.com>
Documentation/nvme-phy-rx-eom-log.txt [new file with mode: 0644]
nvme-builtin.h
nvme-print-binary.c
nvme-print-json.c
nvme-print-stdout.c
nvme-print.c
nvme-print.h
nvme-wrap.c
nvme-wrap.h
nvme.c

diff --git a/Documentation/nvme-phy-rx-eom-log.txt b/Documentation/nvme-phy-rx-eom-log.txt
new file mode 100644 (file)
index 0000000..015c851
--- /dev/null
@@ -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
index 784e19db5f4a39ec05f55f18da5736bcc6f0cff5..9b9a145f0c4f9a14ec5bed6a1ca1cc64c93f985d 100644 (file)
@@ -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)
index 3a4617a33598d55084f02ae70cf6f83ef9612d76..45d86d3268598b4435f33ac16608536f51f45ebd 100644 (file)
@@ -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));
@@ -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,
index 7e6b05cbb551e4afb1395adebc6a2c4c339768a5..485c013fa7c22e8424c42c086bc00a048fb7966e 100644 (file)
@@ -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)
 {
 
@@ -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,
index dd5d7dccbb66fe2df1315a085f5d0f5c3577fb43..4990a24a8d7141fe3bc069a27fa9233530c768ce 100644 (file)
@@ -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;
@@ -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,
index 286e4d55fb23b41e434272c8ef7c919b8cb31647..572b641b121962ab95203d52f02695f0dcbf455b 100644 (file)
@@ -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)
 {
index 9b81b45bd238a991a7652266b207e09aef6dce1c..1c7e5dcc93e11fda82c599961b98456b03d450b5 100644 (file)
@@ -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);
@@ -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,
index bec9b69b9ccb50bc19f0efa8809c8676250e0546..20cc617e2c5173562b26fba6010b8886e88f3d2a 100644 (file)
@@ -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)
 {
index 729dab39be087e791973fe3de26741232eff4e4a..c18115d224a4d026099af3b474d1d4b6c18fe748 100644 (file)
@@ -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,
diff --git a/nvme.c b/nvme.c
index 1e4422e0f6efd66d83d8e80b90bf26172a60913d..197cc48f5038958b5f520915ccd6c8d1583730f2 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -1965,6 +1965,104 @@ static int get_boot_part_log(int argc, char **argv, struct command *cmd, struct
        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)
 {