Files
s390-tools/tunedasd/src/disk.c
Stefan Haberland 3f12bdf4c7 tunedasd: add copy_pair swap capability
Add an option to tunedasd to trigger a copy pair swap using the appropriate
ioctl for DASD devices.

      -s, --copy-pair-swap COPY_PAIR

This command requires a comma separated pair of primary,secondary to be
specified. In case of success the old secondary will become the new primary
device and the old primary will become a secondary device.

Example:

tunedasd /dev/dasda -s 0.0.9700,0.0.9740

This will set the old secondary device 0.0.9740 as the new primary.
The old primary device 0.0.9700 will automatically become a secondary
device.

Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Jan Hoeppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-15 10:24:42 +01:00

598 lines
13 KiB
C

/*
* tunedasd - Adjust tunable parameters on DASD devices
*
* Functions to handle DASD-IOCTLs
*
* Copyright IBM Corp. 2004, 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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include "lib/dasd_base.h"
#include "lib/dasd_sys.h"
#include "lib/util_libc.h"
#include "disk.h"
#include "tunedasd.h"
/* id definition for profile items */
enum prof_id {
prof_reqs = 0,
prof_sects = 1,
prof_sizes = 2,
prof_total = 3,
prof_totsect = 4,
prof_start = 5,
prof_irq = 6,
prof_irqsect = 7,
prof_end = 8,
prof_queue = 9
};
/* Mapping for caching modes */
static struct {
char* mode;
int id;
} mode_list[] = {
{ "normal", DASD_NORMAL_CACHE },
{ "bypass", DASD_BYPASS_CACHE },
{ "inhibit", DASD_INHIBIT_LOAD },
{ "sequential", DASD_SEQ_ACCESS },
{ "prestage", DASD_SEQ_PRESTAGE },
{ "record", DASD_REC_ACCESS }
};
/*
* Check for valid cache parameters.
*/
int
check_cache (char* cache)
{
unsigned int i;
/* Check for valid parameters */
for (i = 0; i < sizeof(mode_list)/sizeof(mode_list[0]); i++) {
if (!strcmp (cache, mode_list[i].mode)) {
return mode_list[i].id;
}
}
error_print ("Invalid caching mode '%s' given", cache);
return -1;
}
/*
* Retrieve name of cache mode identified by number.
*/
static char *get_cache_name(int id)
{
unsigned int i;
for (i = 0; i < sizeof (mode_list) / sizeof (mode_list[0]);
i++) {
if (id == mode_list[i].id) {
return mode_list[i].mode;
}
}
return "<unknown>";
}
/*
* Check for valid number of cylinders.
*/
int
check_no_cyl (char* no_cyl)
{
int nr_cyl;
char* err_ptr;
if (!no_cyl) {
/* set default = 2 Cylinders - if option is missing */
return 2;
} else {
err_ptr = NULL;
nr_cyl = strtoul (no_cyl, &err_ptr, 0);
if ((errno) ||
(*err_ptr != '\0')) {
error_print ("Invalid number of cylinders given '%s'",
no_cyl);
return -1;
}
}
return nr_cyl;
}
/*
* Check for valid profile item parameters.
*/
int
check_prof_item (char* prof_item)
{
/* Mapping for profile items */
static struct {
char* item;
int id;
} prof_list[] = {
{ "reqs", prof_reqs },
{ "sects", prof_sects },
{ "sizes", prof_sizes },
{ "total", prof_total },
{ "totsect", prof_totsect },
{ "start", prof_start },
{ "irq", prof_irq },
{ "irqsect", prof_irqsect },
{ "end", prof_end },
{ "queue", prof_queue }
};
unsigned int i;
/* Check for valid parameters */
for (i = 0; i < sizeof(prof_list)/sizeof(prof_list[0]); i++) {
if (!strcmp (prof_item, prof_list[i].item)) {
return prof_list[i].id;
}
}
error_print ("Invalid profile item '%s' given", prof_item);
return -1;
}
/*
* Get the caching algorithm used for the channel programs of this device.
* 'cache' is the caching mode (see ESS docu for more info) and 'no_cyl'
* the number of cylinders to be cached.
*/
int disk_get_cache(char *device)
{
attrib_data_t attrib_data;
int rc;
rc = dasd_get_cache(device, &attrib_data);
if (rc)
return rc;
printf ("%s (%i cyl)\n",
get_cache_name(attrib_data.operation),
attrib_data.nr_cyl);
return 0;
}
/*
* Set the caching algorithm used for the channel programs of this device.
* 'cache' is the caching mode (see ESS docu for more info) and 'no_cyl'
* the number of cylinders to be cached.
*/
int disk_set_cache(char *device, char *cache, char *no_cyl)
{
attrib_data_t attrib_data;
int rc;
/* get caching mode and # cylinders */
attrib_data.operation = check_cache (cache);
attrib_data.nr_cyl = check_no_cyl (no_cyl);
if (attrib_data.nr_cyl > 40) {
printf ("WARNING: This is a very large number of "
"cylinders ;) %i\n",
attrib_data.nr_cyl);
}
/* Set the given caching attributes */
printf ("Setting cache mode for device <%s>...\n", device);
rc = dasd_set_cache(device, &attrib_data);
if (rc) {
error_print("Could not set caching for device <%s>", device);
return -1;
}
printf ("Done.\n");
return 0;
}
/*
* Reserve the device.
*/
int disk_reserve(char *device)
{
int rc;
/* Reserve device */
printf ("Reserving device <%s>...\n", device);
rc = dasd_disk_reserve(device);
if (rc) {
error_print("Could not reserve device <%s>", device);
return -1;
}
printf("Done.\n");
return 0;
}
/*
* Release the device.
*/
int disk_release(char *device)
{
int rc;
printf ("Releasing device <%s>...\n", device);
rc = dasd_disk_release(device);
if (rc) {
error_print("Could not release device <%s>", device);
return -1;
}
printf("Done.\n");
return 0;
}
/*
* Unconditional reserve the device.
* This means to reserve the device even if it was already reserved.
* The current reserve is broken (steal lock).
*/
int disk_slock(char *device)
{
int rc;
/* Unconditional reserve device */
printf ("Unconditional reserving device <%s>...\n", device);
rc = dasd_slock(device);
if (rc) {
error_print("Could not unconditional reserve device <%s>", device);
return -1;
}
printf ("Done.\n");
return 0;
}
/*
* Uses the Sense Path Group ID (SNID) ioctl to find out if
* a device is reserved to it's path group.
*/
int disk_query_reserve_status(char *device)
{
int rc;
rc = dasd_query_reserve(device);
if (rc < 0) {
error_print("Could not read reserve status for device <%s>", device);
return -1;
}
switch (rc) {
case 0:
printf("none\n");
break;
case 1:
printf("implicit\n");
break;
case 2:
printf("other\n");
break;
case 3:
printf("reserved\n");
break;
}
return 0;
}
static int disk_profile_summary(dasd_profile_info_t dasd_profile_info)
{
int factor, i;
/* prevent counter 'overflow' on output */
for (factor = 1; (dasd_profile_info.dasd_io_reqs / factor) > 9999999;
factor *= 10) ;
/* print the profile info */
printf("\n%d dasd I/O requests\n", dasd_profile_info.dasd_io_reqs);
printf("with %u sectors(512B each)\n",
dasd_profile_info.dasd_io_sects);
printf("Scale factor is %d \n\n", factor);
printf(" __<4 ___8 __16 __32 __64 "
" _128 _256 _512 __1k __2k "
" __4k __8k _16k _32k _64k "
" 128k\n");
printf(" _256 _512 __1M __2M __4M "
" __8M _16M _32M _64M 128M "
" 256M 512M __1G __2G __4G "
" _>4G\n");
printf("Histogram of sizes (512B secs)\n");
for (i = 0; i < 16; i++) {
printf("%7d ", dasd_profile_info.dasd_io_secs[i] / factor);
}
printf("\n");
for (; i < 32; i++) {
printf("%7d ", dasd_profile_info.dasd_io_secs[i] / factor);
}
printf("\n");
printf("Histogram of I/O times (microseconds)\n");
for (i = 0; i < 16; i++) {
printf("%7d ", dasd_profile_info.dasd_io_times[i] / factor);
}
printf("\n");
for (; i < 32; i++) {
printf("%7d ", dasd_profile_info.dasd_io_times[i] / factor);
}
printf("\n");
printf("Histogram of I/O times per sector\n");
for (i = 0; i < 16; i++) {
printf("%7d ", dasd_profile_info.dasd_io_timps[i] / factor);
}
printf("\n");
for (; i < 32; i++) {
printf("%7d ", dasd_profile_info.dasd_io_timps[i] / factor);
}
printf("\n");
printf("Histogram of I/O time till ssch\n");
for (i = 0; i < 16; i++) {
printf("%7d ", dasd_profile_info.dasd_io_time1[i] / factor);
}
printf("\n");
for (; i < 32; i++) {
printf("%7d ", dasd_profile_info.dasd_io_time1[i] / factor);
}
printf("\n");
printf("Histogram of I/O time between ssch and irq\n");
for (i = 0; i < 16; i++) {
printf("%7d ", dasd_profile_info.dasd_io_time2[i] / factor);
}
printf("\n");
for (; i < 32; i++) {
printf("%7d ", dasd_profile_info.dasd_io_time2[i] / factor);
}
printf("\n");
printf("Histogram of I/O time between ssch and irq per "
"sector\n");
for (i = 0; i < 16; i++) {
printf("%7d ", dasd_profile_info.dasd_io_time2ps[i] / factor);
}
printf("\n");
for (; i < 32; i++) {
printf("%7d ", dasd_profile_info.dasd_io_time2ps[i] / factor);
}
printf("\n");
printf("Histogram of I/O time between irq and end\n");
for (i = 0; i < 16; i++) {
printf("%7d ", dasd_profile_info.dasd_io_time3[i] / factor);
}
printf("\n");
for (; i < 32; i++) {
printf("%7d ", dasd_profile_info.dasd_io_time3[i] / factor);
}
printf("\n");
printf("# of req in chanq at enqueuing (1..32) \n");
for (i = 0; i < 16; i++) {
printf("%7d ", dasd_profile_info.dasd_io_nr_req[i] / factor);
}
printf("\n");
for (; i < 32; i++) {
printf("%7d ", dasd_profile_info.dasd_io_nr_req[i] / factor);
}
printf("\n");
return 0;
}
static int disk_profile_item(dasd_profile_info_t dasd_profile_info,
char *prof_item)
{
int i;
/* Check for given profile item*/
switch (check_prof_item (prof_item)) {
case prof_reqs:
printf ("%d", dasd_profile_info.dasd_io_reqs);
break;
case prof_sects:
printf ("%d", dasd_profile_info.dasd_io_sects);
break;
case prof_sizes:
for (i = 0; i < 32; i++) {
printf ("%10d|", dasd_profile_info.dasd_io_secs[i]);
}
break;
case prof_total:
for (i = 0; i < 32; i++) {
printf ("%10d|", dasd_profile_info.dasd_io_times[i]);
}
break;
case prof_totsect:
for (i = 0; i < 32; i++) {
printf ("%10d|", dasd_profile_info.dasd_io_timps[i]);
}
break;
case prof_start:
for (i = 0; i < 32; i++) {
printf ("%10d|", dasd_profile_info.dasd_io_time1[i]);
}
break;
case prof_irq:
for (i = 0; i < 32; i++) {
printf ("%10d|", dasd_profile_info.dasd_io_time2[i]);
}
break;
case prof_irqsect:
for (i = 0; i < 32; i++) {
printf ("%10d|", dasd_profile_info.dasd_io_time2ps[i]);
}
break;
case prof_end:
for (i = 0; i < 32; i++) {
printf ("%10d|", dasd_profile_info.dasd_io_time3[i]);
}
break;
case prof_queue:
for (i = 0; i < 32; i++) {
printf ("%10d|", dasd_profile_info.dasd_io_nr_req[i]);
}
break;
}
printf ("\n");
return 0;
}
/*
* Get and print the profiling info of the device.
*/
int disk_profile(char *device, char *prof_item)
{
dasd_profile_info_t dasd_profile_info;
int rc;
/* Get the profile info */
rc = dasd_profile(device, &dasd_profile_info);
if (rc) {
switch (errno) {
case EIO: /* profiling is not active */
error_print ("Profiling (on device <%s>) is not "
"active.", device);
break;
default: /* all other errors */
error_print ("Could not get profile info for device "
"<%s>.", device);
}
return -1;
}
/* Check for profile item or summary */
if (!prof_item) {
rc = disk_profile_summary (dasd_profile_info);
} else {
rc = disk_profile_item (dasd_profile_info, prof_item);
}
return rc;
}
/*
* Reset the profiling counters of the device.
*/
int disk_reset_prof(char *device)
{
int rc;
/* reset profile info */
printf ("Resetting profile info for device <%s>...\n", device);
rc = dasd_reset_profile(device);
if (rc) {
error_print("Could not reset profile info for device <%s>", device);
return -1;
}
printf ("Done.\n");
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;
}
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;
}