Commit 4fa52aa7 authored by Stefan Weinhuber's avatar Stefan Weinhuber Committed by Martin Schwidefsky

[S390] dasd: add enhanced DASD statistics interface

This patch extends the DASD statistics to allow for a more detailed
analysis of DASD I/O operations. In particular we want the statistics
to provide answers to the following questions:
- How many requests used a PAV alias?
- How many requests used High Performance FICON?
- How do read request perform versus write requests?

The existing DASD statistics interface has several shortcomings
- The interface for global data is a formatted text table in procfs
  (/proc/dasd/statistics). The layout is meant for human readers and
  is not to easy to parse. If values get to large for the table
  layout, they get scaled down.
- The statistics which are collected per block device can be
  accessed via an ioctl interface, which can only be extended by
  defining a new ioctl.
- There is no statistics interface for individual PAV base and alias
  devices.

To overcome theses shortcomings we create a new DASD statistics
interface in debugfs. This interface will contain one entry for global
data, one per DASD block device, and one per DASD base and alias
device. Each file contains the statistic data in easy to parse
name/value and name/array pairs. The existing interfaces will remain
functional, but they will not be extended.
Signed-off-by: default avatarStefan Weinhuber <wein@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 8bb3a2eb
This diff is collapsed.
...@@ -382,6 +382,41 @@ struct dasd_path { ...@@ -382,6 +382,41 @@ struct dasd_path {
__u8 npm; __u8 npm;
}; };
struct dasd_profile_info {
/* legacy part of profile data, as in dasd_profile_info_t */
unsigned int dasd_io_reqs; /* number of requests processed */
unsigned int dasd_io_sects; /* number of sectors processed */
unsigned int dasd_io_secs[32]; /* histogram of request's sizes */
unsigned int dasd_io_times[32]; /* histogram of requests's times */
unsigned int dasd_io_timps[32]; /* h. of requests's times per sector */
unsigned int dasd_io_time1[32]; /* hist. of time from build to start */
unsigned int dasd_io_time2[32]; /* hist. of time from start to irq */
unsigned int dasd_io_time2ps[32]; /* hist. of time from start to irq */
unsigned int dasd_io_time3[32]; /* hist. of time from irq to end */
unsigned int dasd_io_nr_req[32]; /* hist. of # of requests in chanq */
/* new data */
struct timespec starttod; /* time of start or last reset */
unsigned int dasd_io_alias; /* requests using an alias */
unsigned int dasd_io_tpm; /* requests using transport mode */
unsigned int dasd_read_reqs; /* total number of read requests */
unsigned int dasd_read_sects; /* total number read sectors */
unsigned int dasd_read_alias; /* read request using an alias */
unsigned int dasd_read_tpm; /* read requests in transport mode */
unsigned int dasd_read_secs[32]; /* histogram of request's sizes */
unsigned int dasd_read_times[32]; /* histogram of requests's times */
unsigned int dasd_read_time1[32]; /* hist. time from build to start */
unsigned int dasd_read_time2[32]; /* hist. of time from start to irq */
unsigned int dasd_read_time3[32]; /* hist. of time from irq to end */
unsigned int dasd_read_nr_req[32]; /* hist. of # of requests in chanq */
};
struct dasd_profile {
struct dentry *dentry;
struct dasd_profile_info *data;
spinlock_t lock;
};
struct dasd_device { struct dasd_device {
/* Block device stuff. */ /* Block device stuff. */
struct dasd_block *block; struct dasd_block *block;
...@@ -431,6 +466,9 @@ struct dasd_device { ...@@ -431,6 +466,9 @@ struct dasd_device {
/* default expiration time in s */ /* default expiration time in s */
unsigned long default_expires; unsigned long default_expires;
struct dentry *debugfs_dentry;
struct dasd_profile profile;
}; };
struct dasd_block { struct dasd_block {
...@@ -453,9 +491,8 @@ struct dasd_block { ...@@ -453,9 +491,8 @@ struct dasd_block {
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
struct timer_list timer; struct timer_list timer;
#ifdef CONFIG_DASD_PROFILE struct dentry *debugfs_dentry;
struct dasd_profile_info_t profile; struct dasd_profile profile;
#endif
}; };
...@@ -589,12 +626,13 @@ dasd_check_blocksize(int bsize) ...@@ -589,12 +626,13 @@ dasd_check_blocksize(int bsize)
} }
/* externals in dasd.c */ /* externals in dasd.c */
#define DASD_PROFILE_ON 1 #define DASD_PROFILE_OFF 0
#define DASD_PROFILE_OFF 0 #define DASD_PROFILE_ON 1
#define DASD_PROFILE_GLOBAL_ONLY 2
extern debug_info_t *dasd_debug_area; extern debug_info_t *dasd_debug_area;
extern struct dasd_profile_info_t dasd_global_profile; extern struct dasd_profile_info dasd_global_profile_data;
extern unsigned int dasd_profile_level; extern unsigned int dasd_global_profile_level;
extern const struct block_device_operations dasd_device_operations; extern const struct block_device_operations dasd_device_operations;
extern struct kmem_cache *dasd_page_cache; extern struct kmem_cache *dasd_page_cache;
...@@ -662,6 +700,11 @@ void dasd_device_remove_stop_bits(struct dasd_device *, int); ...@@ -662,6 +700,11 @@ void dasd_device_remove_stop_bits(struct dasd_device *, int);
int dasd_device_is_ro(struct dasd_device *); int dasd_device_is_ro(struct dasd_device *);
void dasd_profile_reset(struct dasd_profile *);
int dasd_profile_on(struct dasd_profile *);
void dasd_profile_off(struct dasd_profile *);
void dasd_global_profile_reset(void);
char *dasd_get_user_string(const char __user *, size_t);
/* externals in dasd_devmap.c */ /* externals in dasd_devmap.c */
extern int dasd_max_devindex; extern int dasd_max_devindex;
......
...@@ -239,7 +239,7 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) ...@@ -239,7 +239,7 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp)
*/ */
static int dasd_ioctl_reset_profile(struct dasd_block *block) static int dasd_ioctl_reset_profile(struct dasd_block *block)
{ {
memset(&block->profile, 0, sizeof(struct dasd_profile_info_t)); dasd_profile_reset(&block->profile);
return 0; return 0;
} }
...@@ -248,10 +248,40 @@ static int dasd_ioctl_reset_profile(struct dasd_block *block) ...@@ -248,10 +248,40 @@ static int dasd_ioctl_reset_profile(struct dasd_block *block)
*/ */
static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
{ {
if (dasd_profile_level == DASD_PROFILE_OFF) struct dasd_profile_info_t *data;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
spin_lock_bh(&block->profile.lock);
if (block->profile.data) {
data->dasd_io_reqs = block->profile.data->dasd_io_reqs;
data->dasd_io_sects = block->profile.data->dasd_io_sects;
memcpy(data->dasd_io_secs, block->profile.data->dasd_io_secs,
sizeof(data->dasd_io_secs));
memcpy(data->dasd_io_times, block->profile.data->dasd_io_times,
sizeof(data->dasd_io_times));
memcpy(data->dasd_io_timps, block->profile.data->dasd_io_timps,
sizeof(data->dasd_io_timps));
memcpy(data->dasd_io_time1, block->profile.data->dasd_io_time1,
sizeof(data->dasd_io_time1));
memcpy(data->dasd_io_time2, block->profile.data->dasd_io_time2,
sizeof(data->dasd_io_time2));
memcpy(data->dasd_io_time2ps,
block->profile.data->dasd_io_time2ps,
sizeof(data->dasd_io_time2ps));
memcpy(data->dasd_io_time3, block->profile.data->dasd_io_time3,
sizeof(data->dasd_io_time3));
memcpy(data->dasd_io_nr_req,
block->profile.data->dasd_io_nr_req,
sizeof(data->dasd_io_nr_req));
spin_unlock_bh(&block->profile.lock);
} else {
spin_unlock_bh(&block->profile.lock);
return -EIO; return -EIO;
if (copy_to_user(argp, &block->profile, }
sizeof(struct dasd_profile_info_t))) if (copy_to_user(argp, data, sizeof(*data)))
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
......
...@@ -32,28 +32,6 @@ static struct proc_dir_entry *dasd_proc_root_entry = NULL; ...@@ -32,28 +32,6 @@ static struct proc_dir_entry *dasd_proc_root_entry = NULL;
static struct proc_dir_entry *dasd_devices_entry = NULL; static struct proc_dir_entry *dasd_devices_entry = NULL;
static struct proc_dir_entry *dasd_statistics_entry = NULL; static struct proc_dir_entry *dasd_statistics_entry = NULL;
#ifdef CONFIG_DASD_PROFILE
static char *
dasd_get_user_string(const char __user *user_buf, size_t user_len)
{
char *buffer;
buffer = kmalloc(user_len + 1, GFP_KERNEL);
if (buffer == NULL)
return ERR_PTR(-ENOMEM);
if (copy_from_user(buffer, user_buf, user_len) != 0) {
kfree(buffer);
return ERR_PTR(-EFAULT);
}
/* got the string, now strip linefeed. */
if (buffer[user_len - 1] == '\n')
buffer[user_len - 1] = 0;
else
buffer[user_len] = 0;
return buffer;
}
#endif /* CONFIG_DASD_PROFILE */
static int static int
dasd_devices_show(struct seq_file *m, void *v) dasd_devices_show(struct seq_file *m, void *v)
{ {
...@@ -167,6 +145,55 @@ static const struct file_operations dasd_devices_file_ops = { ...@@ -167,6 +145,55 @@ static const struct file_operations dasd_devices_file_ops = {
}; };
#ifdef CONFIG_DASD_PROFILE #ifdef CONFIG_DASD_PROFILE
static int dasd_stats_all_block_on(void)
{
int i, rc;
struct dasd_device *device;
rc = 0;
for (i = 0; i < dasd_max_devindex; ++i) {
device = dasd_device_from_devindex(i);
if (IS_ERR(device))
continue;
if (device->block)
rc = dasd_profile_on(&device->block->profile);
dasd_put_device(device);
if (rc)
return rc;
}
return 0;
}
static void dasd_stats_all_block_off(void)
{
int i;
struct dasd_device *device;
for (i = 0; i < dasd_max_devindex; ++i) {
device = dasd_device_from_devindex(i);
if (IS_ERR(device))
continue;
if (device->block)
dasd_profile_off(&device->block->profile);
dasd_put_device(device);
}
}
static void dasd_stats_all_block_reset(void)
{
int i;
struct dasd_device *device;
for (i = 0; i < dasd_max_devindex; ++i) {
device = dasd_device_from_devindex(i);
if (IS_ERR(device))
continue;
if (device->block)
dasd_profile_reset(&device->block->profile);
dasd_put_device(device);
}
}
static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor) static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor)
{ {
int i; int i;
...@@ -183,18 +210,18 @@ static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int f ...@@ -183,18 +210,18 @@ static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int f
static int dasd_stats_proc_show(struct seq_file *m, void *v) static int dasd_stats_proc_show(struct seq_file *m, void *v)
{ {
#ifdef CONFIG_DASD_PROFILE #ifdef CONFIG_DASD_PROFILE
struct dasd_profile_info_t *prof; struct dasd_profile_info *prof;
int factor; int factor;
/* check for active profiling */ /* check for active profiling */
if (dasd_profile_level == DASD_PROFILE_OFF) { if (!dasd_global_profile_level) {
seq_printf(m, "Statistics are off - they might be " seq_printf(m, "Statistics are off - they might be "
"switched on using 'echo set on > " "switched on using 'echo set on > "
"/proc/dasd/statistics'\n"); "/proc/dasd/statistics'\n");
return 0; return 0;
} }
prof = &dasd_global_profile_data;
prof = &dasd_global_profile;
/* prevent counter 'overflow' on output */ /* prevent counter 'overflow' on output */
for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999; for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
factor *= 10); factor *= 10);
...@@ -245,6 +272,7 @@ static ssize_t dasd_stats_proc_write(struct file *file, ...@@ -245,6 +272,7 @@ static ssize_t dasd_stats_proc_write(struct file *file,
{ {
#ifdef CONFIG_DASD_PROFILE #ifdef CONFIG_DASD_PROFILE
char *buffer, *str; char *buffer, *str;
int rc;
if (user_len > 65536) if (user_len > 65536)
user_len = 65536; user_len = 65536;
...@@ -259,32 +287,40 @@ static ssize_t dasd_stats_proc_write(struct file *file, ...@@ -259,32 +287,40 @@ static ssize_t dasd_stats_proc_write(struct file *file,
str = skip_spaces(str + 4); str = skip_spaces(str + 4);
if (strcmp(str, "on") == 0) { if (strcmp(str, "on") == 0) {
/* switch on statistics profiling */ /* switch on statistics profiling */
dasd_profile_level = DASD_PROFILE_ON; rc = dasd_stats_all_block_on();
if (rc) {
dasd_stats_all_block_off();
goto out_error;
}
dasd_global_profile_reset();
dasd_global_profile_level = DASD_PROFILE_ON;
pr_info("The statistics feature has been switched " pr_info("The statistics feature has been switched "
"on\n"); "on\n");
} else if (strcmp(str, "off") == 0) { } else if (strcmp(str, "off") == 0) {
/* switch off and reset statistics profiling */ /* switch off and reset statistics profiling */
memset(&dasd_global_profile, dasd_global_profile_level = DASD_PROFILE_OFF;
0, sizeof (struct dasd_profile_info_t)); dasd_global_profile_reset();
dasd_profile_level = DASD_PROFILE_OFF; dasd_stats_all_block_off();
pr_info("The statistics feature has been switched " pr_info("The statistics feature has been switched "
"off\n"); "off\n");
} else } else
goto out_error; goto out_parse_error;
} else if (strncmp(str, "reset", 5) == 0) { } else if (strncmp(str, "reset", 5) == 0) {
/* reset the statistics */ /* reset the statistics */
memset(&dasd_global_profile, 0, dasd_global_profile_reset();
sizeof (struct dasd_profile_info_t)); dasd_stats_all_block_reset();
pr_info("The statistics have been reset\n"); pr_info("The statistics have been reset\n");
} else } else
goto out_error; goto out_parse_error;
kfree(buffer); kfree(buffer);
return user_len; return user_len;
out_error: out_parse_error:
rc = -EINVAL;
pr_warning("%s is not a supported value for /proc/dasd/statistics\n", pr_warning("%s is not a supported value for /proc/dasd/statistics\n",
str); str);
out_error:
kfree(buffer); kfree(buffer);
return -EINVAL; return rc;
#else #else
pr_warning("/proc/dasd/statistics: is not activated in this kernel\n"); pr_warning("/proc/dasd/statistics: is not activated in this kernel\n");
return user_len; return user_len;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment