diff --git a/include/lib/dasd_base.h b/include/lib/dasd_base.h index 11ec94b1..400e3747 100644 --- a/include/lib/dasd_base.h +++ b/include/lib/dasd_base.h @@ -20,8 +20,8 @@ #include #include -/* A bus id of a DASD is 8 characters long. E.g. 0.0.4711 */ -#define DASD_BUS_ID_SIZE 9 +/* the definition of a BUSID in the DASD driver is 20 */ +#define DASD_BUS_ID_SIZE 20 typedef struct dasd_information2_t { unsigned int devno; /* S/390 devno */ @@ -242,6 +242,13 @@ struct dasd_snid_ioctl_data { __u8 path_mask; } __attribute__((packed)); +struct dasd_copypair_swap_data { + char primary[DASD_BUS_ID_SIZE]; /* BUSID of primary */ + char secondary[DASD_BUS_ID_SIZE]; /* BUSID of secondary */ + /* Reserved for future updates. */ + char reserved[64]; +}; + #ifndef __linux__ /* definition from hdreg.h */ struct hd_geometry { @@ -278,6 +285,8 @@ struct hd_geometry { #define BIODASDSATTR _IOW(DASD_IOCTL_LETTER, 2, attrib_data_t) /* Release Allocated Space */ #define BIODASDRAS _IOW(DASD_IOCTL_LETTER, 3, format_data_t) +/* Swap copy pair */ +#define BIODASDPPRCSWAP _IOW(DASD_IOCTL_LETTER, 4, struct dasd_copypair_swap_data) /* Get Sense Path Group ID (SNID) data */ #define BIODASDSNID _IOWR(DASD_IOCTL_LETTER, 1, struct dasd_snid_ioctl_data) /* Check device format according to format_data_t */ @@ -318,5 +327,6 @@ int dasd_set_cache(const char *device, attrib_data_t *attrib_data); int dasd_query_reserve(const char *device); int dasd_profile(const char *device, dasd_profile_info_t *dasd_profile_info); int dasd_reset_profile(const char *device); +int dasd_copy_swap(const char *device, struct dasd_copypair_swap_data *data); #endif /* LIB_DASD_BASE_H */ diff --git a/libdasd/dasd_ioctl.c b/libdasd/dasd_ioctl.c index 3a36375b..f6847618 100644 --- a/libdasd/dasd_ioctl.c +++ b/libdasd/dasd_ioctl.c @@ -452,3 +452,31 @@ int dasd_reset_profile(const char *device) return 0; } + +/* + * Initiate the swap of a copy pairs primary,secondary relation. + * The old secondary will become the new primary and vice versa. + * + * @param[in] device node device node's name + * @param[in] copy_pair data pointer to dasd copypair data with: + * 'primary' old primary, becoming secondary + * 'secondary' old secondary, becoming primary. + * + * @retval errno in case of failure + * @retval 0 in case of success + * @retval 1 swap data invalid + * @retval 2 no active device found + * @retval 3 wrong primary specified + * @retval 4 secondary device not found + * @retval 5 swap already running + */ +int dasd_copy_swap(const char *device, struct dasd_copypair_swap_data *data) +{ + int fd; + + fd = dasd_open_device(device, O_RDONLY); + RUN_IOCTL(fd, BIODASDPPRCSWAP, data); + dasd_close_device(fd); + + return 0; +} diff --git a/tunedasd/include/disk.h b/tunedasd/include/disk.h index ea35f6f3..55a32d00 100644 --- a/tunedasd/include/disk.h +++ b/tunedasd/include/disk.h @@ -31,6 +31,7 @@ int disk_query_reserve_status(char* device); int disk_profile (char* device, char* prof_item); int disk_reset_prof(char *device); int disk_reset_chpid(char *device, char *chpid); +int disk_copy_swap(char *device, char *copy_pair); #endif /* not DISK_H */ diff --git a/tunedasd/man/tunedasd.8 b/tunedasd/man/tunedasd.8 index 94c3608f..ae41b97e 100644 --- a/tunedasd/man/tunedasd.8 +++ b/tunedasd/man/tunedasd.8 @@ -174,6 +174,14 @@ 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 "\-s" " or " "\-\-copy\-pair\-swap " +Swap the copy roles of the specified copy pair. The has to +be a comma separated pair of \fBPRIMARY,SECONDARY\fR where +\fBPRIMARY\fR is the old primary device that will become a secondary +automatically. The old \fBSECONDARY\fR device will become the new +primary device. Both devices have to be online for this operation to +succeed. .\" .\".TP .\".BR "\-o" " or " "\-\-online" @@ -197,6 +205,12 @@ channel paths for I/O. .br tunedasd -p 45 /dev/dasdc + +.br +4. Scenario: Swap copy pair 0.0.9700 and 0.0.9740 +.br + + tunedasd -s 0.0.9700,0.0.9740 /dev/dasdc .br .SH "SEE ALSO" .BR dasdview (8), diff --git a/tunedasd/src/disk.c b/tunedasd/src/disk.c index 1e8de16c..490c1c51 100644 --- a/tunedasd/src/disk.c +++ b/tunedasd/src/disk.c @@ -21,12 +21,11 @@ #include "lib/dasd_base.h" #include "lib/dasd_sys.h" +#include "lib/util_libc.h" #include "disk.h" #include "tunedasd.h" -#define BUS_ID_SIZE 30 - /* id definition for profile items */ enum prof_id { prof_reqs = 0, @@ -549,3 +548,50 @@ int disk_reset_chpid(char *device, char *chpid) return -1; } + +int disk_copy_swap(char *device, char *copy_pair) +{ + struct dasd_copypair_swap_data data = { 0 }; + char *primary, *secondary; + int rc = 0; + + primary = strtok(copy_pair, ","); + secondary = strtok(NULL, ","); + + if (!primary || !secondary) { + error_print("%s: Error parsing Copy Pair %s", device, copy_pair); + return -1; + } + + util_strlcpy(data.primary, primary, DASD_BUS_ID_SIZE); + util_strlcpy(data.secondary, secondary, DASD_BUS_ID_SIZE); + + printf("Swapping copy pair %s %s on device <%s>...\n", primary, secondary, device); + rc = dasd_copy_swap(device, &data); + + switch (rc) { + case 0: + printf("Done.\n"); + return 0; + case 1: + error_print("Swap data invalid"); + break; + case 2: + error_print("No active device found"); + break; + case 3: + error_print("Wrong primary device specified"); + break; + case 4: + error_print("Secondary device not found"); + break; + case 5: + error_print("Swap already running"); + break; + default: + error_print("Swap not successful rc %d", rc); + break; + } + + return -1; +} diff --git a/tunedasd/src/tunedasd.c b/tunedasd/src/tunedasd.c index 3c14eb8c..691841c0 100644 --- a/tunedasd/src/tunedasd.c +++ b/tunedasd/src/tunedasd.c @@ -119,12 +119,17 @@ static struct util_opt opt_vec[] = { .desc = "Reset all channel paths of a device", .flags = UTIL_OPT_FLAG_NOSHORT, }, + { + .option = { "copy-pair-swap", required_argument, NULL, 's' }, + .argument = "COPY_PAIR", + .desc = "Swap a specified, comma separated copy pair.", + }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; -#define CMD_KEYWORD_NUM 16 +#define CMD_KEYWORD_NUM 17 #define DEVICES_NUM 256 enum cmd_keyword_id { @@ -144,6 +149,7 @@ enum cmd_keyword_id { cmd_keyword_path_all, cmd_keyword_enable_stats, cmd_keyword_disable_stats, + cmd_keyword_copy_swap, }; @@ -167,7 +173,8 @@ static const struct { { "path_reset", cmd_keyword_path }, { "path_reset_all", cmd_keyword_path_all }, { "enable-stats", cmd_keyword_enable_stats }, - { "disable-stats", cmd_keyword_disable_stats } + { "disable-stats", cmd_keyword_disable_stats }, + { "copy-swap", cmd_keyword_copy_swap }, }; @@ -180,26 +187,27 @@ enum cmd_key_state { /* Determines which combination of keywords are valid */ static 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 path path - * ion cach e yl rve ase k ile _ite t_pr y_re _all - * e m of serv + /* help vers get_ cach no_c rese rele sloc prof prof rese quer path path enab disa copy + * ion cach e yl rve ase k ile _ite t_pr y_re _all le-s ble- -swa + * e m of serv tats stat p */ - /* help */ { req, opt, opt, opt, opt, opt, opt, opt, opt, opt, opt, inv, inv, inv, inv, inv }, - /* version */ { inv, req, inv, inv, 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, inv, inv }, - /* cache */ { opt, opt, inv, req, opt, inv, inv, 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, inv, inv }, - /* reserve */ { opt, opt, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv }, - /* release */ { opt, opt, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv }, - /* slock */ { opt, opt, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv }, - /* profile */ { opt, opt, inv, inv, inv, inv, inv, inv, req, opt, inv, inv, inv, inv, inv, inv }, - /* prof_item */ { opt, opt, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, inv }, - /* reset_prof */ { opt, opt, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv }, - /* query_reserve */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv }, - /* path */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv }, - /* path_all */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv }, - /* enable-stats */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv }, - /* disable-stats */ { inv, inv, inv, inv, 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, inv, inv, inv }, + /* version */ { inv, req, inv, inv, inv, 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, inv, inv, inv }, + /* cache */ { opt, opt, inv, req, opt, inv, inv, inv, 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, inv, inv, inv }, + /* reserve */ { opt, opt, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv }, + /* release */ { opt, opt, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv }, + /* slock */ { opt, opt, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv }, + /* profile */ { opt, opt, inv, inv, inv, inv, inv, inv, req, opt, inv, inv, inv, inv, inv, inv, inv }, + /* prof_item */ { opt, opt, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, inv, inv }, + /* reset_prof */ { opt, opt, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv }, + /* query_reserve */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv }, + /* path */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv }, + /* path_all */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv }, + /* enable-stats */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv }, + /* disable-stats */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv }, + /* copy-swap */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req }, }; struct parameter { @@ -439,7 +447,9 @@ static int get_command_line(int argc, char *argv[], struct command_line *line) rc = store_option (&cmdline, cmd_keyword_query_reserve, optarg); break; - + case 's': + rc = store_option(&cmdline, cmd_keyword_copy_swap, optarg); + break; case -1: /* End of options string - start of devices list */ cmdline.device_id = optind; @@ -508,6 +518,9 @@ static int do_command(char *device, struct command_line cmdline) case cmd_keyword_path_all: rc = disk_reset_chpid(device, NULL); break; + case cmd_keyword_copy_swap: + rc = disk_copy_swap(device, cmdline.parm[cmd_keyword_copy_swap].data); + break; default: error_print ("Unknown command '%s' specified", get_keyword_name (i));