Subject: [PATCH] [FEAT LS1213] dasd: add query host access to volume support From: Stefan Haberland Summary: dasd: add query host access to volume support Description: With this feature, applications can query if a DASD volume is online to another operating system instances by checking the online status of all attached hosts from the storage server. Upstream-ID: - Problem-ID: LS1213 Signed-off-by: Stefan Haberland --- dasdfmt/dasdfmt.8 | 7 ++ dasdfmt/dasdfmt.c | 38 ++++++++++++-- dasdfmt/dasdfmt.h | 4 + fdasd/fdasd.8 | 9 ++- fdasd/fdasd.c | 32 ++++++++++++ fdasd/fdasd.h | 28 +++++----- include/libzds.h | 1 include/u2s.h | 5 + libu2s/u2s.c | 43 ++++++++++++++++ libzds/Makefile | 2 libzds/libzds.c | 46 +++++++++++++++-- zconf/lsdasd | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++- zconf/lsdasd.8 | 5 + zdsfs/zdsfs.1 | 4 + zdsfs/zdsfs.c | 18 ++++++ 15 files changed, 345 insertions(+), 36 deletions(-) --- a/dasdfmt/dasdfmt.8 +++ b/dasdfmt/dasdfmt.8 @@ -7,7 +7,7 @@ dasdfmt \- formatting of DASD (ECKD) dis .br [-r \fIcylinder\fR] [-b \fIblksize\fR] [-l \fIvolser\fR] [-d \fIlayout\fR] .br - [-L] [-V] [-F] [-k] \fIdevice\fR + [-L] [-V] [-F] [-k] [-C] \fIdevice\fR .SH DESCRIPTION \fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it @@ -70,6 +70,11 @@ Print version number and exit. Formats the device without checking, if the device is in use. .TP +\fB-C\fR or \fB--check_host_count\fR +Force dasdfmt to check the host access open count to ensure the device +is not online on another operating system instance + +.TP \fB-d\fR \fIlayout\fR or \fB--disk_layout\fR=\fIlayout\fR Formats the device with compatible disk layout or linux disk layout. \fIlayout\fR is either \fIcdl\fR for the compatible disk layout --- a/dasdfmt/dasdfmt.c +++ b/dasdfmt/dasdfmt.c @@ -18,6 +18,8 @@ #include "util_proc.h" #include "dasd_sys.h" +#define BUSIDSIZE 8 + /* Full tool name */ static const char tool_name[] = "dasdfmt: zSeries DASD format program"; @@ -46,7 +48,7 @@ print_version (void) */ static void exit_usage(int exitcode) { - printf("Usage: %s [-htvypPLVFk]\n" + printf("Usage: %s [-htvypPLVFkC]\n" " [-l | --label=]\n" " [-b | --blocksize=]\n" " [-d | --disk_layout=]\n" @@ -65,6 +67,9 @@ static void exit_usage(int exitcode) " -v means verbose mode\n" " -F means don't check if the device is in use\n" " -k means keep volume serial\n" + " -C or --check_host_count means force dasdfmt to check\n" + " the host access open count to ensure the device\n" + " is not online on another operating system instance\n" " --norecordzero prevent storage server from modifying" " record 0\n\n" " is the volume identifier, which is converted\n" @@ -190,7 +195,6 @@ static void get_device_name(dasdfmt_info } } - /* * initialize the dasdfmt info structure */ @@ -214,6 +218,7 @@ static void init_info(dasdfmt_info_t *in info->node_specified = 0; info->device_id = 0; info->keep_volser = 0; + info->force_host = 0; } @@ -776,6 +781,7 @@ static void do_format_dasd(dasdfmt_info_ dasd_information_t dasd_info; struct dasd_eckd_characteristics *characteristics; unsigned int cylinders, heads; + int count; if (info->verbosity > 0) printf("Retrieving disk geometry...\n"); @@ -835,6 +841,27 @@ static void do_format_dasd(dasdfmt_info_ if ((info->verbosity > 0) || (!info->withoutprompt)) dasdfmt_print_info(info, vlabel, cylinders, heads, p); + count = u2s_get_host_access_count(info->devname); + if (info->force_host) { + if (count > 1) { + ERRMSG_EXIT(EXIT_FAILURE, + "\n%s: Disk %s is online on OS instances in %d different LPARs.\n" + "Note: Your installation might include z/VM systems that are configured to\n" + "automatically vary on disks, regardless of whether they are subsequently used.\n\n", + prog_name, info->devname, count); + } else if (count < 0) { + ERRMSG("\nHosts access information not available for disk %s.\n\n", + info->devname); + return; + } + } else if (count > 1) + ERRMSG("\nWARNING:\n" + "Disk %s is online on operating system instances in %d different LPARs.\n" + "Ensure that the disk is not being used by a system outside your LPAR.\n" + "Note: Your installation might include z/VM systems that are configured to\n" + "automatically vary on disks, regardless of whether they are subsequently used.\n", + info->devname, count); + if (!info->testmode) { if (!info->withoutprompt) { printf("\n--->> ATTENTION! <<---\n"); @@ -916,10 +943,6 @@ int main(int argc,char *argv[]) info.force=1; break; - case 'C': - format_params.intensity |= DASD_FMT_INT_COMPAT; - break; - case 'd' : if (strncmp(optarg,"cdl",3)==0) { @@ -1017,6 +1040,9 @@ int main(int argc,char *argv[]) case 'k' : info.keep_volser=1; break; + case 'C': + info.force_host = 1; + break; case -1: /* End of options string - start of devices list */ info.device_id = optind; --- a/dasdfmt/dasdfmt.h +++ b/dasdfmt/dasdfmt.h @@ -214,7 +214,7 @@ typedef struct format_data_t { if (*endptr) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ "is in invalid format\n",prog_name);} -#define dasdfmt_getopt_string "b:n:l:f:d:m:r:hpPLtyvVFk" +#define dasdfmt_getopt_string "b:n:l:f:d:m:r:hpPLtyvVFkC" static struct option dasdfmt_getopt_long_options[]= { @@ -233,6 +233,7 @@ static struct option dasdfmt_getopt_long { "help", 0, 0, 'h'}, { "keep_volser", 0, 0, 'k'}, { "norecordzero", 0, 0, 'z'}, + { "check_host_count", 0, 0, 'C'}, {0, 0, 0, 0} }; @@ -265,6 +266,7 @@ typedef struct dasdfmt_info { int node_specified; int device_id; int keep_volser; + int force_host; } dasdfmt_info_t; --- a/fdasd/fdasd.8 +++ b/fdasd/fdasd.8 @@ -4,11 +4,11 @@ fdasd \- partitioning tool. .SH SYNOPSIS interactive mode: .br - \fBfdasd\fR [-s] [-r] \fIdevice\fR + \fBfdasd\fR [-s] [-r] [-C] \fIdevice\fR .br command line mode: .br - \fBfdasd\fR [-s] [-r] {-a[-k|-l \fIvolser\fR]|-i|-p|-c \fIconf_file\fR} + \fBfdasd\fR [-s] [-r] [-C] {-a[-k|-l \fIvolser\fR]|-i|-p|-c \fIconf_file\fR} [-f \fI[type,blocksize]\fR] \fIdevice\fR .br help: @@ -131,6 +131,11 @@ In combination with the -s option fdasd partition table. .TP +\fB-C\fR or \fB--check_host_count\fR +Force fdasd to check the host access open count to ensure the device +is not online on another operating system instance + +.TP \fB-f\fR \fI[type,blocksize]\fR or \fB--force\fR \fI[type,blocksize]\fR Force fdasd to work on non DASD devices. .br --- a/fdasd/fdasd.c +++ b/fdasd/fdasd.c @@ -369,7 +369,10 @@ fdasd_usage (void) " found in CONFIGFILE\n" "-i, --volser Print volume serial\n" "-p, --table Print partition table\n" - "-f, --force Force fdasd to work on non DASD devices\n"); + "-f, --force Force fdasd to work on non DASD devices\n" + "-C, --check_host_count Force fdasd to check the host access\n" + " open count to ensure the device is not\n" + " online on another operating system instance\n"); } @@ -572,6 +575,9 @@ fdasd_parse_options (fdasd_anchor_t *anc anc->force_virtual++; fdasd_parse_force_options(anc, optarg); break; + case 'C': + anc->force_host++; + break; case -1: /* End of options string - start of devices list */ break; @@ -824,6 +830,7 @@ fdasd_verify_device (fdasd_anchor_t *anc { struct stat dst; char err_str[ERROR_STRING_SIZE]; + int count; if ((stat(name, &dst)) < 0 ) { snprintf(err_str, ERROR_STRING_SIZE, @@ -856,6 +863,29 @@ fdasd_verify_device (fdasd_anchor_t *anc fdasd_error(anc, device_verification_failed, err_str); } + count = u2s_get_host_access_count(name); + if (anc->force_host) { + if (count > 1) { + snprintf(err_str, ERROR_STRING_SIZE, + "Disk %s is online on operating system instances in %d different LPARs.\n" + "Note: Your installation might include z/VM systems that are configured to\n" + "automatically vary on disks, regardless of whether they are subsequently used.\n", + name, count); + fdasd_error(anc, device_verification_failed, err_str); + } else if (count < 0) { + snprintf(err_str, ERROR_STRING_SIZE, + "Hosts access information not available for disk %s.\n", + name); + fdasd_error(anc, device_verification_failed, err_str); + } + } else if (count > 1) + printf("\nWARNING:\n" + "Disk %s is online on operating system instances in %d different LPARs.\n" + "Ensure that the disk is not being used by a system outside your LPAR.\n" + "Note: Your installation might include z/VM systems that are configured to\n" + "automatically vary on disks, regardless of whether they are subsequently used.\n\n", + name, count); + if (anc->verbose) printf("Verification successful for '%s' (%d/%d)\n", name, (unsigned short) major(dst.st_rdev), --- a/fdasd/fdasd.h +++ b/fdasd/fdasd.h @@ -157,22 +157,23 @@ struct dasd_eckd_characteristics { #define PARTITION_GPFS 5 static struct option fdasd_long_options[] = { - { "version", no_argument, NULL, 'v'}, - { "auto", no_argument, NULL, 'a'}, - { "silent", no_argument, NULL, 's'}, - { "verbose", no_argument, NULL, 'r'}, - { "label", required_argument, NULL, 'l'}, - { "config", required_argument, NULL, 'c'}, - { "help", no_argument, NULL, 'h'}, - { "table", no_argument, NULL, 'p'}, - { "volser", no_argument, NULL, 'i'}, - { "keep_volser", no_argument, NULL, 'k'}, - { "force", optional_argument, NULL, 'f'}, - { 0, 0, 0, 0 } + { "version", no_argument, NULL, 'v'}, + { "auto", no_argument, NULL, 'a'}, + { "silent", no_argument, NULL, 's'}, + { "verbose", no_argument, NULL, 'r'}, + { "label", required_argument, NULL, 'l'}, + { "config", required_argument, NULL, 'c'}, + { "help", no_argument, NULL, 'h'}, + { "table", no_argument, NULL, 'p'}, + { "volser", no_argument, NULL, 'i'}, + { "keep_volser", no_argument, NULL, 'k'}, + { "force", optional_argument, NULL, 'f'}, + { "check_host_count", no_argument, NULL, 'C'}, + { 0, 0, 0, 0 } }; /* Command line option abbreviations */ -static const char option_string[] = "vasrl:c:hpikf::"; +static const char option_string[] = "vasrl:c:hpikf::C"; struct fdasd_options { char *device; @@ -214,6 +215,7 @@ typedef struct fdasd_anchor { int print_volser; int keep_volser; int force_virtual; + int force_host; int big_disk; int silent; int verbose; --- a/include/libzds.h +++ b/include/libzds.h @@ -803,6 +803,7 @@ int lzds_zdsroot_extract_datasets_from_d void lzds_DS1RECFM_to_recfm(char DS1RECFM, char *buffer); +int lzds_analyse_open_count(struct zdsroot *root, int warn); /** @} */ /* end of group libzds_functions_helper */ --- a/include/u2s.h +++ b/include/u2s.h @@ -13,7 +13,8 @@ #define U2S_BUS_ID_SIZE 32 -int -u2s_getbusid(char * devicenode, char * busid); +int u2s_getbusid(char *, char *); +int u2s_read_attribute(char *, char *, char *, size_t); +int u2s_get_host_access_count(char *); #endif /* U2S_H */ --- a/libu2s/u2s.c +++ b/libu2s/u2s.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "u2s.h" @@ -282,3 +283,45 @@ int u2s_getbusid(char *devicenode, char return rc; } + +/* + * Attempts to find the sysfs entry for the given busid and reads + * the contents of a specified attribute to the buffer + */ +int u2s_read_attribute(char *busid, char *attribute, char *buffer, + size_t count) +{ + char path[100]; + int rc, fd; + ssize_t rcount; + + rc = 0; + snprintf(path, sizeof(path), "/sys/bus/ccw/devices/%s/%s", + busid, attribute); + fd = open(path, O_RDONLY); + if (fd < 0) + return errno; + rcount = read(fd, buffer, count); + if (rcount < 0) + rc = errno; + close(fd); + return rc; +} + +int u2s_get_host_access_count(char *devicenode) +{ + char busid[BUSIDSIZE]; + unsigned long value; + char buffer[10]; + char *endp; + + u2s_getbusid(devicenode, busid); + u2s_read_attribute(busid, "host_access_count", buffer, sizeof(buffer)); + + value = strtoul(buffer, &endp, 0); + + if (endp == buffer) + return -EINVAL; + + return value; +} --- a/libzds/Makefile +++ b/libzds/Makefile @@ -8,7 +8,7 @@ CFLAGS += -D_FILE_OFFSET_BITS=64 all: libzds.a -libzds.a: libzds.o $(rootdir)/libutil/util_list.o $(rootdir)/libvtoc/vtoc.o +libzds.a: libzds.o $(rootdir)/libutil/util_list.o $(rootdir)/libvtoc/vtoc.o $(rootdir)/libu2s/u2s.o libzds.o: ../include/libzds.h --- a/libzds/libzds.c +++ b/libzds/libzds.c @@ -29,6 +29,7 @@ #include "libzds.h" #include "util.h" +#include "u2s.h" #include #include @@ -72,6 +73,8 @@ struct errorlog { */ #define ERRORMSG 240 +#define BUSIDSIZE 8 + /** * @brief An internal structure that represents an entry in the error log. */ @@ -3760,15 +3763,46 @@ void lzds_DS1RECFM_to_recfm(char DS1RECF */ } +int lzds_analyse_open_count(struct zdsroot *root, int warn) +{ + struct dasd *dasd; + int value; + int rc = 0; + util_list_iterate(root->dasdlist, dasd) { + value = u2s_get_host_access_count(dasd->device); + if (value < 0) { + fprintf(stderr, + "Hosts access information not available for disk %s.\n", + dasd->device); + rc = value; + continue; + } + if (value == 1) + continue; + if (warn) + fprintf(stderr, + "\nWARNING:\n" + "Disk %s is online on operating system instances in %d different LPARs.\n" + "Ensure that the disk is not being used by a system outside your LPAR.\n" + "Note: Your installation might include z/VM systems that are configured to\n" + "automatically vary on disks, regardless of whether they are subsequently used.\n", + dasd->device, value); + else { + fprintf(stderr, + "\nERROR:\n" + "Disk %s is online on operating system instances in %d different LPARs.\n" + "Ensure that the disk is not being used by a system outside your LPAR.\n" + "Note: Your installation might include z/VM systems that are configured to\n" + "automatically vary on disks, regardless of whether they are subsequently used.\n", + dasd->device, value); + rc = -EACCES; + } + } - - - - - - + return rc; +} --- a/zconf/lsdasd +++ b/zconf/lsdasd @@ -30,6 +30,8 @@ function PrintUsage() { Print old version of lsdasd output. -l|--long Print extended information about DASDs. + -H|--host-access-list + Print information about hosts accessing DASDs. -v|--verbose For compatibility/future use. Currently ignored. --version @@ -115,6 +117,33 @@ function listDASDDeviceDirectories() { } #------------------------------------------------------------------------------ +# find dasd directory in debugfs +#------------------------------------------------------------------------------ +function findDASDDebugfsDirectorie() { + local mntentries + + while read -a mntentries + do + if [[ "${mntentries[2]}" == "debugfs" ]] + then + DASD_DBF_DIR="${mntentries[1]}" + break; + fi + done < /etc/mtab + if [[ "$DASD_DBF_DIR" == "" ]] + then + echo "$CMD: No debugfs mount point found" >&2 + exit 1 + fi + DASD_DBF_DIR="$DASD_DBF_DIR/dasd" + if [[ ! -d "$DASD_DBF_DIR" ]] + then + echo "$CMD: Default DASD debugfs directory $DASD_DBF_DIR does not exist" >&2 + exit 1 + fi +} + +#------------------------------------------------------------------------------ # gather device data and call appropriate output function #------------------------------------------------------------------------------ function gatherDeviceData() { @@ -197,6 +226,8 @@ function gatherDeviceData() { extended elif [[ "$PRINTUID" == "true" ]]; then uid + elif [[ "$OUTPUT" == "host" ]]; then + host else newoutput fi @@ -527,6 +558,104 @@ function extended() "${HPF_PATHS[@]}" ; } +function host() +{ +findDASDDebugfsDirectorie + +if [[ ! -f "$DASD_DBF_DIR/$BUSID/host_access_list" ]] +then + printf "\n%s: hosts access information not available\n" "$BUSID" + return +fi + +local temp=`mktemp /tmp/lsdasd.XXXXXX` +if test -w $temp ; then :; else + printf "\nCreating temporary file failed\n" + return +fi + +cat $DASD_DBF_DIR/$BUSID/host_access_list > $temp 2> /dev/null +ret=$? +if [[ $ret -ne 0 ]] +then + printf "%s: hosts access information not available\n" "$BUSID" + rm -f $temp + return $ret +fi + +unset index +unset array +declare -a array + +index=(pgid status_flags sysplex_name supported_cylinder timestamp) + +for element in ${index[@]} +do + count=0 + + declare -a $element + OLDIFS=$IFS + IFS=$'\n' + for value in `grep $element $temp` + do + (( ++count )) + value=$(echo -e $value | cut -d ' ' -f2) + eval $element[$count]=$value + done + IFS=$OLDIFS +done + +printf "Host information for %s\n" "$BUSID"; +printf "Path-Group-ID LPAR CPU FL Status Sysplex Max_Cyls Time\n"; +printf "================================================================================\n"; + +# mask bits for online and reserved state +online_reserved_mask=0xE0 + +# print name value lists +for i in `seq 1 $count`; +do + # get flags field + value=${status_flags[$i]} + # mark as hex value + value=0x$value + # mask online and reserved bits + value=$(($value & $online_reserved_mask)) + + case $value in + 0 ) # 0x00 + STATE="OFF" + ;; + 32 ) # 0x20 + STATE="OFF-RSV" + ;; + 64 ) # 0x40 + STATE="ON" + ;; + 96 ) # 0x60 + STATE="ON-RSV" + ;; + * ) + STATE="-" + ;; + esac + + printf "%22s %02s %07s %02s %-6s %-8s %11u %10lu\n" \ + "${pgid[$i]}" \ + "${pgid[$i]:4:2}" \ + "${pgid[$i]:6:4}" \ + "${status_flags[$i]}" \ + "$STATE" \ + "${sysplex_name[$i]}" \ + "${supported_cylinder[$i]}" \ + "${timestamp[$i]}" \ + ; +done +printf "\n"; + +rm -f $temp +} + function uid() { #-------------------------------------------# @@ -586,6 +715,9 @@ while [ $# -gt 0 ]; do --long|-l) OUTPUT="extended" ;; + --host-access-list|-H) + OUTPUT="host" + ;; --version) PrintVersion exit 0 @@ -626,8 +758,13 @@ fi # gather information on devices in list PROCESSING=" $PROCESSING | gatherDeviceData " + # sort resulting list -PROCESSING=" $PROCESSING | sort -t: -k1n -k2 | cut -d: -f3- " +if [[ "$OUTPUT" == "host" ]]; then + PROCESSING=" $PROCESSING" +else + PROCESSING=" $PROCESSING | sort -t: -k1n -k2 | cut -d: -f3- " +fi if [[ "$PRINTUID" == "true" ]] && [[ "$OUTPUT" != "old" ]]; then printf "Bus-ID Name UID\n" --- a/zconf/lsdasd.8 +++ b/zconf/lsdasd.8 @@ -40,11 +40,14 @@ Include only base devices. Old output of lsdasd for compatibility. .TP .BR -l | --long -Extended output of lsdasd including UID, attributes and path information. +Extended output of lsdasd including UID and attributes. .TP .BR -u | --uid Output includes and is sorted by UID. .TP +.BR -H | --host-acces +Show information about all hosts using this device. +.TP .BR -v | --verbose Only for compatibility (and maybe future) use. This option currently does nothing. --- a/zdsfs/zdsfs.1 +++ b/zdsfs/zdsfs.1 @@ -150,6 +150,10 @@ If \fI\fR is set to 0, no seek histor case `seek' is still supported, but a `seek' operation might result in a read from the beginning of the data set. +.TP +\fB\-o\fR check_host_count +Stop processing if the device is used by another operating system instance. + .SS "Applicable FUSE options (version 2.8):" This is a selected subset of all FUSE options. Use the zdsfs \fB\--help\fR option to print a full list. --- a/zdsfs/zdsfs.c +++ b/zdsfs/zdsfs.c @@ -39,6 +39,7 @@ struct zdsfs_info { int devcount; int allow_inclomplete_multi_volume; int keepRDW; + int host_count; unsigned int tracks_per_frame; unsigned long long seek_buffer_size; struct zdsroot *zdsroot; @@ -765,6 +766,7 @@ static const struct fuse_opt zdsfs_opts[ FUSE_OPT_KEY("seekbuffer=", KEY_SEEKBUFFER), ZDSFS_OPT("rdw", keepRDW, 1), ZDSFS_OPT("ignore_incomplete", allow_inclomplete_multi_volume, 1), + ZDSFS_OPT("check_host_count", host_count, 1), FUSE_OPT_END }; @@ -790,7 +792,10 @@ static void usage(const char *progname) " data set are missing\n" " -o tracks=N Size of the track buffer in tracks (default 128)\n" " -o seekbuffer=S Upper limit in bytes for the seek history buffer\n" -" size (default 1048576)\n", progname); +" size (default 1048576)\n" +" -o check_host_count Stop processing if the device is used by another\n" +" operating system instance\n" + , progname); } static void zdsfs_process_device(const char *device) @@ -1014,6 +1019,17 @@ int main(int argc, char *argv[]) argv[0]); exit(1); } + + if (zdsfsinfo.host_count) { + /* check, print error and exit if multiple online */ + rc = lzds_analyse_open_count(zdsfsinfo.zdsroot, 0); + if (rc == -EACCES) + goto cleanup; + } else { + /* check, print warning if multiple online */ + lzds_analyse_open_count(zdsfsinfo.zdsroot, 1); + } + rc = zdsfs_verify_datasets(); if (rc) goto cleanup;