linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Nikita Proshkin <n.proshkin@yadro.com>
To: <linux-pci@vger.kernel.org>, Martin Mares <mj@ucw.cz>
Cc: <linux@yadro.com>, Bjorn Helgaas <helgaas@kernel.org>,
	Sergei Miroshnichenko <s.miroshnichenko@yadro.com>,
	Nikita Proshkin <n.proshkin@yadro.com>
Subject: [PATCH v2 11/15] pciutils-pcilmr: Add the ability to pass multiple links to the utility
Date: Wed, 27 Dec 2023 14:45:00 +0500	[thread overview]
Message-ID: <20231227094504.32257-12-n.proshkin@yadro.com> (raw)
In-Reply-To: <20231227094504.32257-1-n.proshkin@yadro.com>

* Add support for different utility modes;
* Make the default (now --margin) mode capable to accept several
  components and run test for all of them;
* Add --full mode for sequential start of the test on all ready links
  in the system;
* The complication of the main function is due to the need to pre-read the
  parameters of the devices before starting the tests in order to calculate
  Total ETA of the utility.

Reviewed-by: Sergei Miroshnichenko <s.miroshnichenko@yadro.com>
Signed-off-by: Nikita Proshkin <n.proshkin@yadro.com>
---
 pcilmr.c | 243 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 189 insertions(+), 54 deletions(-)

diff --git a/pcilmr.c b/pcilmr.c
index 3634c97..3c2f250 100644
--- a/pcilmr.c
+++ b/pcilmr.c
@@ -17,11 +17,17 @@
 
 const char program_name[] = "pcilmr";
 
+enum mode { MARGIN, FULL };
+
 static const char usage_msg[]
   = "Usage:\n"
-    "pcilmr [<margining options>] <downstream component>\n\n"
+    "pcilmr [--margin] [<margining options>] <downstream component> ...\n"
+    "pcilmr --full [<margining options>]\n"
     "Device Specifier:\n"
     "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
+    "Modes:\n"
+    "--margin\t\tMargin selected Links\n"
+    "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
     "Margining options:\n\n"
     "Margining Test settings:\n"
     "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
@@ -100,27 +106,59 @@ parse_csv_arg(char *arg, u8 *vals)
   return cnt;
 }
 
