diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-24 09:55:45 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-24 09:55:45 -0700 |
commit | 21c7075fa5a756f1c95f6b463ff42cd320cc0301 (patch) | |
tree | 69524dd01fbebe662abe3b7296664592d3ce562b /drivers | |
parent | ff0c4ad2c3a75ccfe6adca916e50804eb45bb2d9 (diff) | |
parent | 73b7d40ff1bcd44b4245c2714b88cf872fe44685 (diff) | |
download | lwn-21c7075fa5a756f1c95f6b463ff42cd320cc0301.tar.gz lwn-21c7075fa5a756f1c95f6b463ff42cd320cc0301.zip |
Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
* 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6: (21 commits)
[S390] use siginfo for sigtrap signals
[S390] dasd: add enhanced DASD statistics interface
[S390] kvm: make sigp emerg smp capable
[S390] disable cpu measurement alerts on a dying cpu
[S390] initial cr0 bits
[S390] iucv cr0 enablement bit
[S390] race safe external interrupt registration
[S390] remove tape block docu
[S390] ap: toleration support for ap device type 10
[S390] cleanup program check handler prototypes
[S390] remove kvm mmu reload on s390
[S390] Use gmap translation for accessing guest memory
[S390] use gmap address spaces for kvm guest images
[S390] kvm guest address space mapping
[S390] fix s390 assembler code alignments
[S390] move sie code to entry.S
[S390] kvm: handle tprot intercepts
[S390] qdio: clear shared DSCI before scheduling the queue handler
[S390] reference bit testing for unmapped pages
[S390] irqs: Do not trace arch_local_{*,irq_*} functions
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/block/dasd.c | 576 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 57 | ||||
-rw-r--r-- | drivers/s390/block/dasd_ioctl.c | 38 | ||||
-rw-r--r-- | drivers/s390/block/dasd_proc.c | 106 | ||||
-rw-r--r-- | drivers/s390/char/Kconfig | 3 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 15 | ||||
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 96 | ||||
-rw-r--r-- | drivers/s390/crypto/ap_bus.h | 22 |
8 files changed, 782 insertions, 131 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 86b6f1cc1b10..432444af7ee4 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -22,6 +22,8 @@ #include <linux/hdreg.h> #include <linux/async.h> #include <linux/mutex.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> #include <asm/ccwdev.h> #include <asm/ebcdic.h> @@ -45,6 +47,7 @@ * SECTION: exported variables of dasd.c */ debug_info_t *dasd_debug_area; +static struct dentry *dasd_debugfs_root_entry; struct dasd_discipline *dasd_diag_discipline_pointer; void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *); @@ -71,6 +74,8 @@ static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); static void dasd_device_timeout(unsigned long); static void dasd_block_timeout(unsigned long); static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *); +static void dasd_profile_init(struct dasd_profile *, struct dentry *); +static void dasd_profile_exit(struct dasd_profile *); /* * SECTION: Operations on the device structure. @@ -121,7 +126,7 @@ struct dasd_device *dasd_alloc_device(void) device->state = DASD_STATE_NEW; device->target = DASD_STATE_NEW; mutex_init(&device->state_mutex); - + spin_lock_init(&device->profile.lock); return device; } @@ -159,6 +164,7 @@ struct dasd_block *dasd_alloc_block(void) init_timer(&block->timer); block->timer.function = dasd_block_timeout; block->timer.data = (unsigned long) block; + spin_lock_init(&block->profile.lock); return block; } @@ -222,19 +228,44 @@ static int dasd_state_known_to_new(struct dasd_device *device) return 0; } +static struct dentry *dasd_debugfs_setup(const char *name, + struct dentry *base_dentry) +{ + struct dentry *pde; + + if (!base_dentry) + return NULL; + pde = debugfs_create_dir(name, base_dentry); + if (!pde || IS_ERR(pde)) + return NULL; + return pde; +} + /* * Request the irq line for the device. */ static int dasd_state_known_to_basic(struct dasd_device *device) { + struct dasd_block *block = device->block; int rc; /* Allocate and register gendisk structure. */ - if (device->block) { - rc = dasd_gendisk_alloc(device->block); + if (block) { + rc = dasd_gendisk_alloc(block); if (rc) return rc; - } + block->debugfs_dentry = + dasd_debugfs_setup(block->gdp->disk_name, + dasd_debugfs_root_entry); + dasd_profile_init(&block->profile, block->debugfs_dentry); + if (dasd_global_profile_level == DASD_PROFILE_ON) + dasd_profile_on(&device->block->profile); + } + device->debugfs_dentry = + dasd_debugfs_setup(dev_name(&device->cdev->dev), + dasd_debugfs_root_entry); + dasd_profile_init(&device->profile, device->debugfs_dentry); + /* register 'device' debug area, used for all DBF_DEV_XXX calls */ device->debug_area = debug_register(dev_name(&device->cdev->dev), 4, 1, 8 * sizeof(long)); @@ -253,6 +284,9 @@ static int dasd_state_basic_to_known(struct dasd_device *device) { int rc; if (device->block) { + dasd_profile_exit(&device->block->profile); + if (device->block->debugfs_dentry) + debugfs_remove(device->block->debugfs_dentry); dasd_gendisk_free(device->block); dasd_block_clear_timer(device->block); } @@ -260,6 +294,9 @@ static int dasd_state_basic_to_known(struct dasd_device *device) if (rc) return rc; dasd_device_clear_timer(device); + dasd_profile_exit(&device->profile); + if (device->debugfs_dentry) + debugfs_remove(device->debugfs_dentry); DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device); if (device->debug_area != NULL) { @@ -609,21 +646,13 @@ void dasd_enable_device(struct dasd_device *device) /* * SECTION: device operation (interrupt handler, start i/o, term i/o ...) */ -#ifdef CONFIG_DASD_PROFILE -struct dasd_profile_info_t dasd_global_profile; -unsigned int dasd_profile_level = DASD_PROFILE_OFF; +unsigned int dasd_global_profile_level = DASD_PROFILE_OFF; -/* - * Increments counter in global and local profiling structures. - */ -#define dasd_profile_counter(value, counter, block) \ -{ \ - int index; \ - for (index = 0; index < 31 && value >> (2+index); index++); \ - dasd_global_profile.counter[index]++; \ - block->profile.counter[index]++; \ -} +#ifdef CONFIG_DASD_PROFILE +struct dasd_profile_info dasd_global_profile_data; +static struct dentry *dasd_global_profile_dentry; +static struct dentry *dasd_debugfs_global_entry; /* * Add profiling information for cqr before execution. @@ -634,30 +663,121 @@ static void dasd_profile_start(struct dasd_block *block, { struct list_head *l; unsigned int counter; - - if (dasd_profile_level != DASD_PROFILE_ON) - return; + struct dasd_device *device; /* count the length of the chanq for statistics */ counter = 0; - list_for_each(l, &block->ccw_queue) - if (++counter >= 31) - break; - dasd_global_profile.dasd_io_nr_req[counter]++; - block->profile.dasd_io_nr_req[counter]++; + if (dasd_global_profile_level || block->profile.data) + list_for_each(l, &block->ccw_queue) + if (++counter >= 31) + break; + + if (dasd_global_profile_level) { + dasd_global_profile_data.dasd_io_nr_req[counter]++; + if (rq_data_dir(req) == READ) + dasd_global_profile_data.dasd_read_nr_req[counter]++; + } + + spin_lock(&block->profile.lock); + if (block->profile.data) + block->profile.data->dasd_io_nr_req[counter]++; + if (rq_data_dir(req) == READ) + block->profile.data->dasd_read_nr_req[counter]++; + spin_unlock(&block->profile.lock); + + /* + * We count the request for the start device, even though it may run on + * some other device due to error recovery. This way we make sure that + * we count each request only once. + */ + device = cqr->startdev; + if (device->profile.data) { + counter = 1; /* request is not yet queued on the start device */ + list_for_each(l, &device->ccw_queue) + if (++counter >= 31) + break; + } + spin_lock(&device->profile.lock); + if (device->profile.data) { + device->profile.data->dasd_io_nr_req[counter]++; + if (rq_data_dir(req) == READ) + device->profile.data->dasd_read_nr_req[counter]++; + } + spin_unlock(&device->profile.lock); } /* * Add profiling information for cqr after execution. */ + +#define dasd_profile_counter(value, index) \ +{ \ + for (index = 0; index < 31 && value >> (2+index); index++) \ + ; \ +} + +static void dasd_profile_end_add_data(struct dasd_profile_info *data, + int is_alias, + int is_tpm, + int is_read, + long sectors, + int sectors_ind, + int tottime_ind, + int tottimeps_ind, + int strtime_ind, + int irqtime_ind, + int irqtimeps_ind, + int endtime_ind) +{ + /* in case of an overflow, reset the whole profile */ + if (data->dasd_io_reqs == UINT_MAX) { + memset(data, 0, sizeof(*data)); + getnstimeofday(&data->starttod); + } + data->dasd_io_reqs++; + data->dasd_io_sects += sectors; + if (is_alias) + data->dasd_io_alias++; + if (is_tpm) + data->dasd_io_tpm++; + + data->dasd_io_secs[sectors_ind]++; + data->dasd_io_times[tottime_ind]++; + data->dasd_io_timps[tottimeps_ind]++; + data->dasd_io_time1[strtime_ind]++; + data->dasd_io_time2[irqtime_ind]++; + data->dasd_io_time2ps[irqtimeps_ind]++; + data->dasd_io_time3[endtime_ind]++; + + if (is_read) { + data->dasd_read_reqs++; + data->dasd_read_sects += sectors; + if (is_alias) + data->dasd_read_alias++; + if (is_tpm) + data->dasd_read_tpm++; + data->dasd_read_secs[sectors_ind]++; + data->dasd_read_times[tottime_ind]++; + data->dasd_read_time1[strtime_ind]++; + data->dasd_read_time2[irqtime_ind]++; + data->dasd_read_time3[endtime_ind]++; + } +} + static void dasd_profile_end(struct dasd_block *block, struct dasd_ccw_req *cqr, struct request *req) { long strtime, irqtime, endtime, tottime; /* in microseconds */ long tottimeps, sectors; + struct dasd_device *device; + int sectors_ind, tottime_ind, tottimeps_ind, strtime_ind; + int irqtime_ind, irqtimeps_ind, endtime_ind; - if (dasd_profile_level != DASD_PROFILE_ON) + device = cqr->startdev; + if (!(dasd_global_profile_level || + block->profile.data || + device->profile.data)) return; sectors = blk_rq_sectors(req); @@ -672,29 +792,392 @@ static void dasd_profile_end(struct dasd_block *block, tottime = ((cqr->endclk - cqr->buildclk) >> 12); tottimeps = tottime / sectors; - if (!dasd_global_profile.dasd_io_reqs) - memset(&dasd_global_profile, 0, - sizeof(struct dasd_profile_info_t)); - dasd_global_profile.dasd_io_reqs++; - dasd_global_profile.dasd_io_sects += sectors; - - if (!block->profile.dasd_io_reqs) - memset(&block->profile, 0, - sizeof(struct dasd_profile_info_t)); - block->profile.dasd_io_reqs++; - block->profile.dasd_io_sects += sectors; - - dasd_profile_counter(sectors, dasd_io_secs, block); - dasd_profile_counter(tottime, dasd_io_times, block); - dasd_profile_counter(tottimeps, dasd_io_timps, block); - dasd_profile_counter(strtime, dasd_io_time1, block); - dasd_profile_counter(irqtime, dasd_io_time2, block); - dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, block); - dasd_profile_counter(endtime, dasd_io_time3, block); + dasd_profile_counter(sectors, sectors_ind); + dasd_profile_counter(tottime, tottime_ind); + dasd_profile_counter(tottimeps, tottimeps_ind); + dasd_profile_counter(strtime, strtime_ind); + dasd_profile_counter(irqtime, irqtime_ind); + dasd_profile_counter(irqtime / sectors, irqtimeps_ind); + dasd_profile_counter(endtime, endtime_ind); + + if (dasd_global_profile_level) { + dasd_profile_end_add_data(&dasd_global_profile_data, + cqr->startdev != block->base, + cqr->cpmode == 1, + rq_data_dir(req) == READ, + sectors, sectors_ind, tottime_ind, + tottimeps_ind, strtime_ind, + irqtime_ind, irqtimeps_ind, + endtime_ind); + } + + spin_lock(&block->profile.lock); + if (block->profile.data) + dasd_profile_end_add_data(block->profile.data, + cqr->startdev != block->base, + cqr->cpmode == 1, + rq_data_dir(req) == READ, + sectors, sectors_ind, tottime_ind, + tottimeps_ind, strtime_ind, + irqtime_ind, irqtimeps_ind, + endtime_ind); + spin_unlock(&block->profile.lock); + + spin_lock(&device->profile.lock); + if (device->profile.data) + dasd_profile_end_add_data(device->profile.data, + cqr->startdev != block->base, + cqr->cpmode == 1, + rq_data_dir(req) == READ, + sectors, sectors_ind, tottime_ind, + tottimeps_ind, strtime_ind, + irqtime_ind, irqtimeps_ind, + endtime_ind); + spin_unlock(&device->profile.lock); +} + +void dasd_profile_reset(struct dasd_profile *profile) +{ + struct dasd_profile_info *data; + + spin_lock_bh(&profile->lock); + data = profile->data; + if (!data) { + spin_unlock_bh(&profile->lock); + return; + } + memset(data, 0, sizeof(*data)); + getnstimeofday(&data->starttod); + spin_unlock_bh(&profile->lock); +} + +void dasd_global_profile_reset(void) +{ + memset(&dasd_global_profile_data, 0, sizeof(dasd_global_profile_data)); + getnstimeofday(&dasd_global_profile_data.starttod); +} + +int dasd_profile_on(struct dasd_profile *profile) +{ + struct dasd_profile_info *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + spin_lock_bh(&profile->lock); + if (profile->data) { + spin_unlock_bh(&profile->lock); + kfree(data); + return 0; + } + getnstimeofday(&data->starttod); + profile->data = data; + spin_unlock_bh(&profile->lock); + return 0; +} + +void dasd_profile_off(struct dasd_profile *profile) +{ + spin_lock_bh(&profile->lock); + kfree(profile->data); + profile->data = NULL; + spin_unlock_bh(&profile->lock); +} + +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; } + +static ssize_t dasd_stats_write(struct file *file, + const char __user *user_buf, + size_t user_len, loff_t *pos) +{ + char *buffer, *str; + int rc; + struct seq_file *m = (struct seq_file *)file->private_data; + struct dasd_profile *prof = m->private; + + if (user_len > 65536) + user_len = 65536; + buffer = dasd_get_user_string(user_buf, user_len); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + str = skip_spaces(buffer); + rc = user_len; + if (strncmp(str, "reset", 5) == 0) { + dasd_profile_reset(prof); + } else if (strncmp(str, "on", 2) == 0) { + rc = dasd_profile_on(prof); + if (!rc) + rc = user_len; + } else if (strncmp(str, "off", 3) == 0) { + dasd_profile_off(prof); + } else + rc = -EINVAL; + kfree(buffer); + return rc; +} + +static void dasd_stats_array(struct seq_file *m, unsigned int *array) +{ + int i; + + for (i = 0; i < 32; i++) + seq_printf(m, "%u ", array[i]); + seq_putc(m, '\n'); +} + +static void dasd_stats_seq_print(struct seq_file *m, + struct dasd_profile_info *data) +{ + seq_printf(m, "start_time %ld.%09ld\n", + data->starttod.tv_sec, data->starttod.tv_nsec); + seq_printf(m, "total_requests %u\n", data->dasd_io_reqs); + seq_printf(m, "total_sectors %u\n", data->dasd_io_sects); + seq_printf(m, "total_pav %u\n", data->dasd_io_alias); + seq_printf(m, "total_hpf %u\n", data->dasd_io_tpm); + seq_printf(m, "histogram_sectors "); + dasd_stats_array(m, data->dasd_io_secs); + seq_printf(m, "histogram_io_times "); + dasd_stats_array(m, data->dasd_io_times); + seq_printf(m, "histogram_io_times_weighted "); + dasd_stats_array(m, data->dasd_io_timps); + seq_printf(m, "histogram_time_build_to_ssch "); + dasd_stats_array(m, data->dasd_io_time1); + seq_printf(m, "histogram_time_ssch_to_irq "); + dasd_stats_array(m, data->dasd_io_time2); + seq_printf(m, "histogram_time_ssch_to_irq_weighted "); + dasd_stats_array(m, data->dasd_io_time2ps); + seq_printf(m, "histogram_time_irq_to_end "); + dasd_stats_array(m, data->dasd_io_time3); + seq_printf(m, "histogram_ccw_queue_length "); + dasd_stats_array(m, data->dasd_io_nr_req); + seq_printf(m, "total_read_requests %u\n", data->dasd_read_reqs); + seq_printf(m, "total_read_sectors %u\n", data->dasd_read_sects); + seq_printf(m, "total_read_pav %u\n", data->dasd_read_alias); + seq_printf(m, "total_read_hpf %u\n", data->dasd_read_tpm); + seq_printf(m, "histogram_read_sectors "); + dasd_stats_array(m, data->dasd_read_secs); + seq_printf(m, "histogram_read_times "); + dasd_stats_array(m, data->dasd_read_times); + seq_printf(m, "histogram_read_time_build_to_ssch "); + dasd_stats_array(m, data->dasd_read_time1); + seq_printf(m, "histogram_read_time_ssch_to_irq "); + dasd_stats_array(m, data->dasd_read_time2); + seq_printf(m, "histogram_read_time_irq_to_end "); + dasd_stats_array(m, data->dasd_read_time3); + seq_printf(m, "histogram_read_ccw_queue_length "); + dasd_stats_array(m, data->dasd_read_nr_req); +} + +static int dasd_stats_show(struct seq_file *m, void *v) +{ + struct dasd_profile *profile; + struct dasd_profile_info *data; + + profile = m->private; + spin_lock_bh(&profile->lock); + data = profile->data; + if (!data) { + spin_unlock_bh(&profile->lock); + seq_printf(m, "disabled\n"); + return 0; + } + dasd_stats_seq_print(m, data); + spin_unlock_bh(&profile->lock); + return 0; +} + +static int dasd_stats_open(struct inode *inode, struct file *file) +{ + struct dasd_profile *profile = inode->i_private; + return single_open(file, dasd_stats_show, profile); +} + +static const struct file_operations dasd_stats_raw_fops = { + .owner = THIS_MODULE, + .open = dasd_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = dasd_stats_write, +}; + +static ssize_t dasd_stats_global_write(struct file *file, + const char __user *user_buf, + size_t user_len, loff_t *pos) +{ + char *buffer, *str; + ssize_t rc; + + if (user_len > 65536) + user_len = 65536; + buffer = dasd_get_user_string(user_buf, user_len); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + str = skip_spaces(buffer); + rc = user_len; + if (strncmp(str, "reset", 5) == 0) { + dasd_global_profile_reset(); + } else if (strncmp(str, "on", 2) == 0) { + dasd_global_profile_reset(); + dasd_global_profile_level = DASD_PROFILE_GLOBAL_ONLY; + } else if (strncmp(str, "off", 3) == 0) { + dasd_global_profile_level = DASD_PROFILE_OFF; + } else + rc = -EINVAL; + kfree(buffer); + return rc; +} + +static int dasd_stats_global_show(struct seq_file *m, void *v) +{ + if (!dasd_global_profile_level) { + seq_printf(m, "disabled\n"); + return 0; + } + dasd_stats_seq_print(m, &dasd_global_profile_data); + return 0; +} + +static int dasd_stats_global_open(struct inode *inode, struct file *file) +{ + return single_open(file, dasd_stats_global_show, NULL); +} + +static const struct file_operations dasd_stats_global_fops = { + .owner = THIS_MODULE, + .open = dasd_stats_global_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = dasd_stats_global_write, +}; + +static void dasd_profile_init(struct dasd_profile *profile, + struct dentry *base_dentry) +{ + mode_t mode; + struct dentry *pde; + + if (!base_dentry) + return; + profile->dentry = NULL; + profile->data = NULL; + mode = (S_IRUSR | S_IWUSR | S_IFREG); + pde = debugfs_create_file("statistics", mode, base_dentry, + profile, &dasd_stats_raw_fops); + if (pde && !IS_ERR(pde)) + profile->dentry = pde; + return; +} + +static void dasd_profile_exit(struct dasd_profile *profile) +{ + dasd_profile_off(profile); + if (profile->dentry) { + debugfs_remove(profile->dentry); + profile->dentry = NULL; + } +} + +static void dasd_statistics_removeroot(void) +{ + dasd_global_profile_level = DASD_PROFILE_OFF; + if (dasd_global_profile_dentry) { + debugfs_remove(dasd_global_profile_dentry); + dasd_global_profile_dentry = NULL; + } + if (dasd_debugfs_global_entry) + debugfs_remove(dasd_debugfs_global_entry); + if (dasd_debugfs_root_entry) + debugfs_remove(dasd_debugfs_root_entry); +} + +static void dasd_statistics_createroot(void) +{ + mode_t mode; + struct dentry *pde; + + dasd_debugfs_root_entry = NULL; + dasd_debugfs_global_entry = NULL; + dasd_global_profile_dentry = NULL; + pde = debugfs_create_dir("dasd", NULL); + if (!pde || IS_ERR(pde)) + goto error; + dasd_debugfs_root_entry = pde; + pde = debugfs_create_dir("global", dasd_debugfs_root_entry); + if (!pde || IS_ERR(pde)) + goto error; + dasd_debugfs_global_entry = pde; + + mode = (S_IRUSR | S_IWUSR | S_IFREG); + pde = debugfs_create_file("statistics", mode, dasd_debugfs_global_entry, + NULL, &dasd_stats_global_fops); + if (!pde || IS_ERR(pde)) + goto error; + dasd_global_profile_dentry = pde; + return; + +error: + DBF_EVENT(DBF_ERR, "%s", + "Creation of the dasd debugfs interface failed"); + dasd_statistics_removeroot(); + return; +} + #else #define dasd_profile_start(block, cqr, req) do {} while (0) #define dasd_profile_end(block, cqr, req) do {} while (0) + +static void dasd_statistics_createroot(void) +{ + return; +} + +static void dasd_statistics_removeroot(void) +{ + return; +} + +int dasd_stats_generic_show(struct seq_file *m, void *v) +{ + seq_printf(m, "Statistics are not activated in this kernel\n"); + return 0; +} + +static void dasd_profile_init(struct dasd_profile *profile, + struct dentry *base_dentry) +{ + return; +} + +static void dasd_profile_exit(struct dasd_profile *profile) +{ + return; +} + +int dasd_profile_on(struct dasd_profile *profile) +{ + return 0; +} + #endif /* CONFIG_DASD_PROFILE */ /* @@ -2441,6 +2924,7 @@ dasd_exit(void) debug_unregister(dasd_debug_area); dasd_debug_area = NULL; } + dasd_statistics_removeroot(); } /* @@ -2992,6 +3476,8 @@ static int __init dasd_init(void) dasd_diag_discipline_pointer = NULL; + dasd_statistics_createroot(); + rc = dasd_devmap_init(); if (rc) goto failed; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index d1e4f2c1264c..1dd12bd85a69 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -382,6 +382,41 @@ struct dasd_path { __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 { /* Block device stuff. */ struct dasd_block *block; @@ -431,6 +466,9 @@ struct dasd_device { /* default expiration time in s */ unsigned long default_expires; + + struct dentry *debugfs_dentry; + struct dasd_profile profile; }; struct dasd_block { @@ -453,9 +491,8 @@ struct dasd_block { struct tasklet_struct tasklet; struct timer_list timer; -#ifdef CONFIG_DASD_PROFILE - struct dasd_profile_info_t profile; -#endif + struct dentry *debugfs_dentry; + struct dasd_profile profile; }; @@ -589,12 +626,13 @@ dasd_check_blocksize(int bsize) } /* 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 struct dasd_profile_info_t dasd_global_profile; -extern unsigned int dasd_profile_level; +extern struct dasd_profile_info dasd_global_profile_data; +extern unsigned int dasd_global_profile_level; extern const struct block_device_operations dasd_device_operations; extern struct kmem_cache *dasd_page_cache; @@ -662,6 +700,11 @@ void dasd_device_remove_stop_bits(struct dasd_device *, int); 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 */ extern int dasd_max_devindex; diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 72261e4c516d..eb4e034378cd 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -239,7 +239,7 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) */ 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; } @@ -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) { - 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; - if (copy_to_user(argp, &block->profile, - sizeof(struct dasd_profile_info_t))) + } + if (copy_to_user(argp, data, sizeof(*data))) return -EFAULT; return 0; } diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index c4a6a31bd9cd..6c3c5364d082 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -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_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 dasd_devices_show(struct seq_file *m, void *v) { @@ -167,6 +145,55 @@ static const struct file_operations dasd_devices_file_ops = { }; #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) { int i; @@ -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) { #ifdef CONFIG_DASD_PROFILE - struct dasd_profile_info_t *prof; + struct dasd_profile_info *prof; int factor; /* 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 " "switched on using 'echo set on > " "/proc/dasd/statistics'\n"); return 0; } + prof = &dasd_global_profile_data; - prof = &dasd_global_profile; /* prevent counter 'overflow' on output */ for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999; factor *= 10); @@ -245,6 +272,7 @@ static ssize_t dasd_stats_proc_write(struct file *file, { #ifdef CONFIG_DASD_PROFILE char *buffer, *str; + int rc; if (user_len > 65536) user_len = 65536; @@ -259,32 +287,40 @@ static ssize_t dasd_stats_proc_write(struct file *file, str = skip_spaces(str + 4); if (strcmp(str, "on") == 0) { /* 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 " "on\n"); } else if (strcmp(str, "off") == 0) { /* switch off and reset statistics profiling */ - memset(&dasd_global_profile, - 0, sizeof (struct dasd_profile_info_t)); - dasd_profile_level = DASD_PROFILE_OFF; + dasd_global_profile_level = DASD_PROFILE_OFF; + dasd_global_profile_reset(); + dasd_stats_all_block_off(); pr_info("The statistics feature has been switched " "off\n"); } else - goto out_error; + goto out_parse_error; } else if (strncmp(str, "reset", 5) == 0) { /* reset the statistics */ - memset(&dasd_global_profile, 0, - sizeof (struct dasd_profile_info_t)); + dasd_global_profile_reset(); + dasd_stats_all_block_reset(); pr_info("The statistics have been reset\n"); } else - goto out_error; + goto out_parse_error; kfree(buffer); return user_len; -out_error: +out_parse_error: + rc = -EINVAL; pr_warning("%s is not a supported value for /proc/dasd/statistics\n", str); +out_error: kfree(buffer); - return -EINVAL; + return rc; #else pr_warning("/proc/dasd/statistics: is not activated in this kernel\n"); return user_len; diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index a4f117d9fdc6..2c9a776bd63c 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -116,9 +116,6 @@ config S390_TAPE called tape390 and include all selected interfaces and hardware drivers. -comment "S/390 tape interface support" - depends on S390_TAPE - comment "S/390 tape hardware support" depends on S390_TAPE diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5c4e741d8221..68be6e157126 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -95,9 +95,11 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) } } -static inline u32 shared_ind_set(void) +static inline u32 clear_shared_ind(void) { - return q_indicators[TIQDIO_SHARED_IND].ind; + if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) + return 0; + return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); } /** @@ -107,7 +109,7 @@ static inline u32 shared_ind_set(void) */ static void tiqdio_thinint_handler(void *alsi, void *data) { - u32 si_used = shared_ind_set(); + u32 si_used = clear_shared_ind(); struct qdio_q *q; last_ai_time = S390_lowcore.int_clock; @@ -150,13 +152,6 @@ static void tiqdio_thinint_handler(void *alsi, void *data) qperf_inc(q, adapter_int); } rcu_read_unlock(); - - /* - * If the shared indicator was used clear it now after all queues - * were processed. - */ - if (si_used && shared_ind_set()) - xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); } static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 16e4a25596e7..f8134a44cefa 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -6,6 +6,7 @@ * Martin Schwidefsky <schwidefsky@de.ibm.com> * Ralph Wuerthner <rwuerthn@de.ibm.com> * Felix Beck <felix.beck@de.ibm.com> + * Holger Dengler <hd@linux.vnet.ibm.com> * * Adjunct processor bus. * @@ -222,47 +223,52 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind) } #endif -static inline struct ap_queue_status __ap_4096_commands_available(ap_qid_t qid, - int *support) +#ifdef CONFIG_64BIT +static inline struct ap_queue_status +__ap_query_functions(ap_qid_t qid, unsigned int *functions) { register unsigned long reg0 asm ("0") = 0UL | qid | (1UL << 23); - register struct ap_queue_status reg1 asm ("1"); - register unsigned long reg2 asm ("2") = 0UL; + register struct ap_queue_status reg1 asm ("1") = AP_QUEUE_STATUS_INVALID; + register unsigned long reg2 asm ("2"); asm volatile( ".long 0xb2af0000\n" - "0: la %1,0\n" - "1:\n" - EX_TABLE(0b, 1b) - : "+d" (reg0), "=d" (reg1), "=d" (reg2) + "0:\n" + EX_TABLE(0b, 0b) + : "+d" (reg0), "+d" (reg1), "=d" (reg2) : : "cc"); - if (reg2 & 0x6000000000000000ULL) - *support = 1; - else - *support = 0; - + *functions = (unsigned int)(reg2 >> 32); return reg1; } +#endif /** - * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA - * support. + * ap_query_functions(): Query supported functions. * @qid: The AP queue number + * @functions: Pointer to functions field. * - * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not. + * Returns + * 0 on success. + * -ENODEV if queue not valid. + * -EBUSY if device busy. + * -EINVAL if query function is not supported */ -int ap_4096_commands_available(ap_qid_t qid) +static int ap_query_functions(ap_qid_t qid, unsigned int *functions) { +#ifdef CONFIG_64BIT struct ap_queue_status status; - int i, support = 0; - status = __ap_4096_commands_available(qid, &support); + int i; + status = __ap_query_functions(qid, functions); for (i = 0; i < AP_MAX_RESET; i++) { + if (ap_queue_status_invalid_test(&status)) + return -ENODEV; + switch (status.response_code) { case AP_RESPONSE_NORMAL: - return support; + return 0; case AP_RESPONSE_RESET_IN_PROGRESS: case AP_RESPONSE_BUSY: break; @@ -270,7 +276,7 @@ int ap_4096_commands_available(ap_qid_t qid) case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: case AP_RESPONSE_INVALID_ADDRESS: - return 0; + return -ENODEV; case AP_RESPONSE_OTHERWISE_CHANGED: break; default: @@ -278,10 +284,31 @@ int ap_4096_commands_available(ap_qid_t qid) } if (i < AP_MAX_RESET - 1) { udelay(5); - status = __ap_4096_commands_available(qid, &support); + status = __ap_query_functions(qid, functions); } } - return support; + return -EBUSY; +#else + return -EINVAL; +#endif +} + +/** + * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA + * support. + * @qid: The AP queue number + * + * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not. + */ +int ap_4096_commands_available(ap_qid_t qid) +{ + unsigned int functions; + + if (ap_query_functions(qid, &functions)) + return 0; + + return test_ap_facility(functions, 1) && + test_ap_facility(functions, 2); } EXPORT_SYMBOL(ap_4096_commands_available); @@ -1135,6 +1162,7 @@ static void ap_scan_bus(struct work_struct *unused) struct device *dev; ap_qid_t qid; int queue_depth, device_type; + unsigned int device_functions; int rc, i; if (ap_select_domain() != 0) @@ -1183,14 +1211,30 @@ static void ap_scan_bus(struct work_struct *unused) INIT_LIST_HEAD(&ap_dev->list); setup_timer(&ap_dev->timeout, ap_request_timeout, (unsigned long) ap_dev); - if (device_type == 0) { + switch (device_type) { + case 0: if (ap_probe_device_type(ap_dev)) { kfree(ap_dev); continue; } - } - else + break; + case 10: + if (ap_query_functions(qid, &device_functions)) { + kfree(ap_dev); + continue; + } + if (test_ap_facility(device_functions, 3)) + ap_dev->device_type = AP_DEVICE_TYPE_CEX3C; + else if (test_ap_facility(device_functions, 4)) + ap_dev->device_type = AP_DEVICE_TYPE_CEX3A; + else { + kfree(ap_dev); + continue; + } + break; + default: ap_dev->device_type = device_type; + } ap_dev->device.bus = &ap_bus_type; ap_dev->device.parent = ap_root_device; diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 08b9738285b4..d960a6309eec 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -6,6 +6,7 @@ * Martin Schwidefsky <schwidefsky@de.ibm.com> * Ralph Wuerthner <rwuerthn@de.ibm.com> * Felix Beck <felix.beck@de.ibm.com> + * Holger Dengler <hd@linux.vnet.ibm.com> * * Adjunct processor bus header file. * @@ -72,7 +73,26 @@ struct ap_queue_status { unsigned int int_enabled : 1; unsigned int response_code : 8; unsigned int pad2 : 16; -}; +} __packed; + +#define AP_QUEUE_STATUS_INVALID \ + { 1, 1, 1, 0xF, 1, 0xFF, 0xFFFF } + +static inline +int ap_queue_status_invalid_test(struct ap_queue_status *status) +{ + struct ap_queue_status invalid = AP_QUEUE_STATUS_INVALID; + return !(memcmp(status, &invalid, sizeof(struct ap_queue_status))); +} + +#define MAX_AP_FACILITY 31 + +static inline int test_ap_facility(unsigned int function, unsigned int nr) +{ + if (nr > MAX_AP_FACILITY) + return 0; + return function & (unsigned int)(0x80000000 >> nr); +} #define AP_RESPONSE_NORMAL 0x00 #define AP_RESPONSE_Q_NOT_AVAIL 0x01 |