UTILINC=pciutils.h bitops.h $(PCIINC)
-LMR=margin_hw.o margin.o margin_log.o margin_results.o
+LMR=margin_hw.o margin.o margin_log.o margin_results.o margin_args.o
LMROBJS=$(addprefix lmr/,$(LMR))
LMRINC=lmr/lmr.h $(UTILINC)
bool hawd; // Hardware Autonomous Width Disable
};
-struct margin_link {
- struct margin_dev down_port;
- struct margin_dev up_port;
-};
-
/* Specification Revision 5.0 Table 8-11 */
struct margin_params {
bool ind_error_sampler;
};
/* pcilmr arguments */
-struct margin_args {
+
+// Common args
+struct margin_com_args {
+ u8 error_limit; // [0; 63]
+ bool run_margin; // Or print params only
+ u8 verbosity; // 0 - basic;
+ // 1 - add info about remaining time and lanes in progress during margining
+ u64 steps_utility; // For ETA logging
+ bool save_csv;
+ char *dir_for_csv;
+};
+
+struct margin_link_args {
+ struct margin_com_args *common;
u8 steps_t; // 0 == use NumTimingSteps
u8 steps_v; // 0 == use NumVoltageSteps
u8 parallel_lanes; // [1; MaxLanes + 1]
- u8 error_limit; // [0; 63]
u8 recvs[6]; // Receivers Numbers
u8 recvs_n; // 0 == margin all available receivers
u8 lanes[32]; // Lanes to Margin
u8 lanes_n; // 0 == margin all available lanes
- bool run_margin; // Or print params only
- u8 verbosity; // 0 - basic;
- // 1 - add info about remaining time and lanes in progress during margining
+};
- u64 *steps_utility; // For ETA logging
+struct margin_link {
+ struct margin_dev down_port;
+ struct margin_dev up_port;
+ struct margin_link_args args;
};
/* Receiver structure */
u8 verbosity;
};
+/* margin_args */
+
+enum margin_mode { MARGIN, FULL, SCAN };
+
+extern const char *usage;
+
+struct margin_link *margin_parse_util_args(struct pci_access *pacc, int argc, char **argv,
+ enum margin_mode mode, u8 *links_n);
+
/* margin_hw */
bool margin_port_is_down(struct pci_dev *dev);
bool margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
struct margin_params *params);
-enum margin_test_status margin_process_args(struct margin_dev *dev, struct margin_args *args);
+enum margin_test_status margin_process_args(struct margin_link *link);
-/* Awaits that args are prepared through process_args.
+/* Awaits that links are prepared through process_args.
Returns number of margined Receivers through recvs_n */
-struct margin_results *margin_test_link(struct margin_link *link, struct margin_args *args,
- u8 *recvs_n);
+struct margin_results *margin_test_link(struct margin_link *link, u8 *recvs_n);
void margin_free_results(struct margin_results *results, u8 results_n);
/* b:d.f -> b:d.f */
void margin_log_bdfs(struct pci_dev *down_port, struct pci_dev *up_port);
+void margin_gen_bdfs(struct pci_dev *down_port, struct pci_dev *up_port, char *dest, size_t maxlen);
/* Print Link header (bdfs, width, speed) */
void margin_log_link(struct margin_link *link);
void margin_results_print_brief(struct margin_results *results, u8 recvs_n);
-void margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
- struct pci_dev *up_port);
+void margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link);
#endif
/*
* The PCI Utilities -- Obtain the margin information of the Link
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
/* Awaits that Receiver is prepared through prep_dev function */
static bool
-margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
+margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_link_args *args,
struct margin_results *results)
{
u8 *lanes_to_margin = args->lanes;
.lane_reversal = false,
.params = ¶ms,
.parallel_lanes = args->parallel_lanes ? args->parallel_lanes : 1,
- .error_limit = args->error_limit };
+ .error_limit = args->common->error_limit };
results->recvn = recvn;
results->lanes_n = lanes_n;
= recv.lane_reversal ? dev->width - lanes_to_margin[i] - 1 : lanes_to_margin[i];
}
- if (args->run_margin)
+ if (args->common->run_margin)
{
- if (args->verbosity > 0)
+ if (args->common->verbosity > 0)
margin_log("\n");
- struct margin_lanes_data lanes_data
- = { .recv = &recv, .verbosity = args->verbosity, .steps_utility = args->steps_utility };
+ struct margin_lanes_data lanes_data = { .recv = &recv,
+ .verbosity = args->common->verbosity,
+ .steps_utility = &args->common->steps_utility };
enum margin_dir dir[] = { TIM_LEFT, TIM_RIGHT, VOLT_UP, VOLT_DOWN };
lanes_data.ind = timing ? params.ind_left_right_tim : params.ind_up_down_volt;
lanes_data.dir = dir[i];
lanes_data.steps_lane_total = timing ? steps_t : steps_v;
- if (*args->steps_utility >= lanes_data.steps_lane_total)
- *args->steps_utility -= lanes_data.steps_lane_total;
+ if (args->common->steps_utility >= lanes_data.steps_lane_total)
+ args->common->steps_utility -= lanes_data.steps_lane_total;
else
- *args->steps_utility = 0;
+ args->common->steps_utility = 0;
margin_test_lanes(lanes_data);
}
lanes_done += use_lanes;
}
- if (args->verbosity > 0)
+ if (args->common->verbosity > 0)
margin_log("\n");
if (recv.lane_reversal)
{
}
enum margin_test_status
-margin_process_args(struct margin_dev *dev, struct margin_args *args)
+margin_process_args(struct margin_link *link)
{
+ struct margin_dev *dev = &link->down_port;
+ struct margin_link_args *args = &link->args;
+
u8 receivers_n = 2 + 2 * dev->retimers_n;
if (!args->recvs_n)
}
struct margin_results *
-margin_test_link(struct margin_link *link, struct margin_args *args, u8 *recvs_n)
+margin_test_link(struct margin_link *link, u8 *recvs_n)
{
+ struct margin_link_args *args = &link->args;
+
bool status = margin_prep_link(link);
u8 receivers_n = status ? args->recvs_n : 1;
--- /dev/null
+/*
+ * The PCI Utilities -- Parse pcilmr utility arguments
+ *
+ * Copyright (c) 2024 KNS Group LLC (YADRO)
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lmr.h"
+
+const char* usage
+ = "! Utility requires preliminary preparation of the system. Refer to the pcilmr man page !\n\n"
+ "Usage:\n"
+ "pcilmr [--margin] [<margining options>] <downstream component> ...\n"
+ "pcilmr --full [<margining options>]\n"
+ "pcilmr --scan\n\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"
+ "--scan\t\t\tScan for Links available for margining\n\n"
+ "Margining options:\n\n"
+ "Margining Test settings:\n"
+ "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
+ "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
+ "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
+ "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
+ "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
+ "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
+ "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
+ "\t\t\tDefault: all available Receivers (including Retimers).\n"
+ "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
+ "\t\t\tDefault: 1.\n"
+ "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
+ "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
+ "\t\t\tbad, so this option is for experiments mostly.\n"
+ "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
+ "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
+ "-V\t\t\tSame as -T option, but for Voltage.\n"
+ "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
+ "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
+ "Use only one of -T/-t options at the same time (same for -V/-v).\n"
+ "Without these options utility will use MaxSteps from Device\n"
+ "capabilities as test limit.\n\n"
+ "Margining Log settings:\n"
+ "-o <directory>\t\tSave margining results in csv form into the\n"
+ "\t\t\tspecified directory. Utility will generate file with the\n"
+ "\t\t\tname in form of 'lmr_<downstream component>_Rx#_<timestamp>.csv'\n"
+ "\t\t\tfor each successfully tested receiver.\n";
+
+static struct pci_dev *
+dev_for_filter(struct pci_access *pacc, char *filter)
+{
+ struct pci_filter pci_filter;
+ pci_filter_init(pacc, &pci_filter);
+ if (pci_filter_parse_slot(&pci_filter, filter))
+ die("Invalid device ID: %s\n", filter);
+
+ if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
+ die("Invalid device ID: %s\n", filter);
+
+ if (pci_filter.domain == -1)
+ pci_filter.domain = 0;
+
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
+ {
+ if (pci_filter_match(&pci_filter, p))
+ return p;
+ }
+
+ die("No such PCI device: %s or you don't have enough privileges.\n", filter);
+}
+
+static u8
+parse_csv_arg(char *arg, u8 *vals)
+{
+ u8 cnt = 0;
+ char *token = strtok(arg, ",");
+ while (token)
+ {
+ vals[cnt] = atoi(token);
+ cnt++;
+ token = strtok(NULL, ",");
+ }
+ return cnt;
+}
+
+static u8
+find_ready_links(struct pci_access *pacc, struct margin_link *links, bool cnt_only)
+{
+ u8 cnt = 0;
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
+ {
+ if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
+ {
+ struct pci_dev *down = NULL;
+ struct pci_dev *up = NULL;
+ margin_find_pair(pacc, p, &down, &up);
+
+ if (down && margin_verify_link(down, up)
+ && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
+ {
+ if (!cnt_only)
+ margin_fill_link(down, up, &(links[cnt]));
+ cnt++;
+ }
+ }
+ }
+ return cnt;
+}
+
+static void
+init_link_args(struct margin_link_args *link_args, struct margin_com_args *com_args)
+{
+ memset(link_args, 0, sizeof(*link_args));
+ link_args->common = com_args;
+ link_args->parallel_lanes = 1;
+}
+
+static void
+parse_dev_args(int argc, char **argv, struct margin_link_args *args, u8 link_speed)
+{
+ if (argc == optind)
+ return;
+ int c;
+ while ((c = getopt(argc, argv, "+r:l:p:t:v:VT")) != -1)
+ {
+ switch (c)
+ {
+ case 't':
+ args->steps_t = atoi(optarg);
+ break;
+ case 'T':
+ args->steps_t = 63;
+ break;
+ case 'v':
+ args->steps_v = atoi(optarg);
+ break;
+ case 'V':
+ args->steps_v = 127;
+ break;
+ case 'p':
+ args->parallel_lanes = atoi(optarg);
+ break;
+ case 'l':
+ args->lanes_n = parse_csv_arg(optarg, args->lanes);
+ break;
+ case 'r':
+ args->recvs_n = parse_csv_arg(optarg, args->recvs);
+ break;
+ case '?':
+ die("Invalid arguments\n\n%s", usage);
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+struct margin_link *
+margin_parse_util_args(struct pci_access *pacc, int argc, char **argv, enum margin_mode mode,
+ u8 *links_n)
+{
+ struct margin_com_args *com_args = xmalloc(sizeof(*com_args));
+ com_args->error_limit = 4;
+ com_args->run_margin = true;
+ com_args->verbosity = 1;
+ com_args->steps_utility = 0;
+ com_args->dir_for_csv = NULL;
+ com_args->save_csv = false;
+
+ int c;
+ while ((c = getopt(argc, argv, "+e:co:")) != -1)
+ {
+ switch (c)
+ {
+ case 'c':
+ com_args->run_margin = false;
+ break;
+ case 'e':
+ com_args->error_limit = atoi(optarg);
+ break;
+ case 'o':
+ com_args->dir_for_csv = optarg;
+ com_args->save_csv = true;
+ break;
+ default:
+ die("Invalid arguments\n\n%s", usage);
+ }
+ }
+
+ bool status = true;
+ 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);
+ if (!status)
+ {
+ printf("%s", usage);
+ exit(0);
+ }
+
+ u8 ports_n = 0;
+ struct margin_link *links = NULL;
+ char err[128];
+
+ if (mode == FULL)
+ {
+ ports_n = find_ready_links(pacc, NULL, true);
+ if (ports_n == 0)
+ die("Links not found or you don't have enough privileges.\n");
+ else
+ {
+ links = xmalloc(ports_n * sizeof(*links));
+ find_ready_links(pacc, links, false);
+ for (int i = 0; i < ports_n; i++)
+ init_link_args(&(links[i].args), com_args);
+ }
+ }
+ else if (mode == MARGIN)
+ {
+ while (optind != argc)
+ {
+ struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
+ optind++;
+ links = xrealloc(links, (ports_n + 1) * sizeof(*links));
+ struct pci_dev *down;
+ struct pci_dev *up;
+ if (!margin_find_pair(pacc, dev, &down, &up))
+ die("Cannot find pair for the specified device: %s\n", argv[optind]);
+ struct pci_cap *cap = pci_find_cap(down, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
+ if (!cap)
+ 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, up, &(links[ports_n])))
+ {
+ margin_gen_bdfs(down, up, err, sizeof(err));
+ die("Link %s 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",
+ err);
+ }
+ init_link_args(&(links[ports_n].args), com_args);
+ parse_dev_args(argc, argv, &(links[ports_n].args),
+ links[ports_n].down_port.link_speed - 4);
+ ports_n++;
+ }
+ }
+ else
+ die("Bug in the args parsing!\n");
+
+ *links_n = ports_n;
+ return links;
+}
* SPDX-License-Identifier: GPL-2.0-or-later
*/
+#include <memory.h>
+
#include "lmr.h"
static u16 special_hw[][4] =
bool
margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct margin_link *wrappers)
{
+ memset(wrappers, 0, sizeof(*wrappers));
if (!margin_verify_link(down_port, up_port))
return false;
wrappers->down_port = fill_dev_wrapper(down_port);
/*
* The PCI Utilities -- Log margining process
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
up->func);
}
+void
+margin_gen_bdfs(struct pci_dev *down, struct pci_dev *up, char *dest, size_t maxlen)
+{
+ if (margin_print_domain)
+ snprintf(dest, maxlen, "%x:%x:%x.%x -> %x:%x:%x.%x", down->domain, down->bus, down->dev,
+ down->func, up->domain, up->bus, up->dev, up->func);
+ else
+ snprintf(dest, maxlen, "%x:%x.%x -> %x:%x.%x", down->bus, down->dev, down->func, up->bus,
+ up->dev, up->func);
+}
+
void
margin_log_link(struct margin_link *link)
{
/*
* The PCI Utilities -- Display/save margining results
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
}
void
-margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
- struct pci_dev *up_port)
+margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link)
{
char timestamp[64];
time_t tim = time(NULL);
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", gmtime(&tim));
+ char *dir = link->args.common->dir_for_csv;
size_t pathlen = strlen(dir) + 128;
char *path = xmalloc(pathlen);
FILE *csv;
enum lane_rating lane_rating;
u8 link_speed;
+ struct pci_dev *port;
+
for (int i = 0; i < recvs_n; i++)
{
res = &(results[i]);
if (res->test_status != MARGIN_TEST_OK)
continue;
+
+ port = res->recvn == 6 ? link->up_port.dev : link->down_port.dev;
snprintf(path, pathlen, "%s/lmr_%0*x.%02x.%02x.%x_Rx%X_%s.csv", dir,
- up_port->domain_16 == 0xffff ? 8 : 4, up_port->domain, up_port->bus, up_port->dev,
- up_port->func, 10 + res->recvn - 1, timestamp);
+ port->domain_16 == 0xffff ? 8 : 4, port->domain, port->bus, port->dev,
+ port->func, 10 + res->recvn - 1, timestamp);
csv = fopen(path, "w");
if (!csv)
die("Error while saving %s\n", path);
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include "lmr/lmr.h"
const char program_name[] = "pcilmr";
-enum mode { MARGIN, FULL, SCAN };
-
-static const char usage_msg[]
- = "! Utility requires preliminary preparation of the system. Refer to the pcilmr man page !\n\n"
- "Usage:\n"
- "pcilmr [--margin] [<margining options>] <downstream component> ...\n"
- "pcilmr --full [<margining options>]\n"
- "pcilmr --scan\n\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"
- "--scan\t\t\tScan for Links available for margining\n\n"
- "Margining options:\n\n"
- "Margining Test settings:\n"
- "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
- "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
- "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
- "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
- "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
- "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
- "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
- "\t\t\tDefault: all available Receivers (including Retimers).\n"
- "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
- "\t\t\tDefault: 1.\n"
- "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
- "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
- "\t\t\tbad, so this option is for experiments mostly.\n"
- "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
- "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
- "-V\t\t\tSame as -T option, but for Voltage.\n"
- "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
- "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
- "Use only one of -T/-t options at the same time (same for -V/-v).\n"
- "Without these options utility will use MaxSteps from Device\n"
- "capabilities as test limit.\n\n"
- "Margining Log settings:\n"
- "-o <directory>\t\tSave margining results in csv form into the\n"
- "\t\t\tspecified directory. Utility will generate file with the\n"
- "\t\t\tname in form of 'lmr_<downstream component>_Rx#_<timestamp>.csv'\n"
- "\t\t\tfor each successfully tested receiver.\n";
-
-static struct pci_dev *
-dev_for_filter(struct pci_access *pacc, char *filter)
-{
- struct pci_filter pci_filter;
- pci_filter_init(pacc, &pci_filter);
- if (pci_filter_parse_slot(&pci_filter, filter))
- die("Invalid device ID: %s\n", filter);
-
- if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
- die("Invalid device ID: %s\n", filter);
-
- if (pci_filter.domain == -1)
- pci_filter.domain = 0;
-
- for (struct pci_dev *p = pacc->devices; p; p = p->next)
- {
- if (pci_filter_match(&pci_filter, p))
- return p;
- }
-
- die("No such PCI device: %s or you don't have enough privileges.\n", filter);
-}
-
-static u8
-parse_csv_arg(char *arg, u8 *vals)
-{
- u8 cnt = 0;
- char *token = strtok(arg, ",");
- while (token)
- {
- vals[cnt] = atoi(token);
- cnt++;
- token = strtok(NULL, ",");
- }
- return cnt;
-}
-
static void
scan_links(struct pci_access *pacc, bool only_ready)
{
exit(0);
}
-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 *p = pacc->devices; p; p = p->next)
- {
- if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
- {
- struct pci_dev *down = NULL;
- struct pci_dev *up = NULL;
- margin_find_pair(pacc, p, &down, &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_ports;
- struct pci_dev **down_ports;
- u8 ports_n = 0;
-
+ u8 links_n = 0;
struct margin_link *links;
bool *checks_status_ports;
- bool status = true;
- enum mode mode;
+ enum margin_mode mode;
/* each link has several receivers -> several results */
struct margin_results **results;
u8 *results_n;
- 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;
-
- bool run_margin = true;
-
- char *dir_for_csv = NULL;
- bool save_csv = false;
-
- u64 total_steps = 0;
-
pacc = pci_alloc();
pci_init(pacc);
pci_scan_bus(pacc);
{ .name = "full", .has_arg = no_argument, .flag = NULL, .val = 2 },
{ 0, 0, 0, 0 } };
+ opterr = 0;
int c;
- c = getopt_long(argc, argv, ":", long_options, NULL);
+ c = getopt_long(argc, argv, "+", long_options, NULL);
switch (c)
{
mode = SCAN;
if (optind == argc)
scan_links(pacc, false);
- optind--;
+ else
+ die("Invalid arguments\n\n%s", usage);
break;
case 2:
mode = FULL;
break;
}
- while ((c = getopt(argc, argv, ":r:e:l:cp:t:v:VTo:")) != -1)
- {
- switch (c)
- {
- case 't':
- steps_t_arg = atoi(optarg);
- break;
- case 'T':
- steps_t_arg = 63;
- break;
- case 'v':
- steps_v_arg = atoi(optarg);
- break;
- case 'V':
- steps_v_arg = 127;
- break;
- case 'p':
- parallel_lanes_arg = atoi(optarg);
- break;
- case 'c':
- run_margin = false;
- break;
- case 'l':
- lanes_n = parse_csv_arg(optarg, lanes_arg);
- break;
- case 'e':
- error_limit = atoi(optarg);
- break;
- case 'r':
- recvs_n = parse_csv_arg(optarg, recvs_arg);
- break;
- case 'o':
- dir_for_csv = optarg;
- save_csv = true;
- break;
- default:
- die("Invalid arguments\n\n%s", usage_msg);
- }
- }
-
- 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);
- if (!status)
- {
- printf("%s", usage_msg);
- exit(0);
- }
-
- 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));
-
- u8 cnt = 0;
- while (optind != argc)
- {
- struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
- if (!margin_find_pair(pacc, dev, &(down_ports[cnt]), &(up_ports[cnt])))
- die("Cannot find pair for the specified device: %s\n", argv[optind]);
- cnt++;
- optind++;
- }
- }
- else
- die("Bug in the args parsing!\n");
+ opterr = 1;
- 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");
+ links = margin_parse_util_args(pacc, argc, argv, mode, &links_n);
+ struct margin_com_args *com_args = links[0].args.common;
- 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));
+ results = xmalloc(links_n * sizeof(*results));
+ results_n = xmalloc(links_n * sizeof(*results_n));
+ checks_status_ports = xmalloc(links_n * sizeof(*checks_status_ports));
- for (int i = 0; i < ports_n; i++)
+ for (int i = 0; i < links_n; i++)
{
- 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 (!margin_fill_link(down_ports[i], up_ports[i], &links[i]))
- {
- checks_status_ports[i] = false;
- results[i] = xmalloc(sizeof(*results[i]));
- results[i]->test_status = MARGIN_TEST_PREREQS;
- continue;
- }
-
- if ((args_status = margin_process_args(&links[i].down_port, &args[i])) != MARGIN_TEST_OK)
+ if ((args_status = margin_process_args(&links[i])) != MARGIN_TEST_OK)
{
checks_status_ports[i] = false;
results[i] = xmalloc(sizeof(*results[i]));
checks_status_ports[i] = true;
struct margin_params params;
+ struct margin_link_args *link_args = &links[i].args;
- for (int j = 0; j < args[i].recvs_n; j++)
+ for (int j = 0; j < link_args->recvs_n; j++)
{
- if (margin_read_params(pacc, args[i].recvs[j] == 6 ? up_ports[i] : down_ports[i],
- args[i].recvs[j], ¶ms))
+ if (margin_read_params(
+ pacc, link_args->recvs[j] == 6 ? links[i].up_port.dev : links[i].down_port.dev,
+ link_args->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;
- u8 parallel_recv = parallel_lanes_arg > params.max_lanes + 1 ? params.max_lanes + 1 :
- parallel_lanes_arg;
+ u8 steps_t = link_args->steps_t ? link_args->steps_t : params.timing_steps;
+ u8 steps_v = link_args->steps_v ? link_args->steps_v : params.volt_steps;
+ u8 parallel_recv = link_args->parallel_lanes > params.max_lanes + 1 ?
+ params.max_lanes + 1 :
+ link_args->parallel_lanes;
u8 step_multiplier
- = args[i].lanes_n / parallel_recv + ((args[i].lanes_n % parallel_recv) > 0);
+ = link_args->lanes_n / parallel_recv + ((link_args->lanes_n % parallel_recv) > 0);
- total_steps += steps_t * step_multiplier;
+ com_args->steps_utility += steps_t * step_multiplier;
if (params.ind_left_right_tim)
- total_steps += steps_t * step_multiplier;
+ com_args->steps_utility += steps_t * step_multiplier;
if (params.volt_support)
{
- total_steps += steps_v * step_multiplier;
+ com_args->steps_utility += steps_v * step_multiplier;
if (params.ind_up_down_volt)
- total_steps += steps_v * step_multiplier;
+ com_args->steps_utility += steps_v * step_multiplier;
}
}
}
}
- for (int i = 0; i < ports_n; i++)
+ for (int i = 0; i < links_n; i++)
{
if (checks_status_ports[i])
- results[i] = margin_test_link(&links[i], &args[i], &results_n[i]);
+ results[i] = margin_test_link(&links[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)
+ if (results[i]->test_status == MARGIN_TEST_ARGS_RECVS)
{
margin_log_link(&links[i]);
printf("\nInvalid RecNums specified.\n");
printf("\n----\n\n");
}
- if (run_margin)
+ if (com_args->run_margin)
{
printf("Results:\n");
printf("\nPass/fail criteria:\nTiming:\n");
printf("THR -\tThe set (using the utility options) \n\tstep threshold has been reached\n\n");
printf("Notations:\nst - steps\n\n");
- for (int i = 0; i < ports_n; i++)
+ for (int i = 0; i < links_n; i++)
{
printf("Link ");
- margin_log_bdfs(down_ports[i], up_ports[i]);
+ margin_log_bdfs(links[i].down_port.dev, links[i].up_port.dev);
printf(":\n\n");
margin_results_print_brief(results[i], results_n[i]);
- if (save_csv)
- margin_results_save_csv(results[i], results_n[i], dir_for_csv, up_ports[i]);
+ if (com_args->save_csv)
+ margin_results_save_csv(results[i], results_n[i], &links[i]);
printf("\n");
}
}
- for (int i = 0; i < ports_n; i++)
+ for (int i = 0; i < links_n; i++)
margin_free_results(results[i], results_n[i]);
free(results_n);
free(results);
- free(up_ports);
- free(down_ports);
+ free(com_args);
free(links);
free(checks_status_ports);
- free(args);
pci_cleanup(pacc);
return 0;