Files
s390-tools/zconf/css/lscss.c
Vineeth Vijayan 761a185938 zconf/lscss: show device number from pmcw
lscss shows device number as 'None' if the corresponding device in the
subchannel is non-operational or not connected. Instead, show the
device-no derived from the new dev_busid attribute which provides the
value of device_id irrespective of the device availability.

i.e the current lscss shows

Device   Subchan.  DevType CU Type Use  PIM PAM POM  CHPIDs
----------------------------------------------------------------------
0.0.0000 0.0.0000  3390/0c 3990/e9      f0  a0  ff   32333435 00000000
none     0.0.00c7                       f0  a0  5f   32333435 00000000
none     0.0.00c8                       f0  a0  5f   32333435 00000000

here the subchannels 0.0.00c7 and 0.0.00c8 do not have an operational
device on them. Hence the device is shown as none.

With this patch,

Device   Subchan.  DevType CU Type Use  PIM PAM POM  CHPIDs
----------------------------------------------------------------------
0.0.0000 0.0.0000  3390/0c 3990/e9      f0  a0  ff   32333435 00000000
0.0.74c8 0.0.00c7                       f0  a0  5f   32333435 00000000
0.0.74c9 0.0.00c8                       f0  a0  5f   32333435 00000000

The device-no is shown as 0.0.74c8; Where the DevType field still
indicates that the device is not present.

Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-09-30 17:20:52 +02:00

943 lines
25 KiB
C

