From: Sebastien GODARD Date: Mon, 2 Sep 2019 09:40:35 +0000 (+0200) Subject: sar/sadc: Add stable identifier support for disks statistics X-Git-Tag: v12.1.7~27 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=22e3fb23f8c099415b8e95938562f0afc1842b9c;p=sysstat sar/sadc: Add stable identifier support for disks statistics This patch adds new fields to stats_disk structure to save a stable identifier for each block device (see issue #195). A stable identifier is a name that should not change across reboots for the same physical device. At the present time this stable identifier is the WWN (World Wide Name) id that is read from /dev/disk/by-id if it exists for the device. If it doesn't exist then we fall back on using the pretty name (sda, sda1, etc.). The stable identifier is always collected by sadc when disks statistics are collected (sadc option "-S DISK | XDISK"). It can be printed by sar (or sadf) with the option "-j SID" (SID stands for Stable IDentifier). Signed-off-by: Sebastien GODARD --- diff --git a/common.c b/common.c index 08bdcd2..0fb62dd 100644 --- a/common.c +++ b/common.c @@ -269,6 +269,146 @@ void sysstat_panic(const char *function, int error_code) exit(1); } +/* + *************************************************************************** + * Extract WWWN identifiers from filename, as read from /dev/disk/by-id. + * + * Sample valid names read from /dev/disk/by-id: + * wwn-0x5000cca369f193ac + * wwn-0x5000cca369f193ac-part12 + * wwn-0x600605b00a2bdf00242b28c10dcb1999 + * wwn-0x600605b00a2bdf00242b28c10dcb1999-part2 + * + * WWN ids like these ones are ignored: + * wwn-0x5438850077615869953x + * wwn-0x5438850077615869953x-part1 + * + * IN: + * @name Filename read from /dev/disk/by-id. + * + * OUT: + * @wwn WWN identifier (8 or 16 hex characters). + * @part-nr Partition number if applicable. + * + * RETURNS: + * 0 on success, -1 otherwise. + *************************************************************************** +*/ +int extract_wwnid(char *name, unsigned long long *wwn, unsigned int *part_nr) +{ + char id[17]; + char *s; + int wwnlen; + + *wwn = *(wwn + 1) = 0ULL; + *part_nr = 0; + + /* Check name */ + if (((wwnlen = strlen(name)) < 22) || (strncmp(name, "wwn-0x", 6))) + return -1; + + /* Is there a partition number? */ + if ((s = strstr(name, "-part")) != NULL) { + /* Yes: Get partition number */ + if (sscanf(s + 5, "%u", part_nr) == 0) + return -1; + wwnlen = s - name - 6; + } + else { + wwnlen -= 6; /* Don't count "wwn-0x" */ + } + + /* Check WWN length */ + if ((wwnlen != 16) && (wwnlen != 32)) + return -1; + + /* Extract first 8 hex chars of WWN */ + strncpy(id, name + 6, 16); + id[16] = '\0'; + if (sscanf(id, "%llx", wwn) == 0) + return -1; + + if (strlen(name) < 38) + /* This is a short (8 hex chars) WWN id */ + return 0; + + /* Extract second part of WWN */ + if (sscanf(name + 22, "%llx", wwn + 1) == 0) + return -1; + + return 0; +} + +/* + *************************************************************************** + * Get WWWN identifiers from a pretty filename using links present in + * /dev/disk/by-id directory. + * + * IN: + * @pretty Pretty name (e.g. sda, sdb3...). + * + * OUT: + * @wwn WWN identifier (8 or 16 hex characters). + * @part-nr Partition number if applicable. + * + * RETURNS: + * 0 on success, -1 otherwise. + *************************************************************************** +*/ +int get_wwnid_from_pretty(char *pretty, unsigned long long *wwn, unsigned int *part_nr) +{ + DIR *dir; + struct dirent *drd; + ssize_t r; + char link[PATH_MAX], target[PATH_MAX], wwn_name[FILENAME_MAX]; + char *name; + int rc = -1; + + /* Open /dev/disk/by-id directory */ + if ((dir = opendir(DEV_DISK_BY_ID)) == NULL) + return -1; + + /* Get current id */ + while ((drd = readdir(dir)) != NULL) { + + if (strncmp(drd->d_name, "wwn-0x", 6)) + continue; + + /* Get absolute path for current persistent name */ + snprintf(link, PATH_MAX, "%s/%s", DEV_DISK_BY_ID, drd->d_name); + + /* Persistent name is usually a symlink: Read it... */ + r = readlink(link, target, PATH_MAX); + if ((r <= 0) || (r >= PATH_MAX)) + continue; + + target[r] = '\0'; + + /* ... and get device pretty name it points at */ + name = basename(target); + if (!name || (name[0] == '\0')) + continue; + + if (!strncmp(name, pretty, FILENAME_MAX)) { + /* We have found pretty name for current persistent one */ + strncpy(wwn_name, drd->d_name, FILENAME_MAX); + wwn_name[FILENAME_MAX - 1] = '\0'; + + /* Try to extract WWN */ + if (!extract_wwnid(wwn_name, wwn, part_nr)) { + /* WWN successfully extracted */ + rc = 0; + break; + } + } + } + + /* Close directory */ + closedir(dir); + + return rc; +} + #ifndef SOURCE_SADC /* *************************************************************************** @@ -804,7 +944,7 @@ char **get_persistent_names(void) * i != k because we are ignoring "." and ".." entries. */ for (i = 0; i < n; i++) { - /* Ignore "." and "..". */ + /* Ignore "." and ".." */ if (!strcmp(".", namelist[i]->d_name) || !strcmp("..", namelist[i]->d_name)) continue; diff --git a/common.h b/common.h index 651d6e6..86905ba 100644 --- a/common.h +++ b/common.h @@ -82,6 +82,7 @@ #define DEVICES PRE "/proc/devices" #define SYSFS_USBDEV PRE "/sys/bus/usb/devices" #define DEV_DISK_BY PRE "/dev/disk/by" +#define DEV_DISK_BY_ID PRE "/dev/disk/by-id" #define SYSFS_IDVENDOR "idVendor" #define SYSFS_IDPRODUCT "idProduct" #define SYSFS_BMAXPOWER "bMaxPower" @@ -242,6 +243,10 @@ int is_device (char *, int); void sysstat_panic (const char *, int); +int extract_wwnid + (char *, unsigned long long *, unsigned int *); +int get_wwnid_from_pretty + (char *, unsigned long long *, unsigned int *); #ifndef SOURCE_SADC int count_bits diff --git a/json_stats.c b/json_stats.c index 661417a..f16e285 100644 --- a/json_stats.c +++ b/json_stats.c @@ -734,7 +734,8 @@ __print_funct_t json_print_disk_stats(struct activity *a, int curr, int tab, } /* Get device name */ - dev_name = get_sa_devname(sdc->major, sdc->minor, flags); + dev_name = get_sa_devname(sdc->major, sdc->minor, + sdc->wwn, sdc->part_nr, flags); if (a->item_list != NULL) { /* A list of devices has been entered on the command line */ diff --git a/pcp_stats.c b/pcp_stats.c index 6a5a34c..675a082 100644 --- a/pcp_stats.c +++ b/pcp_stats.c @@ -610,7 +610,8 @@ __print_funct_t pcp_print_disk_stats(struct activity *a, int curr, unsigned long } /* Get device name */ - dev_name = get_sa_devname(sdc->major, sdc->minor, flags); + dev_name = get_sa_devname(sdc->major, sdc->minor, + sdc->wwn, sdc->part_nr, flags); if (a->item_list != NULL) { /* A list of devices has been entered on the command line */ diff --git a/pr_stats.c b/pr_stats.c index bb321ac..72c9a67 100644 --- a/pr_stats.c +++ b/pr_stats.c @@ -1053,7 +1053,7 @@ __print_funct_t print_disk_stats(struct activity *a, int prev, int curr, continue; /* Get device name */ - dev_name = get_sa_devname(sdc->major, sdc->minor, flags); + dev_name = get_sa_devname(sdc->major, sdc->minor, sdc->wwn, sdc->part_nr, flags); if (a->item_list != NULL) { /* A list of devices has been entered on the command line */ diff --git a/raw_stats.c b/raw_stats.c index c42ad98..6fff547 100644 --- a/raw_stats.c +++ b/raw_stats.c @@ -537,7 +537,8 @@ __print_funct_t raw_print_disk_stats(struct activity *a, char *timestr, int curr sdc = (struct stats_disk *) ((char *) a->buf[curr] + i * a->msize); /* Get device name */ - dev_name = get_sa_devname(sdc->major, sdc->minor, flags); + dev_name = get_sa_devname(sdc->major, sdc->minor, + sdc->wwn, sdc->part_nr, flags); if (a->item_list != NULL) { /* A list of devices has been entered on the command line */ diff --git a/rd_stats.c b/rd_stats.c index 68e2799..2569fae 100644 --- a/rd_stats.c +++ b/rd_stats.c @@ -791,8 +791,9 @@ __nr_t read_diskstats_disk(struct stats_disk *st_disk, __nr_t nr_alloc, char line[1024]; char dev_name[MAX_NAME_LEN]; struct stats_disk *st_disk_i; - unsigned int major, minor, rd_ticks, wr_ticks, dc_ticks, tot_ticks, rq_ticks; + unsigned int major, minor, rd_ticks, wr_ticks, dc_ticks, tot_ticks, rq_ticks, part_nr; unsigned long rd_ios, wr_ios, dc_ios, rd_sec, wr_sec, dc_sec; + unsigned long long wwn[2]; __nr_t dsk_read = 0; if ((fp = fopen(DISKSTATS, "r")) == NULL) @@ -839,6 +840,15 @@ __nr_t read_diskstats_disk(struct stats_disk *st_disk, __nr_t nr_alloc, st_disk_i->dc_ticks = dc_ticks; st_disk_i->tot_ticks = tot_ticks; st_disk_i->rq_ticks = rq_ticks; + + if (get_wwnid_from_pretty(dev_name, wwn, &part_nr) < 0) { + st_disk_i->wwn[0] = 0ULL; + } + else { + st_disk_i->wwn[0] = wwn[0]; + st_disk_i->wwn[1] = wwn[1]; + st_disk_i->part_nr = part_nr; + } } } } diff --git a/rd_stats.h b/rd_stats.h index bc99127..c01d7e2 100644 --- a/rd_stats.h +++ b/rd_stats.h @@ -262,6 +262,7 @@ struct stats_serial { /* Structure for block devices statistics */ struct stats_disk { unsigned long long nr_ios; + unsigned long long wwn[2]; unsigned long rd_sect __attribute__ ((aligned (8))); unsigned long wr_sect __attribute__ ((aligned (8))); unsigned long dc_sect __attribute__ ((aligned (8))); @@ -272,12 +273,13 @@ struct stats_disk { unsigned int major; unsigned int minor; unsigned int dc_ticks; + unsigned int part_nr; }; #define STATS_DISK_SIZE (sizeof(struct stats_disk)) -#define STATS_DISK_ULL 1 +#define STATS_DISK_ULL 3 #define STATS_DISK_UL 3 -#define STATS_DISK_U 7 +#define STATS_DISK_U 8 /* Structure for network interfaces statistics */ struct stats_net_dev { diff --git a/rndr_stats.c b/rndr_stats.c index a6aaa97..9d22590 100644 --- a/rndr_stats.c +++ b/rndr_stats.c @@ -1091,7 +1091,8 @@ __print_funct_t render_disk_stats(struct activity *a, int isdb, char *pre, } /* Get device name */ - dev_name = get_sa_devname(sdc->major, sdc->minor, flags); + dev_name = get_sa_devname(sdc->major, sdc->minor, + sdc->wwn, sdc->part_nr, flags); if (a->item_list != NULL) { /* A list of devices has been entered on the command line */ diff --git a/sa.h b/sa.h index b8776f9..3082485 100644 --- a/sa.h +++ b/sa.h @@ -82,7 +82,8 @@ */ #define S_F_SINCE_BOOT 0x00000001 -#define S_F_SA_ROTAT 0x00000002 +#define S_F_SA_ROTAT 0x00000002 /* Only used by sadc */ +#define S_F_DEV_SID 0x00000002 /* Only used by sar/sadf */ #define S_F_DEV_PRETTY 0x00000004 #define S_F_FORCE_FILE 0x00000008 #define S_F_INTERVAL_SET 0x00000010 @@ -117,6 +118,7 @@ #define WANT_SINCE_BOOT(m) (((m) & S_F_SINCE_BOOT) == S_F_SINCE_BOOT) #define WANT_SA_ROTAT(m) (((m) & S_F_SA_ROTAT) == S_F_SA_ROTAT) +#define USE_STABLE_ID(m) (((m) & S_F_DEV_SID) == S_F_DEV_SID) #define USE_PRETTY_OPTION(m) (((m) & S_F_DEV_PRETTY) == S_F_DEV_PRETTY) #define FORCE_FILE(m) (((m) & S_F_FORCE_FILE) == S_F_FORCE_FILE) #define INTERVAL_SET(m) (((m) & S_F_INTERVAL_SET) == S_F_INTERVAL_SET) @@ -186,34 +188,35 @@ /* Keywords */ #define K_A_NULL "A_NULL" -#define K_XALL "XALL" -#define K_SUM "SUM" +#define K_CPU "CPU" #define K_DEV "DEV" #define K_EDEV "EDEV" -#define K_NFS "NFS" -#define K_NFSD "NFSD" -#define K_SOCK "SOCK" -#define K_IP "IP" -#define K_EIP "EIP" -#define K_ICMP "ICMP" #define K_EICMP "EICMP" -#define K_TCP "TCP" -#define K_ETCP "ETCP" -#define K_UDP "UDP" -#define K_SOCK6 "SOCK6" -#define K_IP6 "IP6" -#define K_EIP6 "EIP6" -#define K_ICMP6 "ICMP6" #define K_EICMP6 "EICMP6" -#define K_UDP6 "UDP6" -#define K_CPU "CPU" +#define K_EIP "EIP" +#define K_EIP6 "EIP6" +#define K_ETCP "ETCP" #define K_FAN "FAN" -#define K_TEMP "TEMP" -#define K_IN "IN" +#define K_FC "FC" #define K_FREQ "FREQ" +#define K_ICMP "ICMP" +#define K_ICMP6 "ICMP6" +#define K_IN "IN" +#define K_IP "IP" +#define K_IP6 "IP6" #define K_MOUNT "MOUNT" -#define K_FC "FC" +#define K_NFS "NFS" +#define K_NFSD "NFSD" +#define K_SID "SID" +#define K_SOCK "SOCK" +#define K_SOCK6 "SOCK6" #define K_SOFT "SOFT" +#define K_SUM "SUM" +#define K_TCP "TCP" +#define K_TEMP "TEMP" +#define K_UDP "UDP" +#define K_UDP6 "UDP6" +#define K_XALL "XALL" #define K_INT "INT" #define K_DISK "DISK" @@ -1372,7 +1375,7 @@ void free_structures char *get_devname (unsigned int, unsigned int, int); char *get_sa_devname - (unsigned int, unsigned int, unsigned int); + (unsigned int, unsigned int, unsigned long long [], unsigned int, unsigned int); void get_file_timestamp_struct (unsigned int, struct tm *, struct file_header *); unsigned long long get_global_cpu_statistics diff --git a/sa_common.c b/sa_common.c index 7bf8aac..36016b3 100644 --- a/sa_common.c +++ b/sa_common.c @@ -2192,6 +2192,11 @@ int parse_sar_opt(char *argv[], int *opt, struct activity *act[], return 1; } (*opt)++; + if (!strcmp(argv[*opt], K_SID)) { + *flags |= S_F_DEV_SID + S_F_DEV_PRETTY; + return 0; + } + if (strnlen(argv[*opt], MAX_FILE_LEN) >= MAX_FILE_LEN - 1) return 1; @@ -3210,15 +3215,20 @@ void get_global_soft_statistics(struct activity *a, int prev, int curr, * IN: * @major Major number of the device. * @minor Minor number of the device. + * @wwn WWN identifier of the device (0 if unknown). + * @part_nr Partition number (0 if unknown). * @flags Flags for common options and system state. * * RETURNS: * The name of the device. *************************************************************************** */ -char *get_sa_devname(unsigned int major, unsigned int minor, unsigned int flags) +char *get_sa_devname(unsigned int major, unsigned int minor, unsigned long long wwn[], + unsigned int part_nr, unsigned int flags) { char *dev_name = NULL, *persist_dev_name = NULL; + static char sid[64]; + char xsid[32] = "", pn[16] = ""; if (DISPLAY_PERSIST_NAME_S(flags)) { persist_dev_name = get_persistent_name_from_pretty(get_devname(major, minor, TRUE)); @@ -3228,7 +3238,17 @@ char *get_sa_devname(unsigned int major, unsigned int minor, unsigned int flags) dev_name = persist_dev_name; } else { - if ((USE_PRETTY_OPTION(flags)) && (major == dm_major)) { + if ((USE_STABLE_ID(flags)) && (wwn[0] != 0)) { + if (wwn[1] != 0) { + sprintf(xsid, "%016llx", wwn[1]); + } + if (part_nr) { + sprintf(pn, "-%d", part_nr); + } + snprintf(sid, sizeof(sid), "%#016llx%s%s", wwn[0], xsid, pn); + dev_name = sid; + } + else if ((USE_PRETTY_OPTION(flags)) && (major == dm_major)) { dev_name = transform_devmapname(major, minor); } diff --git a/sadf_misc.c b/sadf_misc.c index d1abca1..9dc0434 100644 --- a/sadf_misc.c +++ b/sadf_misc.c @@ -1594,7 +1594,8 @@ __nr_t count_new_disk(struct activity *a, int curr) sdc = (struct stats_disk *) ((char *) a->buf[curr] + i * a->msize); nr += add_list_item(&(a->item_list), - get_sa_devname(sdc->major, sdc->minor, flags), + get_sa_devname(sdc->major, sdc->minor, sdc->wwn, + sdc->part_nr, flags), MAX_DEV_LEN); } diff --git a/svg_stats.c b/svg_stats.c index 05482dc..50d0a8a 100644 --- a/svg_stats.c +++ b/svg_stats.c @@ -2091,7 +2091,7 @@ __print_funct_t svg_print_disk_stats(struct activity *a, int curr, int action, s static double *spmin, *spmax; static char **out; static int *outsize; - char *item_name; + char *dev_name, *item_name; double rkB, wkB, dkB, aqusz; int i, j, k, pos, restart, *unregistered; @@ -2100,8 +2100,7 @@ __print_funct_t svg_print_disk_stats(struct activity *a, int curr, int action, s * Allocate arrays (#0..7) that will contain the graphs data * and the min/max values. * Also allocate one additional array (#8) for each disk device: - * spmax + 8 will contain the device major number, - * spmin + 8 will contain the device minor number, + * out + 8 will contain the device name (WWN id, pretty name or devm-n), * outsize + 8 will contain a positive value (TRUE) if the device * has either still not been registered, or has been unregistered. */ @@ -2126,34 +2125,36 @@ __print_funct_t svg_print_disk_stats(struct activity *a, int curr, int action, s for (i = 0; i < a->nr[curr]; i++) { sdc = (struct stats_disk *) ((char *) a->buf[curr] + i * a->msize); - /* Get device name */ - item_name = get_sa_devname(sdc->major, sdc->minor, flags); + /* Get device name */ + dev_name = get_sa_devname(sdc->major, sdc->minor, + sdc->wwn, sdc->part_nr, flags); if (a->item_list != NULL) { /* A list of devices has been entered on the command line */ - if (!search_list_item(a->item_list, item_name)) + if (!search_list_item(a->item_list, dev_name)) /* Device not found */ continue; } /* Look for corresponding graph */ for (k = 0; k < a->item_list_sz; k++) { - if ((sdc->major == *(spmax + k * nr_arrays + 8)) && - (sdc->minor == *(spmin + k * nr_arrays + 8))) + item_name = *(out + k * nr_arrays + 8); + if (!strcmp(dev_name, item_name)) /* Graph found! */ break; } if (k == a->item_list_sz) { /* Graph not found: Look for first free entry */ for (k = 0; k < a->item_list_sz; k++) { - if (*(spmax + k * nr_arrays + 8) == -DBL_MAX) + item_name = *(out + k * nr_arrays + 8); + if (!strcmp(item_name, "")) break; } if (k == a->item_list_sz) { /* No free graph entry: Ignore it (should never happen) */ #ifdef DEBUG fprintf(stderr, "%s: Name=%s major=%d minor=%d\n", - __FUNCTION__, item_name, sdc->major, sdc->minor); + __FUNCTION__, dev_name, sdc->major, sdc->minor); #endif continue; } @@ -2171,10 +2172,11 @@ __print_funct_t svg_print_disk_stats(struct activity *a, int curr, int action, s } *unregistered = FALSE; - if (*(spmax + pos + 8) == -DBL_MAX) { - /* Save device major and minor numbers (if not already done) */ - *(spmax + pos + 8) = sdc->major; - *(spmin + pos + 8) = sdc->minor; + item_name = *(out + pos + 8); + if (!item_name[0]) { + /* Save device name (WWN id or pretty name) if not already done */ + strncpy(item_name, dev_name, CHUNKSIZE); + item_name[CHUNKSIZE - 1] = '\0'; } j = check_disk_reg(a, curr, !curr, i); @@ -2291,9 +2293,7 @@ __print_funct_t svg_print_disk_stats(struct activity *a, int curr, int action, s if (!**(out + pos)) continue; - /* Get device name */ - item_name = get_sa_devname(*(spmax + pos + 8), *(spmin + pos + 8), flags); - + item_name = *(out + pos + 8); if (draw_activity_graphs(a->g_nr, g_type, title, g_title, item_name, group, spmin + pos, spmax + pos, out + pos, outsize + pos, diff --git a/xml_stats.c b/xml_stats.c index a345a2d..1d21d06 100644 --- a/xml_stats.c +++ b/xml_stats.c @@ -718,7 +718,8 @@ __print_funct_t xml_print_disk_stats(struct activity *a, int curr, int tab, } /* Get device name */ - dev_name = get_sa_devname(sdc->major, sdc->minor, flags); + dev_name = get_sa_devname(sdc->major, sdc->minor, + sdc->wwn, sdc->part_nr, flags); if (a->item_list != NULL) { /* A list of devices has been entered on the command line */