Subject: [PATCH] [FEAT LS1502] lsdasd/tunedasd: Add channel path aware erp From: Stefan Haberland Summary: lsdasd/tunedasd: Add channel path aware erp Description: Add tools support for channel path aware error recovery. lsdasd shows the newly added IFCC path mask and HPF availability. tunedasd gets an option to reset channel paths for a device. Upstream-ID: - Problem-ID: LS1502 Signed-off-by: Stefan Haberland --- include/dasd_sys.h | 1 libdasd/dasd_sys.c | 94 +++++++++++++++++++++++++++++++++++++ tunedasd/include/disk.h | 3 - tunedasd/man/tunedasd.8 | 17 ++++++ tunedasd/src/Makefile | 2 tunedasd/src/disk.c | 35 ++++++++++++++ tunedasd/src/tunedasd.c | 120 ++++++++++++++++++++++++++++-------------------- zconf/lsdasd | 36 +++++++++++--- 8 files changed, 251 insertions(+), 57 deletions(-) --- a/include/dasd_sys.h +++ b/include/dasd_sys.h @@ -13,5 +13,6 @@ #include "u2s.h" int dasd_sys_raw_track_access(char *); +int dasd_reset_chpid(char *, char *); #endif /* DASD_SYS_H */ --- a/libdasd/dasd_sys.c +++ b/libdasd/dasd_sys.c @@ -6,6 +6,9 @@ * Copyright IBM Corp. 2016 */ +#include +#include + #include "dasd_sys.h" /** @@ -44,3 +47,94 @@ int dasd_sys_raw_track_access(char *devn return (rc == 1) ? 1 : 0; } + + +int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask) +{ + unsigned int val; + char path[40]; + int count, i; + FILE *fp; + + sprintf(path, "/sys/bus/ccw/devices/%s/../chpids", busid); + *mask = 0; + fp = fopen(path, "r"); + if (!fp) + return ENODEV; + + for (i = 0; i < 8; i++) { + count = fscanf(fp, " %x", &val); + if (count != 1) { + fclose(fp); + return EIO; + } + if (val == chpid) + *mask = 0x80 >> i; + } + fclose(fp); + + return 0; +} + +/** + * reset chpid + * + * The "devnode" parameter can be any valid relative or absolute path to + * a DASD device node, for example: + * + * - /dev/dasda + * - /dev/disk/by-path/ccw-0.0.bf20 + * + * @param[in] devnode Device node of interest + * @param[in] chpid The chpid to reset + * If NULL all chpids will be reset + * + * @return 0 on success, otherwise one of the following error codes: + * - EINVAL No valid chpid specified. + * - ENODEV Could not open device. + * - ENOENT Specified chpid not found. + * - EIO Other I/O error + * + */ +int dasd_reset_chpid(char *devnode, char *chpid_char) +{ + unsigned int chpid; + char path[41]; + char busid[9]; + int mask, rc; + char *endptr; + FILE *fp; + + if (u2s_getbusid(devnode, busid)) + return ENODEV; + + if (!chpid_char) { + sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid); + fp = fopen(path, "w"); + if (!fp) + return ENODEV; + fprintf(fp, "%s", "all\n"); + fclose(fp); + return 0; + } + + errno = 0; + chpid = strtoul(chpid_char, &endptr, 16); + if (errno || (endptr && (*endptr != '\0'))) + return EINVAL; + + rc = dasd_get_pm_from_chpid(busid, chpid, &mask); + if (rc) + return rc; + if (!mask) + return ENOENT; + + sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid); + fp = fopen(path, "w"); + if (!fp) + return ENODEV; + fprintf(fp, "%02x", mask); + fclose(fp); + + return 0; +} --- a/tunedasd/include/disk.h +++ b/tunedasd/include/disk.h @@ -27,7 +27,8 @@ int disk_release (char* device); int disk_slock (char* device); int disk_query_reserve_status(char* device); int disk_profile (char* device, char* prof_item); -int disk_reset_prof (char* device); +int disk_reset_prof(char *device); +int disk_reset_chpid(char *device, char *chpid); #endif /* not DISK_H */ --- a/tunedasd/man/tunedasd.8 +++ b/tunedasd/man/tunedasd.8 @@ -149,6 +149,18 @@ Following rows are supported: .TP .BR "\-R" " or " "\-\-reset_prof" Reset profile info of device. +.TP +.BR "\-p" " or " "\-\-path_reset " +Reset a channel path of a selected device. A channel path +might be suspended due to high IFCC error rates or a High Performance +FICON failure. Use this option to resume considering the channel path +for I/O. +.TP +.BR "\-\-path_reset_all" +Reset all channel paths of the selected device. The channel paths +might be suspended due to high IFCC error rates or a High Performance +FICON failure. Use this option to resume considering all defined +channel paths for I/O. .\" .\".TP .\".BR "\-o" " or " "\-\-online" @@ -166,8 +178,13 @@ Reset profile info of device. .br tunedasd -c prestage -n 1 /dev/dasdc + +.br +3. Scenario: Reset failed channel path with CHPID 45 .br + tunedasd -p 45 /dev/dasdc +.br .SH "SEE ALSO" .BR dasdview (8), .BR dasdfmt (8), --- a/tunedasd/src/Makefile +++ b/tunedasd/src/Makefile @@ -5,7 +5,7 @@ includes = $(wildcard ../include/*.h) all: tunedasd -objects = tunedasd.o disk.o +objects = tunedasd.o disk.o ../../libdasd/dasd_sys.o ../../libu2s/u2s.o $(objects): $(includes) tunedasd: $(objects) --- a/tunedasd/src/disk.c +++ b/tunedasd/src/disk.c @@ -9,6 +9,7 @@ #include "disk.h" #include "tunedasd.h" +#include "dasd_sys.h" #include #include @@ -682,3 +683,37 @@ disk_reset_prof (char* device) return 0; } +int disk_reset_chpid(char *device, char *chpid) +{ + int rc; + + if (chpid) + printf("Resetting chpid %s for device <%s>...\n", chpid, + device); + else + printf("Resetting all chpids for device <%s>...\n", device); + + rc = dasd_reset_chpid(device, chpid); + switch (rc) { + case 0: + printf("Done.\n"); + return 0; + case ENODEV: + error_print("%s: %s", device, strerror(errno)); + break; + case EINVAL: + error_print("%s: Could not reset chpid %s: Invalid CHPID", + device, chpid); + break; + case ENOENT: + error_print("%s: Could not reset chpid %s: CHPID not defined for device", + device, chpid); + break; + default: + error_print("%s: Could not reset chpid %s", + device, chpid); + break; + } + + return -1; +} --- a/tunedasd/src/tunedasd.c +++ b/tunedasd/src/tunedasd.c @@ -34,28 +34,30 @@ static const char* usage_text[] = { "(e.g. '/dev/dasda') or a list of devices separated by a space " "character.", "", - "-h, --help Print this help, then exit", - "-v, --version Print version information, then exit", - "-g, --get_cache Get current storage server caching behaviour", - "-c, --cache Define caching behavior on storage server", - " (normal/bypass/inhibit/sequential/prestage/" - "record)", - "-n, --no_cyl Number of cylinders to be cached ", - " (only valid together with --cache)", - "-S, --reserve Reserve device", - "-L, --release Release device", - "-O, --slock Unconditional reserve device", - " Note: Use with care, this breaks an existing " - "lock", - "-Q, --query_reserve Print reserve status of device ", - "-P, --profile Print profile info of device", - "-I, --prof_item Print single profile item", - " (reqs/sects/sizes/total/totsect/start/irq/", - " irqsect/end/queue)", - "-R, --reset_prof Reset profile info of device" + "-h, --help Print this help, then exit", + "-v, --version Print version information, then exit", + "-g, --get_cache Get current storage server caching behaviour", + "-c, --cache Define caching behavior on storage server", + " (normal/bypass/inhibit/sequential/prestage/" + "record)", + "-n, --no_cyl Number of cylinders to be cached ", + " (only valid together with --cache)", + "-S, --reserve Reserve device", + "-L, --release Release device", + "-O, --slock Unconditional reserve device", + " Note: Use with care, this breaks an existing " + "lock", + "-Q, --query_reserve Print reserve status of device ", + "-P, --profile Print profile info of device", + "-I, --prof_item Print single profile item", + " (reqs/sects/sizes/total/totsect/start/irq/", + " irqsect/end/queue)", + "-R, --reset_prof Reset profile info of device", + "-p, --path_reset Reset channel path of a device", + " --path_reset_all Reset all channel paths of a device" }; -#define CMD_KEYWORD_NUM 12 +#define CMD_KEYWORD_NUM 14 #define DEVICES_NUM 256 enum cmd_keyword_id { @@ -71,6 +73,8 @@ enum cmd_keyword_id { cmd_keyword_prof_item = 9, cmd_keyword_reset_prof = 10, cmd_keyword_query_reserve = 11, + cmd_keyword_path = 12, + cmd_keyword_path_all = 13, }; @@ -79,18 +83,20 @@ static const struct { char* keyword; enum cmd_keyword_id id; } keyword_list[] = { - { "help", cmd_keyword_help }, - { "version", cmd_keyword_version }, - { "get_cache", cmd_keyword_get_cache }, - { "cache", cmd_keyword_cache }, - { "no_cyl", cmd_keyword_no_cyl }, - { "reserve", cmd_keyword_reserve }, - { "release", cmd_keyword_release }, - { "slock", cmd_keyword_slock }, - { "profile", cmd_keyword_profile }, - { "prof_item", cmd_keyword_prof_item }, - { "reset_prof", cmd_keyword_reset_prof }, - { "query_reserve", cmd_keyword_query_reserve } + { "help", cmd_keyword_help }, + { "version", cmd_keyword_version }, + { "get_cache", cmd_keyword_get_cache }, + { "cache", cmd_keyword_cache }, + { "no_cyl", cmd_keyword_no_cyl }, + { "reserve", cmd_keyword_reserve }, + { "release", cmd_keyword_release }, + { "slock", cmd_keyword_slock }, + { "profile", cmd_keyword_profile }, + { "prof_item", cmd_keyword_prof_item }, + { "reset_prof", cmd_keyword_reset_prof }, + { "query_reserve", cmd_keyword_query_reserve }, + { "path_reset", cmd_keyword_path }, + { "path_reset_all", cmd_keyword_path_all } }; @@ -103,22 +109,24 @@ enum cmd_key_state { /* Determines which combination of keywords are valid */ enum cmd_key_state cmd_key_table[CMD_KEYWORD_NUM][CMD_KEYWORD_NUM] = { - /* help vers get_ cach no_c rese rele sloc prof prof rese quer - * ion cach e yl rve ase k ile _ite t_pr y_re + /* help vers get_ cach no_c rese rele sloc prof prof rese quer path path + * ion cach e yl rve ase k ile _ite t_pr y_re _all * e m of serv */ - /* help */ { req, opt, opt, opt, opt, opt, opt, opt, opt, opt, opt, inv }, - /* version */ { inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv }, - /* get_cache */ { opt, opt, req, inv, inv, inv, inv, inv, inv, inv, inv, inv }, - /* cache */ { opt, opt, inv, req, opt, inv, inv, inv, inv, inv, inv, inv }, - /* no_cyl */ { opt, opt, inv, req, req, inv, inv, inv, inv, inv, inv, inv }, - /* reserve */ { opt, opt, inv, inv, inv, req, inv, inv, inv, inv, inv, inv }, - /* release */ { opt, opt, inv, inv, inv, inv, req, inv, inv, inv, inv, inv }, - /* slock */ { opt, opt, inv, inv, inv, inv, inv, req, inv, inv, inv, inv }, - /* profile */ { opt, opt, inv, inv, inv, inv, inv, inv, req, opt, inv, inv }, - /* prof_item */ { opt, opt, inv, inv, inv, inv, inv, inv, req, req, inv, inv }, - /* reset_prof */ { opt, opt, inv, inv, inv, inv, inv, inv, inv, inv, req, inv }, - /* query_reserve */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req }, + /* help */ { req, opt, opt, opt, opt, opt, opt, opt, opt, opt, opt, inv, inv, inv }, + /* version */ { inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv }, + /* get_cache */ { opt, opt, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv }, + /* cache */ { opt, opt, inv, req, opt, inv, inv, inv, inv, inv, inv, inv, inv, inv }, + /* no_cyl */ { opt, opt, inv, req, req, inv, inv, inv, inv, inv, inv, inv, inv, inv }, + /* reserve */ { opt, opt, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv }, + /* release */ { opt, opt, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv }, + /* slock */ { opt, opt, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv }, + /* profile */ { opt, opt, inv, inv, inv, inv, inv, inv, req, opt, inv, inv, inv, inv }, + /* prof_item */ { opt, opt, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv }, + /* reset_prof */ { opt, opt, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv }, + /* query_reserve */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv }, + /* path */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv }, + /* path_all */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req }, }; struct parameter { @@ -146,11 +154,13 @@ static struct option options[] = { { "prof_item", required_argument, NULL, 'I'}, { "reset_prof", no_argument, NULL, 'R'}, { "query_reserve", no_argument, NULL, 'Q'}, + { "path_reset", required_argument, NULL, 'p'}, + { "path_reset_all", no_argument, NULL, 'A'}, { NULL, 0, NULL, 0 } }; /* Command line option abbreviations */ -static const char option_string[] = "hvgc:n:SLOPI:RQ"; +static const char option_string[] = "hvgc:n:SLOPI:RQp:"; /* Error message string */ @@ -348,6 +358,14 @@ get_command_line (int argc, char* argv[] optarg); } break; + case 'p': + rc = store_option(&cmdline, cmd_keyword_path, + optarg); + break; + case 'A': + rc = store_option(&cmdline, cmd_keyword_path_all, + optarg); + break; case 'S': rc = store_option (&cmdline, cmd_keyword_reserve, optarg); @@ -400,7 +418,6 @@ get_command_line (int argc, char* argv[] return 0; } - /* * Execute the command. */ @@ -444,6 +461,13 @@ do_command (char* device, struct command case cmd_keyword_query_reserve: rc = disk_query_reserve_status(device); break; + case cmd_keyword_path: + rc = disk_reset_chpid(device, + cmdline.parm[cmd_keyword_path].data); + break; + case cmd_keyword_path_all: + rc = disk_reset_chpid(device, NULL); + break; default: error_print ("Unknown command '%s' specified", get_keyword_name (i)); --- a/zconf/lsdasd +++ b/zconf/lsdasd @@ -368,13 +368,15 @@ function extended() CABLEPM=0 CUIRPM=0 HPFPM=0 + IFCCPM=0 # additional information read DIAG 2> /dev/null < $DEVPATH/use_diag || continue read EER 2> /dev/null < $DEVPATH/eer_enabled || continue read ERP 2> /dev/null < $DEVPATH/erplog || continue + read HPF 2> /dev/null < $DEVPATH/hpf # in case the path_masks do not exist simply ignore it - read OPM NPPM CABLEPM CUIRPM HPFPM 2> /dev/null < $DEVPATH/path_masks + read OPM NPPM CABLEPM CUIRPM HPFPM IFCCPM 2> /dev/null < $DEVPATH/path_masks read -a C 2> /dev/null < $DEVPATH/../chpids || continue read PIM PAM POM 2> /dev/null < $DEVPATH/../pimpampom || continue @@ -385,6 +387,7 @@ function extended() CABLEPM=0x$CABLEPM CUIRPM=0x$CUIRPM HPFPM=0x$HPFPM + IFCCPM=0x$IFCCPM #-----------------------------------------------------------# # aggregate chpids and path mask to useful information # @@ -397,6 +400,7 @@ function extended() CUIR_PATHS=(" " " " " " " " " " " " " " " ") CABLE_PATHS=(" " " " " " " " " " " " " " " ") HPF_PATHS=(" " " " " " " " " " " " " " " ") + IFCC_PATHS=(" " " " " " " " " " " " " " " ") # installed paths j=0 @@ -470,13 +474,25 @@ function extended() (( mask>>=1 )) done + # IFCC unusable paths + j=0 + mask=0x80 + for (( i=0; i<8; i++ )) ;do + PM=$(($IFCCPM&$mask)) + if [ $PM -gt 0 ] ;then + IFCC_PATHS[j]=${C[$i]} ; + ((j++)) ; + fi + (( mask>>=1 )) + done + #-------------------------------------------# # format data for output # #-------------------------------------------# if [[ "$ONLINE" == 0 ]]; then ACTIVE="offline" - printf "%s:%s:%s# status:\t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s#\n" \ + printf "%s:%s:%s# status:\t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ "$SORTKEYLEN" "$SORTKEY" \ "$BUSID" \ "$ACTIVE" \ @@ -484,18 +500,20 @@ function extended() "$READONLY" \ "$EER" \ "$ERP" \ + "$HPF" \ "$DEV_UID" \ "${INSTALLED_PATHS[@]}" \ "${USED_PATHS[@]}" \ "${NP_PATHS[@]}" \ "${CABLE_PATHS[@]}" \ "${CUIR_PATHS[@]}" \ - "${HPF_PATHS[@]}" ; + "${HPF_PATHS[@]}" \ + "${IFCC_PATHS[@]}" ; continue elif [[ "$ALIAS" == 1 ]]; then if [[ "$BASEONLY" == "false" ]]; then ACTIVE="alias" - printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s # uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s#\n" \ + printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ "$SORTKEYLEN" "$SORTKEY" \ "$BUSID" \ "$ACTIVE" \ @@ -504,13 +522,15 @@ function extended() "$READONLY" \ "$EER" \ "$ERP" \ + "$HPF" \ "$DEV_UID" \ "${INSTALLED_PATHS[@]}" \ "${USED_PATHS[@]}" \ "${NP_PATHS[@]}" \ "${CABLE_PATHS[@]}" \ "${CUIR_PATHS[@]}" \ - "${HPF_PATHS[@]}" ; + "${HPF_PATHS[@]}" \ + "${IFCC_PATHS[@]}" ; continue else continue @@ -533,7 +553,7 @@ function extended() COLON=":" fi - printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s#\n" \ + printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ "$SORTKEYLEN" "$SORTKEY" \ "$BUSID" \ "$BLOCKNAME" \ @@ -549,13 +569,15 @@ function extended() "$READONLY" \ "$EER" \ "$ERP" \ + "$HPF" \ "$DEV_UID" \ "${INSTALLED_PATHS[@]}" \ "${USED_PATHS[@]}" \ "${NP_PATHS[@]}" \ "${CABLE_PATHS[@]}" \ "${CUIR_PATHS[@]}" \ - "${HPF_PATHS[@]}" ; + "${HPF_PATHS[@]}" \ + "${IFCC_PATHS[@]}" ; } function host()