+static u8
+find_ready_links(struct pci_access *pacc, struct pci_dev **down_ports, struct pci_dev **up_ports,
+                 bool cnt_only)
+{
+  u8 cnt = 0;
+  for (struct pci_dev *up = pacc->devices; up; up = up->next)
+    {
+      if (pci_find_cap(up, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED))
+        {
+          struct pci_dev *down = find_down_port_for_up(pacc, up);
+
+          if (down && margin_verify_link(down, up)
+              && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
+            {
+              if (!cnt_only)
+                {
+                  up_ports[cnt] = up;
+                  down_ports[cnt] = down;
+                }
+              cnt++;
+            }
+        }
+    }
+  return cnt;
+}
+
 int
 main(int argc, char **argv)
 {
   struct pci_access *pacc;
 
-  struct pci_dev *up_port;
-  struct pci_dev *down_port;
+  struct pci_dev **up_ports;
+  struct pci_dev **down_ports;
+  u8 ports_n = 0;
 
-  struct margin_link link;
+  struct margin_link *links;
+  bool *checks_status_ports;
 
   bool status = true;
+  enum mode mode;
 
-  struct margin_results *results;
-  u8 results_n;
+  /* each link has several receivers -> several results */
+  struct margin_results **results;
+  u8 *results_n;
 
-  struct margin_args args;
+  struct margin_args *args;
 
   u8 steps_t_arg = 0;
   u8 steps_v_arg = 0;
   u8 parallel_lanes_arg = 1;
   u8 error_limit = 4;
+  u8 lanes_arg[32];
+  u8 recvs_arg[6];
 
   u8 lanes_n = 0;
   u8 recvs_n = 0;
@@ -145,7 +183,29 @@ main(int argc, char **argv)
 
   margin_global_logging = true;
 
+  struct option long_options[]
+    = { { .name = "margin", .has_arg = no_argument, .flag = NULL, .val = 0 },
+        { .name = "full", .has_arg = no_argument, .flag = NULL, .val = 1 },
+        { 0, 0, 0, 0 } };
+
   int c;
+  c = getopt_long(argc, argv, ":", long_options, NULL);
+
+  switch (c)
+    {
+      case -1: /* no options (strings like component are possible) */
+        /* FALLTHROUGH */
+      case 0:
+        mode = MARGIN;
+        break;
+      case 1:
+        mode = FULL;
+        break;
+      default: /* unknown option symbol */
+        mode = MARGIN;
+        optind--;
+        break;
+    }
 
   while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VT")) != -1)
     {
@@ -170,20 +230,22 @@ main(int argc, char **argv)
             run_margin = false;
             break;
           case 'l':
-            lanes_n = parse_csv_arg(optarg, args.lanes);
+            lanes_n = parse_csv_arg(optarg, lanes_arg);
             break;
           case 'e':
             error_limit = atoi(optarg);
             break;
           case 'r':
-            recvs_n = parse_csv_arg(optarg, args.recvs);
+            recvs_n = parse_csv_arg(optarg, recvs_arg);
             break;
           default:
             die("Invalid arguments\n\n%s", usage_msg);
         }
     }
 
-  if (optind != argc - 1)
+  if (mode == FULL && optind != argc)
+    status = false;
+  if (mode == MARGIN && optind == argc)
     status = false;
   if (!status && argc > 1)
     die("Invalid arguments\n\n%s", usage_msg);
@@ -193,59 +255,91 @@ main(int argc, char **argv)
       exit(0);
     }
 
-  up_port = dev_for_filter(pacc, argv[argc - 1]);
+  if (mode == FULL)
+    {
+      ports_n = find_ready_links(pacc, NULL, NULL, true);
+      if (ports_n == 0)
+        {
+          die("Links not found or you don't have enough privileges.\n");
+        }
+      else
+        {
+          up_ports = xmalloc(ports_n * sizeof(*up_ports));
+          down_ports = xmalloc(ports_n * sizeof(*down_ports));
+          find_ready_links(pacc, down_ports, up_ports, false);
+        }
+    }
+  else if (mode == MARGIN)
+    {
+      ports_n = argc - optind;
+      up_ports = xmalloc(ports_n * sizeof(*up_ports));
+      down_ports = xmalloc(ports_n * sizeof(*down_ports));
 
-  down_port = find_down_port_for_up(pacc, up_port);
-  if (!down_port)
-    die("Cannot find Upstream Component for the specified device: %s\n", argv[argc - 1]);
+      u8 cnt = 0;
+      while (optind != argc)
+        {
+          up_ports[cnt] = dev_for_filter(pacc, argv[optind]);
+          down_ports[cnt] = find_down_port_for_up(pacc, up_ports[cnt]);
+          if (!down_ports[cnt])
+            die("Cannot find Upstream Component for the specified device: %s\n", argv[optind]);
+          cnt++;
+          optind++;
+        }
+    }
+  else
+    die("Bug in the args parsing!\n");
 
