Files
s390-tools/tunedasd/src/disk.c
Michael Holzheu b627b8d8e1 Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version.

Changes on top of s390-tools-1.39.0:

 - Add MIT license to all source files
 - Add LICENSE file
 - Transform REAMDE to README.md (markdown)
 - Add AUTHORS.md file
 - Add CONTRIBUTING.md file
 - Move changelog from README to CHANGELOG.md file

Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
2017-08-21 10:55:40 +02:00

720 lines
16 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_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 "<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)
{
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;
}