/* * 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 #include #include #include #include #include #include #include #include #include "lib/dasd_sys.h" #include "disk.h" #include "tunedasd.h" #define BUS_ID_SIZE 30 /* * DASD DEFINITIONS (copied from dasd.h) */ #define DASD_IOCTL_LETTER 'D' /* * struct profile_info_t * holds the profiling information */ typedef struct dasd_profile_info_t { unsigned int dasd_io_reqs; /* # of requests processed at all */ unsigned int dasd_io_sects; /* # of sectors processed at all */ unsigned int dasd_io_secs[32]; /* request's sizes */ unsigned int dasd_io_times[32]; /* requests's times */ unsigned int dasd_io_timps[32]; /* requests's times per sector */ unsigned int dasd_io_time1[32]; /* time from build to start */ unsigned int dasd_io_time2[32]; /* time from start to irq */ unsigned int dasd_io_time2ps[32]; /*time from start to irq */ unsigned int dasd_io_time3[32]; /* time from irq to end */ unsigned int dasd_io_nr_req[32]; /* # of requests in chanq */ } dasd_profile_info_t; /* * struct attrib_data_t * represents the operation (cache) bits for the device. * Used in DE to influence caching of the DASD. */ typedef struct attrib_data_t { unsigned char operation:3; /* cache operation mode */ unsigned char reserved:5; unsigned short nr_cyl; /* no of cyliners for read ahaed */ unsigned char reserved2[29]; /* for future use */ } __attribute__ ((packed)) attrib_data_t; /* definition of operation (cache) bits within attributes of DE */ #define DASD_NORMAL_CACHE 0x0 #define DASD_BYPASS_CACHE 0x1 #define DASD_INHIBIT_LOAD 0x2 #define DASD_SEQ_ACCESS 0x3 #define DASD_SEQ_PRESTAGE 0x4 #define DASD_REC_ACCESS 0x5 /* * Data returned by Sense Path Group ID (SNID) */ struct dasd_snid_data { struct { __u8 group:2; __u8 reserve:2; __u8 mode:1; __u8 res:3; } __attribute__ ((packed)) path_state; __u8 pgid[11]; } __attribute__ ((packed)); struct dasd_snid_ioctl_data { struct dasd_snid_data data; __u8 path_mask; } __attribute__ ((packed)); /* * DASD-IOCTLs (copied from dasd.h) */ /* Issue a reserve/release command, rsp. */ #define BIODASDRSRV _IO (DASD_IOCTL_LETTER,2) /* reserve */ #define BIODASDRLSE _IO (DASD_IOCTL_LETTER,3) /* release */ #define BIODASDSLCK _IO (DASD_IOCTL_LETTER,4) /* steal lock */ /* reset profiling information of a device */ #define BIODASDPRRST _IO (DASD_IOCTL_LETTER,5) /* retrieve profiling information of a device */ #define BIODASDPRRD _IOR (DASD_IOCTL_LETTER,2,dasd_profile_info_t) /* Get Attributes (cache operations) */ #define BIODASDGATTR _IOR(DASD_IOCTL_LETTER,5,attrib_data_t) /* Set Attributes (cache operations) */ #define BIODASDSATTR _IOW (DASD_IOCTL_LETTER,2,attrib_data_t) /* Get Sense Path Group ID (SNID) data */ #define BIODASDSNID _IOWR(DASD_IOCTL_LETTER, 1, struct dasd_snid_ioctl_data) /* 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 ""; } /* * 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) { int fd; attrib_data_t attrib_data; /* Open device file */ fd = open (device, O_RDONLY); if (fd == -1) { error_print ("<%s> - %s", device, strerror (errno)); return -1; } /* Get the given caching attributes */ if (ioctl (fd, BIODASDGATTR, &attrib_data)) { error_print ("Could not get cache attributes for device <%s>", device); close (fd); return -1; } printf ("%s (%i cyl)\n", get_cache_name(attrib_data.operation), attrib_data.nr_cyl); close (fd); 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) { int fd; attrib_data_t attrib_data; /* 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); } /* Open device file */ fd = open (device, O_RDONLY); if (fd == -1) { error_print ("<%s> - %s", device, strerror (errno)); return -1; } /* Set the given caching attributes */ printf ("Setting cache mode for device <%s>...\n", device); if (ioctl (fd, BIODASDSATTR, &attrib_data)) { error_print ("Could not set caching for device <%s>", device); close (fd); return -1; } printf ("Done.\n"); close (fd); return 0; } /* * Reserve the device. */ int disk_reserve (char* device) { int fd; /* Open device file */ fd = open (device, O_RDONLY); if (fd == -1) { error_print ("<%s> - %s", device, strerror (errno)); return -1; } /* Reserve device */ printf ("Reserving device <%s>...\n", device); if (ioctl (fd, BIODASDRSRV)) { error_print ("Could not reserve device <%s>", device); close (fd); return -1; } printf ("Done.\n"); close (fd); return 0; } /* * Release the device. */ int disk_release (char* device) { int fd; /* Open device file */ fd = open (device, O_RDONLY); if (fd == -1) { error_print ("<%s> - %s", device, strerror (errno)); return -1; } /* Release device */ printf ("Releasing device <%s>...\n", device); if (ioctl (fd, BIODASDRLSE)) { error_print ("Could not release device <%s>", device); close (fd); return -1; } printf ("Done.\n"); close (fd); 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 fd; /* Open device file */ fd = open (device, O_RDONLY); if (fd == -1) { error_print ("<%s> - %s", device, strerror (errno)); return -1; } /* Unconditional reserve device */ printf ("Unconditional reserving device <%s>...\n", device); if (ioctl (fd, BIODASDSLCK)) { error_print ("Could not unconditional reserve device <%s>", device); close (fd); return -1; } printf ("Done.\n"); close (fd); 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 fd; struct dasd_snid_ioctl_data snid; /* Open device file */ fd = open (device, O_RDONLY); if (fd == -1) { error_print ("<%s> - %s", device, strerror (errno)); return -1; } snid.path_mask = 0; /* Release device */ if (ioctl(fd, BIODASDSNID, &snid)) { error_print("Could not read reserve status" " for device <%s>", device); close (fd); return -1; } switch (snid.data.path_state.reserve) { case 0: printf("none\n"); break; case 1: printf("implicit\n"); break; case 2: printf("other\n"); break; case 3: printf("reserved\n"); break; } close (fd); 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) { int fd, rc; dasd_profile_info_t dasd_profile_info; /* Open device file */ fd = open (device, O_RDONLY); if (fd == -1) { error_print ("<%s> - %s", device, strerror (errno)); return -1; } /* Get the profile info */ if (ioctl (fd, BIODASDPRRD, &dasd_profile_info)) { 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); } close (fd); 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); } close (fd); return rc; } /* * Reset the profiling counters of the device. */ int disk_reset_prof (char* device) { int fd; /* Open device file */ fd = open (device, O_RDONLY); if (fd == -1) { error_print ("<%s> - %s", device, strerror (errno)); return -1; } /* reset profile info */ printf ("Resetting profile info for device <%s>...\n", device); if (ioctl (fd, BIODASDPRRST)) { error_print ("Could not reset profile info for device <%s>", device); close (fd); return -1; } printf ("Done.\n"); close (fd); 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; }