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],
- ¶ms))
+ if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
+ args[i].recvs[j], ¶ms))
{
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
next prev 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).