-  if (!pci_find_cap(up_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
+  if (!pci_find_cap(up_ports[0], PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
     die("Looks like you don't have enough privileges to access "
         "Device Configuration Space.\nTry to run utility as root.\n");
 
-  if (!margin_fill_link(down_port, up_port, &link))
-    {
-      printf("Link ");
-      margin_log_bdfs(down_port, up_port);
-      printf(" is not ready for margining.\n"
-             "Link data rate must be 16 GT/s or 32 GT/s.\n"
-             "Downstream Component must be at D0 PM state.\n");
-      status = false;
-    }
+  results = xmalloc(ports_n * sizeof(*results));
+  results_n = xmalloc(ports_n * sizeof(*results_n));
+  links = xmalloc(ports_n * sizeof(*links));
+  checks_status_ports = xmalloc(ports_n * sizeof(*checks_status_ports));
+  args = xmalloc(ports_n * sizeof(*args));
 
-  if (status)
+  for (int i = 0; i < ports_n; i++)
     {
-      args.error_limit = error_limit;
-      args.lanes_n = lanes_n;
-      args.recvs_n = recvs_n;
-      args.steps_t = steps_t_arg;
-      args.steps_v = steps_v_arg;
-      args.parallel_lanes = parallel_lanes_arg;
-      args.run_margin = run_margin;
-      args.verbosity = 1;
-      args.steps_utility = &total_steps;
+      args[i].error_limit = error_limit;
+      args[i].parallel_lanes = parallel_lanes_arg;
+      args[i].run_margin = run_margin;
+      args[i].verbosity = 1;
+      args[i].steps_t = steps_t_arg;
+      args[i].steps_v = steps_v_arg;
+      for (int j = 0; j < recvs_n; j++)
+        args[i].recvs[j] = recvs_arg[j];
+      args[i].recvs_n = recvs_n;
+      for (int j = 0; j < lanes_n; j++)
+        args[i].lanes[j] = lanes_arg[j];
+      args[i].lanes_n = lanes_n;
+      args[i].steps_utility = &total_steps;
 
       enum margin_test_status args_status;
 
-      if ((args_status = margin_process_args(&link.down_port, &args)) != MARGIN_TEST_OK)
+      if (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
         {
-          status = false;
-          margin_log_link(&link);
-          if (args_status == MARGIN_TEST_ARGS_RECVS)
-            margin_log("\nInvalid RecNums specified.\n");
-          else if (args_status == MARGIN_TEST_ARGS_LANES)
-            margin_log("\nInvalid lanes specified.\n");
+          checks_status_ports[i] = false;
+          results[i] = xmalloc(sizeof(*results[i]));
+          results[i]->test_status = MARGIN_TEST_PREREQS;
+          continue;
         }
-    }
 
-  if (status)
-    {
+      if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
+        {
+          checks_status_ports[i] = false;
+          results[i] = xmalloc(sizeof(*results[i]));
+          results[i]->test_status = args_status;
+          continue;
+        }
+
+      checks_status_ports[i] = true;
       struct margin_params params;
 
-      for (int i = 0; i < args.recvs_n; i++)
+      for (int j = 0; j < args[i].recvs_n; j++)
         {
-          if (margin_read_params(pacc, args.recvs[i] == 6 ? up_port : down_port, args.recvs[i],
-                                 &params))
+          if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
+                                 args[i].recvs[j], &params))
             {
               u8 steps_t = steps_t_arg ? steps_t_arg : params.timing_steps;
               u8 steps_v = steps_v_arg ? steps_v_arg : params.volt_steps;
@@ -253,7 +347,7 @@ main(int argc, char **argv)
                                                                              parallel_lanes_arg;
 
               u8 step_multiplier
-                = args.lanes_n / parallel_recv + ((args.lanes_n % parallel_recv) > 0);
+                = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
 
               total_steps += steps_t * step_multiplier;
               if (params.ind_left_right_tim)
@@ -266,13 +360,40 @@ main(int argc, char **argv)
                 }
             }
         }
+    }
 
-      results = margin_test_link(&link, &args, &results_n);
+  for (int i = 0; i < ports_n; i++)
+    {
+      if (checks_status_ports[i])
+        results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
+      else
+        {
+          results_n[i] = 1;
+          if (results[i]->test_status == MARGIN_TEST_PREREQS)
+            {
+              printf("Link ");
+              margin_log_bdfs(down_ports[i], up_ports[i]);
+              printf(" is not ready for margining.\n"
+                     "Link data rate must be 16 GT/s or 32 GT/s.\n"
+                     "Downstream Component must be at D0 PM state.\n");
+            }
+          else if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
+            {
+              margin_log_link(&links[i]);
+              printf("\nInvalid RecNums specified.\n");
+            }
+          else if (results[i]->test_status == MARGIN_TEST_ARGS_LANES)
+            {
+              margin_log_link(&links[i]);
+              printf("\nInvalid lanes specified.\n");
+            }
+        }
+      printf("\n----\n\n");
     }
 
-  if (status && run_margin)
+  if (run_margin)
     {
-      printf("\nResults:\n");
+      printf("Results:\n");
       printf("\nPass/fail criteria:\nTiming:\n");
       printf("Minimum Offset (spec): %d %% UI\nRecommended Offset: %d %% UI\n", MARGIN_TIM_MIN,
              MARGIN_TIM_RECOMMEND);
@@ -283,11 +404,25 @@ main(int argc, char **argv)
       printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
       printf("Notations:\nst - steps\n\n");
 
-      margin_results_print_brief(results, results_n);
+      for (int i = 0; i < ports_n; i++)
+        {
+          printf("Link ");
+          margin_log_bdfs(down_ports[i], up_ports[i]);
+          printf(":\n\n");
+          margin_results_print_brief(results[i], results_n[i]);
+          printf("\n");
+        }
     }
 
-  if (status)
-    margin_free_results(results, results_n);
+  for (int i = 0; i < ports_n; i++)
+    margin_free_results(results[i], results_n[i]);
+  free(results_n);
+  free(results);
+  free(up_ports);
+  free(down_ports);
+  free(links);
+  free(checks_status_ports);
+  free(args);
 
   pci_cleanup(pacc);
   return 0;
-- 
2.34.1


  parent reply	other threads:[~2023-12-27  9:46 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-12-27  9:44 [PATCH v2 00/15] pciutils: Add utility for Lane Margining Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 01/15] pciutils-lspci: Fix unsynchronized caches in lspci struct device and pci struct pci_dev Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 02/15] pciutils: Add constants for Lane Margining at the Receiver Extended Capability Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 03/15] pciutils-lspci: Add Lane Margining support to the lspci Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 04/15] pciutils-pcilib: Add separate file for bit manipulation functions Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 05/15] pciutils-pcilmr: Add functions for device checking and preparations before main margining processes Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 06/15] pciutils-pcilmr: Add margining process functions Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 07/15] pciutils-pcilmr: Add logging functions for margining Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 08/15] pciutils-pcilmr: Add function for default margining results log Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 09/15] pciutils-pcilmr: Add utility main function Nikita Proshkin
2023-12-27  9:44 ` [PATCH v2 10/15] pciutils-pcilmr: Add support for unique hardware quirks Nikita Proshkin
2023-12-27  9:45 ` Nikita Proshkin [this message]
2023-12-27  9:45 ` [PATCH v2 12/15] pciutils-pcilmr: Add --scan mode to search for all LMR-capable Links Nikita Proshkin
2023-12-27  9:45 ` [PATCH v2 13/15] pciutils-pcilmr: Add option to save margining results in csv form Nikita Proshkin
2023-12-27  9:45 ` [PATCH v2 14/15] pciutils-pcilmr: Add handling of situations when device reports its MaxOffset values equal to 0 Nikita Proshkin
2023-12-27  9:45 ` [PATCH v2 15/15] pciutils-pcilmr: Add pcilmr man page Nikita Proshkin
2024-02-06 13:31 ` [PATCH v2 00/15] pciutils: Add utility for Lane Margining Nikita Proshkin
2024-02-17 22:47 ` Martin Mareš

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231227094504.32257-12-n.proshkin@yadro.com \
    --to=n.proshkin@yadro.com \
    --cc=helgaas@kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux@yadro.com \
    --cc=mj@ucw.cz \
    --cc=s.miroshnichenko@yadro.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).