/*
* lscss - Tool to list information about subchannels
*
* Copyright IBM Corp. 2003, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <stdlib.h>
#include "lib/ccw.h"
#include "lib/util_base.h"
#include "lib/util_file.h"
#include "lib/util_libc.h"
#include "lib/util_list.h"
#include "lib/util_opt.h"
#include "lib/util_path.h"
#include "lib/util_prg.h"
#include "lib/util_rec.h"
#include "lib/util_scandir.h"
#include "lib/util_panic.h"
#include "lib/zt_common.h"
#include "misc.h"
/*
* Numbers for lscss command options that do not have a short form
*/
#define OPT_AVAIL 256 /* --avail */
#define OPT_VPM 257 /* --vpm */
#define OPT_IO 258 /* --io */
#define OPT_CHSC 259 /* --chsc */
#define OPT_EADM 260 /* --eadm */
#define OPT_VFIO 261 /* --vfio */
/* Bus_id format for subchannel or device id */
#define ID_FORMAT "^[[:xdigit:]]{1,2}[.][[:xdigit:]][.][[:xdigit:]]{4}$"
/* UUID format */
#define UUID_FORMAT "^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$"
/* Misc constants */
#define MAX_BUF_SIZE 256
#define PREFIX_ID_LENGTH 4
#define SHORT_ID_LENGTH 4
#define CHPIDS_SEGMENT_LENGTH 8
/* Range of subchannel or device identifiers */
struct range {
struct util_list_node node; /* Pointers to previous and next range */
struct ccw_devid lower; /* Lower sch_id or bus_id */
struct ccw_devid upper; /* Upper sch_id or bus_id */
};
/* Device type and model */
struct devtype {
struct util_list_node node; /* Pointers to previous and next devtype */
bool no_model; /* <type> format input */
unsigned int type; /* Device type */
unsigned int model; /* Specific model of the specified device type */
};
/* Subchannel types */
enum sch_type {
SUBCHANNEL_TYPE_IO = 0, /* I/O subchannels */
SUBCHANNEL_TYPE_CHSC = 1, /* CHSC subchannels */
SUBCHANNEL_TYPE_EADM = 3, /* EADM subchannels */
};
/*
* Private data
*/
static struct lscss_cmd_opts {
/* Boolean flags to indicate wich command options are in effect */
bool opt_short; /* -s or --short */
bool opt_devtype; /* -t or --devtype */
bool opt_devrange; /* -d or --devrange */
bool opt_avail; /* --avail */
bool opt_vpm; /* --vpm */
bool opt_uppercase; /* -u or --uppercase */
bool opt_io; /* --io */
bool opt_chsc; /* --chsc */
bool opt_eadm; /* --eadm */
bool opt_vfio; /* --vfio */
/* List of device types for the output limitation */
int dev_count;
struct util_list *devtypes;
/* List of sch_id or bus_id ranges for the output limitation */
int rng_count;
struct util_list *ranges;
} cmd;
/*
* Program configuration
*/
static const struct util_prg prg = {
.desc = "List information about available subchannels.\n"
"\nRANGE\n"
" ID Select single subchannel by ID, e.g. 0.0.004f or 4f\n"
" FROM-TO Select range of subchannels between FROM and TO\n"
" ID1,ID2-ID3,... Select list of subchannels or subchannel ranges",
.args = "[RANGE]",
.copyright_vec = {
{
.owner = "IBM Corp.",
.pub_first = 2017,
.pub_last = 2017,
},
UTIL_PRG_COPYRIGHT_END
}
};
/*
* Command line options
*/
static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("OPTIONS"),
{
.option = { "short", no_argument, NULL, 's'},
.desc = "Shorten IDs by removing leading \"0.0.\" "
"Note: only IDs beginning with \"0.0.\" "
"will be displayed in this case.",
},
{
.option = { "devtype", required_argument, NULL, 't'},
.argument = "TYPE,..",
.desc = "For IO subchannels, limit output to devices of "
"the given TYPE (DEVTYPE[/MODEL])",
},
{
.option = { "devrange", no_argument, NULL, 'd'},
.desc = "Indicate that RANGE refers to device identifiers",
},
{
.option = { "avail", no_argument, NULL, OPT_AVAIL},
.desc = "Show availability attribute of IO devices",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "vpm", no_argument, NULL, OPT_VPM},
.desc = "Show verified path mask",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "uppercase", no_argument, NULL, 'u'},
.desc = "Print values using uppercase",
},
{
.option = { "io", no_argument, NULL, OPT_IO},
.desc = "Show IO subchannels (default)",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "chsc", no_argument, NULL, OPT_CHSC},
.desc = "Show CHSC subchannels",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "eadm", no_argument, NULL, OPT_EADM},
.desc = "Show EADM subchannels",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "vfio", no_argument, NULL, OPT_VFIO},
.desc = "Show VFIO subchannel information",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "all", no_argument, NULL, 'a'},
.desc = "Show subchannels of all types",
},
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
UTIL_OPT_END
};
/*
* Add new subchannel or device range to the ranges list for further output
* limitation
*/
static void add_new_range(const char *lower_id, const char *upper_id)
{
struct range *rng;
rng = util_malloc(sizeof(*rng));
if (!ccw_parse_str(&rng->lower, lower_id))
errx(EXIT_FAILURE, "Invalid ID specified: %s", lower_id);
if (!ccw_parse_str(&rng->upper, upper_id))
errx(EXIT_FAILURE, "Invalid ID specified: %s", upper_id);
util_list_add_tail(cmd.ranges, rng);
}
/*
* Check if subchannel or device bus_id is within the provided range
*/
static bool id_in_range(struct range *rng, struct ccw_devid *id)
{
/* First compare cssid, then ssid and finally devno */
if (id->cssid == rng->lower.cssid) {
if (id->ssid == rng->lower.ssid) {
if (id->devno < rng->lower.devno)
return false;
} else if (id->ssid < rng->lower.ssid) {
return false;
}
} else if (id->cssid < rng->lower.cssid) {
return false;
}
if (id->cssid == rng->upper.cssid) {
if (id->ssid == rng->upper.ssid) {
if (id->devno > rng->upper.devno)
return false;
} else if (id->ssid > rng->upper.ssid) {
return false;
}
} else if (id->cssid > rng->upper.cssid) {
return false;
}
return true;
}
/*
* Check if subchannel or device id matches any range in the ranges list
*/
static bool id_in_ranges_list(const char *id)
{
struct ccw_devid ccw_id;
struct range *rng;
if (!ccw_parse_str(&ccw_id, id))
errx(EXIT_FAILURE, "Invalid subchannel directory '%s'", id);
util_list_iterate(cmd.ranges, rng) {
if (id_in_range(rng, &ccw_id))
return true;
}
return false;
}
/*
* Parse a string into a devtype structure
*
* @param[in,out] dt Pointer to devtype structure to be initialized
* @parm[in] id String to parse
*
* @returns true if the input string has been parsed successfuly;
* otherwise false
*/
static bool parse_devtype_str(struct devtype *dt, const char *id)
{
char d;
if (strncasecmp(id, "0x", 2) == 0)
return false;
dt->no_model = false;
if (sscanf(id, "%4x %c", &dt->type, &d) == 1) {
/* Process <type> input format (maximum length of 4 characters) */
dt->no_model = true;
} else if (sscanf(id, "%4x/%2x %c", &dt->type, &dt->model,
&d) != 2) {
/*
* Process <type>/<model> input format (maximum lengths of 4 and 2
* characters respectively)
*/
return false;
}
return true;
}
/*
* Add new device type to the devtypes list for further output limitation
*/
static void add_new_devtype(const char *dtype)
{
struct devtype *dt;
dt = util_malloc(sizeof(struct devtype));
if (!parse_devtype_str(dt, dtype))
errx(EXIT_FAILURE, "Invalid device type specified: %s", dtype);
util_list_add_tail(cmd.devtypes, dt);
}
/*
* Check if provided device type matches any from the device types list
*/
static bool in_devtypes_list(char *dtype)
{
struct devtype arg_dt;
struct devtype *dt;
if (!parse_devtype_str(&arg_dt, dtype))
errx(EXIT_FAILURE, "Invalid device type detected: %s", dtype);
util_list_iterate(cmd.devtypes, dt) {
if (arg_dt.type == dt->type) {
if (dt->no_model || arg_dt.model == dt->model)
return true;
}
}
return false;
}
/*
* Fill in the device related entry fields (devtyp, cutype, use, avail)
*
* @returns 0 - devtype matches devtypes list, device information filled in
* 1 - devtype does not match devtypes list, skip entry
*/
static int fill_device_info(struct util_rec *rec, char *path, char *device)
{
unsigned long int val_ul;
char buf[MAX_BUF_SIZE];
if (!path || !device) {
if (cmd.opt_devtype && cmd.dev_count > 0)
return 1;
util_rec_set(rec, "devtyp", "");
util_rec_set(rec, "cutype", "");
util_rec_set(rec, "use", "");
if (cmd.opt_avail)
util_rec_set(rec, "avail", "");
return 0;
}
if (util_file_read_line(buf, sizeof(buf), "%s/%s/devtype",
path, device) == 0) {
if (strcmp(buf, "n/a") == 0)
/* Special case for 'n/a' devtype */
strncpy(buf, "0000/00", sizeof(buf));
if (cmd.opt_devtype && cmd.dev_count > 0 &&
!in_devtypes_list(buf))
return 1;
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "devtyp", "%s", buf);
} else {
if (cmd.opt_devtype && cmd.dev_count > 0)
return 1;
util_rec_set(rec, "devtyp", "");
}
if (util_file_read_line(buf, sizeof(buf), "%s/%s/cutype",
path, device) == 0) {
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "cutype", "%s", buf);
} else {
util_rec_set(rec, "cutype", "");
}
if (util_file_read_ul(&val_ul, 10, "%s/%s/online", path, device) == 0) {
if (val_ul == 1) {
snprintf(buf, sizeof(buf), "yes");
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "use", "%s", buf);
} else {
util_rec_set(rec, "use", "");
}
} else {
util_rec_set(rec, "use", "");
}
if (cmd.opt_avail) {
if (util_file_read_line(buf, sizeof(buf), "%s/%s/availability",
path, device) == 0) {
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "avail", "%s", buf);
} else {
util_rec_set(rec, "avail", "");
}
}
return 0;
}
static bool is_sch_vfio(char *path)
{
char lnk[PATH_MAX], driver_path[PATH_MAX];
ssize_t rc;
snprintf(lnk, PATH_MAX, "%s/driver", path);
rc = readlink(lnk, driver_path, PATH_MAX);
if (rc < 0)
return false;
util_assert(rc < (PATH_MAX - 1),
"Internal error: Symlink name too long");
driver_path[rc] = '\0';
if (strcmp(basename(driver_path), "vfio_ccw") == 0)
return true;
return false;
}
/*
* Fill in the MDEV id entry field
*
* @returns 0 - MDEV id information filled in
* 1 - skip entry
*/
static int fill_vfio_devid(struct util_rec *rec, char *path)
{
char *device, buf[MAX_BUF_SIZE];
struct dirent **de_vec;
int count;
if (!is_sch_vfio(path))
return 1;
/* Find and process mdev device directory */
count = util_scandir(&de_vec, alphasort, path, "%s", UUID_FORMAT);
if (count > 0) {
device = de_vec[0]->d_name;
snprintf(buf, sizeof(buf), "%s", device);
} else {
strncpy(buf, "none", sizeof(buf));
}
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "mdev", "%s", buf);
util_scandir_free(de_vec, count);
return 0;
}
/*
* Fill in the CCW device id entry field
*
* @returns 0 - CCW device id information filled in
* 1 - skip entry
*/
static int fill_io_devid(struct util_rec *rec, char *path)
{
char *device, buf[MAX_BUF_SIZE];
struct dirent **de_vec;
int count;
/* Find and process device directory */
count = util_scandir(&de_vec, alphasort, path, "%s", ID_FORMAT);
if (count > 0) {
device = de_vec[0]->d_name;
if (cmd.opt_short) {
/* Display only 0.0.xxxx devices for --short */
if (strncmp(device, "0.0.", PREFIX_ID_LENGTH) != 0)
return 1;
snprintf(buf, sizeof(buf), "%s", device +
strlen(device) - SHORT_ID_LENGTH);
} else {
snprintf(buf, sizeof(buf), "%s", device);
}
if (cmd.opt_devrange && cmd.rng_count > 0 &&
!id_in_ranges_list(device)) {
util_scandir_free(de_vec, count);
return 1;
}
if (fill_device_info(rec, path, device) != 0) {
util_scandir_free(de_vec, count);
return 1;
}
} else {
if (cmd.opt_devrange && cmd.rng_count > 0) {
util_scandir_free(de_vec, count);
return 1;
}
if (util_file_read_line(buf, sizeof(buf), "%s/dev_busid",
path) == 0) {
if (cmd.opt_short) {
if (strncmp(buf, "0.0.", PREFIX_ID_LENGTH) != 0)
return 1;
memmove(buf, buf + PREFIX_ID_LENGTH,
(sizeof(buf) - PREFIX_ID_LENGTH));
}
}
fill_device_info(rec, NULL, NULL);
}
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "device", "%s", buf);
util_scandir_free(de_vec, count);
return 0;
}
/*
* Print IO subchannel entry
*/
static void print_sch_io(struct util_rec *rec, char *path, char *sch_dir)
{
unsigned int pim, pam, pom;
char buf[MAX_BUF_SIZE];
/* Fill in subchannel ID */
if (cmd.opt_short) {
/* Display only 0.0.xxxx subchannels for --short */
if (strncmp(sch_dir, "0.0.", PREFIX_ID_LENGTH) != 0)
return;
snprintf(buf, sizeof(buf), "%s", sch_dir +
strlen(sch_dir) - SHORT_ID_LENGTH);
} else {
snprintf(buf, sizeof(buf), "%s", sch_dir);
}
if (!cmd.opt_devrange && cmd.rng_count > 0 &&
!id_in_ranges_list(sch_dir))
return;
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "subch", "%s", buf);
if (cmd.opt_vfio) {
if (fill_vfio_devid(rec, path) != 0)
return;
} else if (fill_io_devid(rec, path) != 0)
return;
/* Fill in PIM-PAM-POM data */
if (util_file_read_line(buf, sizeof(buf), "%s/pimpampom", path) == 0) {
if (sscanf(buf, "%x %x %x", &pim, &pam, &pom) == 3) {
if (cmd.opt_uppercase) {
util_rec_set(rec, "pim", "%02X", pim);
util_rec_set(rec, "pam", "%02X", pam);
util_rec_set(rec, "pom", "%02X", pom);
} else {
util_rec_set(rec, "pim", "%02x", pim);
util_rec_set(rec, "pam", "%02x", pam);
util_rec_set(rec, "pom", "%02x", pom);
}
} else {
util_rec_set(rec, "pim", "");
util_rec_set(rec, "pam", "");
util_rec_set(rec, "pom", "");
}
} else {
util_rec_set(rec, "pim", "");
util_rec_set(rec, "pam", "");
util_rec_set(rec, "pom", "");
}
/* Fill in VPM data */
if (cmd.opt_vpm) {
if (util_file_read_line(buf, sizeof(buf),
"%s/vpm", path) == 0) {
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "vpm", "%s", buf);
} else {
util_rec_set(rec, "vpm", "");
}
}
/*
* Fill in CHPIDs data.
* Since chpids are stored as a list of two digit hexadecimal numbers,
* we first read it as a single string, then eliminate blanks from it
* and then break in two 8-char segments.
*/
if (util_file_read_line(buf, sizeof(buf), "%s/chpids", path) == 0) {
misc_str_remove_symbol(buf, ' ');
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "chpids", "%.8s %.8s", buf,
buf + CHPIDS_SEGMENT_LENGTH);
} else {
util_rec_set(rec, "chpids", "");
}
util_rec_print(rec);
}
/*
* Print CHSC subchannel entry
*/
static void print_sch_chsc(struct util_rec *rec, char *sch_dir)
{
char buf[MAX_BUF_SIZE];
/* Skip entry if devrange or devtype option is active */
if (cmd.opt_devrange && cmd.rng_count > 0)
return;
if (cmd.opt_devtype && cmd.dev_count > 0)
return;
/* Fill in subchannel ID */
if (cmd.opt_short) {
/* Display only 0.0.xxxx subchannels for --short */
if (strncmp(sch_dir, "0.0.", PREFIX_ID_LENGTH) != 0)
return;
snprintf(buf, sizeof(buf), "%s", sch_dir +
strlen(sch_dir) - SHORT_ID_LENGTH);
} else {
snprintf(buf, sizeof(buf), "%s", sch_dir);
}
if (cmd.rng_count > 0 && !id_in_ranges_list(sch_dir))
return;
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "subch", "%s", buf);
/* Device field is always 'n/a' for CHSC */
strncpy(buf, "n/a", sizeof(buf));
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "device", "%s", buf);
util_rec_print(rec);
}
/*
* Print EADM subchannel entry
*/
static void print_sch_eadm(struct util_rec *rec, char *sch_dir)
{
char buf[MAX_BUF_SIZE];
/* Skip entry if devrange or devtype option is active */
if (cmd.opt_devrange && cmd.rng_count > 0)
return;
if (cmd.opt_devtype && cmd.dev_count > 0)
return;
/* Fill in subchannel ID */
if (cmd.opt_short) {
/* Display only 0.0.xxxx subchannels for --short */
if (strncmp(sch_dir, "0.0.", PREFIX_ID_LENGTH) != 0)
return;
snprintf(buf, sizeof(buf), "%s", sch_dir +
strlen(sch_dir) - SHORT_ID_LENGTH);
} else {
snprintf(buf, sizeof(buf), "%s", sch_dir);
}
if (cmd.rng_count > 0 && !id_in_ranges_list(sch_dir))
return;
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "subch", "%s", buf);
/* Device field is always 'n/a' for EADM */
strncpy(buf, "n/a", sizeof(buf));
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "device", "%s", buf);
util_rec_print(rec);
}
/*
* Print information for all defunct devices in one css
*/
static void print_defunct_devices(struct util_rec *rec, char *path)
{
char *device, buf[MAX_BUF_SIZE];
struct dirent **de_vec;
int i, count;
/* Process all the devices within defunct directory */
count = util_scandir(&de_vec, alphasort, path, "%s", ID_FORMAT);
for (i = 0; i < count; i++) {
device = de_vec[i]->d_name;
if (cmd.opt_short) {
/* Display only 0.0.xxxx devices for --short */
if (strncmp(device, "0.0.", PREFIX_ID_LENGTH) != 0)
return;
snprintf(buf, sizeof(buf), "%s", device +
strlen(device) - SHORT_ID_LENGTH);
} else {
snprintf(buf, sizeof(buf), "%s", device);
}
if (cmd.opt_devrange && cmd.rng_count > 0 &&
!id_in_ranges_list(device))
continue;
if (fill_device_info(rec, path, device) != 0)
continue;
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "device", "%s", buf);
/* Subchannel field is always 'n/a' for defunct devices */
strncpy(buf, "n/a", sizeof(buf));
if (cmd.opt_uppercase)
util_str_toupper(buf);
util_rec_set(rec, "subch", "%s", buf);
/* Other fields are blank */
util_rec_set(rec, "pim", "");
util_rec_set(rec, "pam", "");
util_rec_set(rec, "pom", "");
if (cmd.opt_vpm)
util_rec_set(rec, "vpm", "");
util_rec_set(rec, "chpids", "");
util_rec_print(rec);
}
util_scandir_free(de_vec, count);
}
/*
* Loop through subchannel directories and print entries of specified type
*/
static void print_subchannels_of_type(enum sch_type type_requested,
struct util_rec *rec)
{
unsigned long int type_ul;
struct dirent **de_vec;
char *path, *sch_dir;
int i, count;
path = util_path_sysfs("bus/css/devices");
count = util_scandir(&de_vec, alphasort, path, "%s", ID_FORMAT);
free(path);
for (i = 0; i < count; i++) {
sch_dir = de_vec[i]->d_name;
path = util_path_sysfs("bus/css/devices/%s", sch_dir);
if (util_file_read_ul(&type_ul, 10, "%s/type", path) == 0) {
if (type_ul != type_requested)
continue;
if (type_ul == SUBCHANNEL_TYPE_IO)
print_sch_io(rec, path, sch_dir);
else if (type_ul == SUBCHANNEL_TYPE_CHSC)
print_sch_chsc(rec, sch_dir);
else if (type_ul == SUBCHANNEL_TYPE_EADM)
print_sch_eadm(rec, sch_dir);
} else {
/*
* Subchannels with no type identifier treated as
* IO subchannels
*/
if (type_requested == SUBCHANNEL_TYPE_IO)
print_sch_io(rec, path, sch_dir);
}
free(path);
}
util_scandir_free(de_vec, count);
/* Process defunct devices (if no subchannel range is specified) */
if (!cmd.opt_devrange && cmd.rng_count > 0)
return;
path = util_path_sysfs("devices");
count = util_scandir(&de_vec, alphasort, path, "css.*");
free(path);
for (i = 0; i < count; i++) {
path = util_path_sysfs("devices/%s/defunct", de_vec[i]->d_name);
print_defunct_devices(rec, path);
free(path);
}
util_scandir_free(de_vec, count);
}
/*
* Print subchannels table
*/
static void cmd_lscss(void)
{
struct util_rec *rec;
if (cmd.opt_io) {
rec = util_rec_new_wide("-");
util_rec_def(rec, "device", UTIL_REC_ALIGN_LEFT, 8, "Device");
util_rec_def(rec, "subch", UTIL_REC_ALIGN_LEFT, 9, "Subchan.");
util_rec_def(rec, "devtyp", UTIL_REC_ALIGN_LEFT, 7, "DevType");
util_rec_def(rec, "cutype", UTIL_REC_ALIGN_LEFT, 7, "CU Type");
util_rec_def(rec, "use", UTIL_REC_ALIGN_LEFT, 4, "Use");
util_rec_def(rec, "pim", UTIL_REC_ALIGN_LEFT, 3, "PIM");
util_rec_def(rec, "pam", UTIL_REC_ALIGN_LEFT, 3, "PAM");
if (cmd.opt_vpm) {
util_rec_def(rec, "pom", UTIL_REC_ALIGN_LEFT, 3, "POM");
util_rec_def(rec, "vpm", UTIL_REC_ALIGN_LEFT, 3, "VPM");
} else
util_rec_def(rec, "pom", UTIL_REC_ALIGN_LEFT, 4, "POM");
util_rec_def(rec, "chpids", UTIL_REC_ALIGN_LEFT, 17, "CHPIDs");
if (cmd.opt_avail)
util_rec_def(rec, "avail", UTIL_REC_ALIGN_LEFT, 6,
"Avail.");
/* Print header only if other subchannel types are requested */
if (cmd.opt_chsc || cmd.opt_eadm || cmd.opt_vfio)
printf("IO Subchannels and Devices:\n");
util_rec_print_hdr(rec);
print_subchannels_of_type(SUBCHANNEL_TYPE_IO, rec);
util_rec_free(rec);
}
if (cmd.opt_chsc) {
rec = util_rec_new_wide("-");
util_rec_def(rec, "device", UTIL_REC_ALIGN_LEFT, 8, "Device");
util_rec_def(rec, "subch", UTIL_REC_ALIGN_LEFT, 9, "Subchan.");
/* Print header only if other subchannel types also requested */
if (cmd.opt_io || cmd.opt_eadm || cmd.opt_vfio)
printf("\nCHSC Subchannels:\n");
util_rec_print_hdr(rec);
print_subchannels_of_type(SUBCHANNEL_TYPE_CHSC, rec);
util_rec_free(rec);
}
if (cmd.opt_eadm) {
rec = util_rec_new_wide("-");
util_rec_def(rec, "device", UTIL_REC_ALIGN_LEFT, 8, "Device");
util_rec_def(rec, "subch", UTIL_REC_ALIGN_LEFT, 9, "Subchan.");
/* Print header only if other subchannel types also requested */
if (cmd.opt_chsc || cmd.opt_io || cmd.opt_vfio)
printf("\nEADM Subchannels:\n");
util_rec_print_hdr(rec);
print_subchannels_of_type(SUBCHANNEL_TYPE_EADM, rec);
util_rec_free(rec);
}
if (cmd.opt_vfio) {
rec = util_rec_new_wide("-");
util_rec_def(rec, "mdev", UTIL_REC_ALIGN_LEFT, 37, "MDEV");
util_rec_def(rec, "subch", UTIL_REC_ALIGN_LEFT, 9, "Subchan.");
util_rec_def(rec, "pim", UTIL_REC_ALIGN_LEFT, 3, "PIM");
util_rec_def(rec, "pam", UTIL_REC_ALIGN_LEFT, 3, "PAM");
if (cmd.opt_vpm) {
util_rec_def(rec, "pom", UTIL_REC_ALIGN_LEFT, 3, "POM");
util_rec_def(rec, "vpm", UTIL_REC_ALIGN_LEFT, 3, "VPM");
} else
util_rec_def(rec, "pom", UTIL_REC_ALIGN_LEFT, 4, "POM");
util_rec_def(rec, "chpids", UTIL_REC_ALIGN_LEFT, 17, "CHPIDs");
/* Print header only if other subchannel types are requested */
if (cmd.opt_io || cmd.opt_chsc || cmd.opt_eadm)
printf("\nI/O Subchannels used for VFIO:\n");
util_rec_print_hdr(rec);
print_subchannels_of_type(SUBCHANNEL_TYPE_IO, rec);
util_rec_free(rec);
}
}
/*
* Parse options and execute the command
*/
int main(int argc, char *argv[])
{
char *id_from, *id_to, *id_list = NULL;
char *dtype, *dtype_list = NULL;
int c, i;
util_prg_init(&prg);
util_opt_init(opt_vec, NULL);
while (1) {
c = util_opt_getopt_long(argc, argv);
if (c == -1)
break;
switch (c) {
case 'h':
util_prg_print_help();
util_opt_print_help();
return EXIT_SUCCESS;
case 'v':
util_prg_print_version();
return EXIT_SUCCESS;
case 's':
cmd.opt_short = true;
break;
case 't':
cmd.opt_devtype = true;
dtype_list = optarg;
break;
case 'd':
cmd.opt_devrange = true;
break;
case OPT_AVAIL:
cmd.opt_avail = true;
break;
case OPT_VPM:
cmd.opt_vpm = true;
break;
case 'u':
cmd.opt_uppercase = true;
break;
case OPT_IO:
cmd.opt_io = true;
break;
case OPT_CHSC:
cmd.opt_chsc = true;
break;
case OPT_EADM:
cmd.opt_eadm = true;
break;
case OPT_VFIO:
cmd.opt_vfio = true;
break;
case 'a':
cmd.opt_io = true;
cmd.opt_chsc = true;
cmd.opt_eadm = true;
break;
default:
util_opt_print_parse_error(c, argv);
return EXIT_FAILURE;
}
}
/*
* VFIO subchannel view and IO subchannel view are mutual exclusion.
* And it does not make sense to use the -d, -t, and --avail options
* together with --vfio.
*/
if (cmd.opt_vfio &&
(cmd.opt_io || cmd.opt_devrange ||
cmd.opt_devtype || cmd.opt_avail)) {
errx(EXIT_FAILURE, "Invalid option combination: "
"--vfio can not be used with --io, -d, -t or --avail");
}
/* Display IO subchannels by default */
if (!cmd.opt_chsc && !cmd.opt_eadm && !cmd.opt_vfio)
cmd.opt_io = true;
/* Process the list of specified device types */
if (dtype_list != NULL) {
cmd.devtypes = util_list_new(struct devtype, node);
cmd.dev_count = 0;
/* Loop over comma-separated list */
dtype = strtok(dtype_list, ",");
while (dtype != NULL) {
add_new_devtype(dtype);
cmd.dev_count++;
dtype = strtok(NULL, ",");
}
}
/* Scan RANGE parameters appending each argument with comma */
for (i = optind; i < argc; i++) {
if (i > optind)
id_list = util_strcat_realloc(id_list, ",");
id_list = util_strcat_realloc(id_list, argv[i]);
}
/* Process the list of specified ranges */
if (id_list != NULL) {
cmd.ranges = util_list_new(struct range, node);
cmd.rng_count = 0;
/* Loop over comma-separated list */
id_from = strtok(id_list, ",");
while (id_from != NULL) {
id_to = strchr(id_from, '-');
if (id_to == NULL)
id_to = id_from;
else
*id_to++ = '\0';
if (*id_to == '\0')
errx(EXIT_FAILURE, "Invalid ID specified: %s", id_from);
add_new_range(id_from, id_to);
cmd.rng_count++;
id_from = strtok(NULL, ",");
}
free(id_list);
}
/* Process lscss command with provided options and attributes */
cmd_lscss();
util_list_free(cmd.devtypes);
util_list_free(cmd.ranges);
return EXIT_SUCCESS;
}