diff options
Diffstat (limited to 'drivers/s390')
32 files changed, 918 insertions, 240 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 86b6f1cc1b1..a1d3ddba99c 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -22,6 +22,9 @@ #include <linux/hdreg.h> #include <linux/async.h> #include <linux/mutex.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/vmalloc.h> #include <asm/ccwdev.h> #include <asm/ebcdic.h> @@ -45,6 +48,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 +75,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 +127,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 +165,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 +229,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 +285,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 +295,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 +647,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 +664,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 +793,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 = vmalloc(user_len + 1); + if (buffer == NULL) + return ERR_PTR(-ENOMEM); + if (copy_from_user(buffer, user_buf, user_len) != 0) { + vfree(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; + vfree(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; + vfree(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 +2925,7 @@ dasd_exit(void) debug_unregister(dasd_debug_area); dasd_debug_area = NULL; } + dasd_statistics_removeroot(); } /* @@ -2992,6 +3477,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_eckd.c b/drivers/s390/block/dasd_eckd.c index 30fb979d684..6e835c9fdfc 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1461,6 +1461,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device) "Read device characteristic failed, rc=%d", rc); goto out_err3; } + + if ((device->features & DASD_FEATURE_USERAW) && + !(private->rdc_data.facilities.RT_in_LR)) { + dev_err(&device->cdev->dev, "The storage server does not " + "support raw-track access\n"); + rc = -EINVAL; + goto out_err3; + } + /* find the valid cylinder size */ if (private->rdc_data.no_cyl == LV_COMPAT_CYL && private->rdc_data.long_no_cyl) diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index 77f778b7b07..16c5208c3dc 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -21,7 +21,7 @@ #include <linux/slab.h> #include <asm/uaccess.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/ebcdic.h> #include "dasd_int.h" diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index d1e4f2c1264..1dd12bd85a6 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 72261e4c516..f1a2016829f 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,12 +248,46 @@ 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) - return -EIO; - if (copy_to_user(argp, &block->profile, - sizeof(struct dasd_profile_info_t))) - return -EFAULT; - return 0; + struct dasd_profile_info_t *data; + int rc = 0; + + 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); + rc = -EIO; + goto out; + } + if (copy_to_user(argp, data, sizeof(*data))) + rc = -EFAULT; +out: + kfree(data); + return rc; } #else static int dasd_ioctl_reset_profile(struct dasd_block *block) diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index c4a6a31bd9c..e12989fff4f 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; - kfree(buffer); + goto out_parse_error; + vfree(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); - kfree(buffer); - return -EINVAL; +out_error: + vfree(buffer); + 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 a4f117d9fdc..2c9a776bd63 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/char/sclp_async.c b/drivers/s390/char/sclp_async.c index 7ad30e72f86..5f9f929e891 100644 --- a/drivers/s390/char/sclp_async.c +++ b/drivers/s390/char/sclp_async.c @@ -82,12 +82,9 @@ static int proc_handler_callhome(struct ctl_table *ctl, int write, return -EFAULT; } else { len = *count; - rc = copy_from_user(buf, buffer, sizeof(buf)); - if (rc != 0) - return -EFAULT; - buf[sizeof(buf) - 1] = '\0'; - if (strict_strtoul(buf, 0, &val) != 0) - return -EINVAL; + rc = kstrtoul_from_user(buffer, len, 0, &val); + if (rc) + return rc; if (val != 0 && val != 1) return -EINVAL; callhome_enabled = val; diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index be55fb2b1b1..837e010299a 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -383,8 +383,10 @@ static int sclp_attach_storage(u8 id) switch (sccb->header.response_code) { case 0x0020: set_bit(id, sclp_storage_ids); - for (i = 0; i < sccb->assigned; i++) - sclp_unassign_storage(sccb->entries[i] >> 16); + for (i = 0; i < sccb->assigned; i++) { + if (sccb->entries[i]) + sclp_unassign_storage(sccb->entries[i] >> 16); + } break; default: rc = -EIO; diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c index 05909a7df8b..a90a02c28d6 100644 --- a/drivers/s390/char/sclp_quiesce.c +++ b/drivers/s390/char/sclp_quiesce.c @@ -13,7 +13,7 @@ #include <linux/smp.h> #include <linux/init.h> #include <linux/reboot.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/ptrace.h> #include <asm/sigp.h> #include <asm/smp.h> diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index c837d7419a6..524d988d89d 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -21,7 +21,7 @@ #include <linux/types.h> #include <linux/interrupt.h> #include <linux/spinlock.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/uaccess.h> #include <asm/cpcmd.h> #include <asm/debug.h> diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 12ef9121d4f..11312f401c7 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -258,13 +258,13 @@ static int vmwdt_suspend(void) if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { pr_err("The system cannot be suspended while the watchdog" " is in use\n"); - return NOTIFY_BAD; + return notifier_from_errno(-EBUSY); } if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) { clear_bit(VMWDT_OPEN, &vmwdt_is_open); pr_err("The system cannot be suspended while the watchdog" " is running\n"); - return NOTIFY_BAD; + return notifier_from_errno(-EBUSY); } return NOTIFY_DONE; } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index c47b25fd3f4..92d7324acb1 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -814,8 +814,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event, mutex_unlock(&css->mutex); continue; } - if (__chsc_do_secm(css, 0)) - ret = NOTIFY_BAD; + ret = __chsc_do_secm(css, 0); + ret = notifier_from_errno(ret); mutex_unlock(&css->mutex); } break; @@ -831,8 +831,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event, mutex_unlock(&css->mutex); continue; } - if (__chsc_do_secm(css, 1)) - ret = NOTIFY_BAD; + ret = __chsc_do_secm(css, 1); + ret = notifier_from_errno(ret); mutex_unlock(&css->mutex); } /* search for subchannels, which appeared during hibernation */ diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 7e297c7bb5f..0b7245c72d5 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -2,7 +2,7 @@ #define S390_DEVICE_H #include <asm/ccwdev.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <linux/wait.h> #include <linux/notifier.h> #include "io_sch.h" diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 7bc643f3f5a..e5c966462c5 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -14,6 +14,8 @@ #include "chsc.h" #define QDIO_BUSY_BIT_PATIENCE (100 << 12) /* 100 microseconds */ +#define QDIO_BUSY_BIT_RETRY_DELAY 10 /* 10 milliseconds */ +#define QDIO_BUSY_BIT_RETRIES 1000 /* = 10s retry time */ #define QDIO_INPUT_THRESHOLD (500 << 12) /* 500 microseconds */ /* diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index f8b03a636e4..0e615cb912d 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -188,19 +188,13 @@ static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf, struct qdio_irq *irq_ptr = seq->private; struct qdio_q *q; unsigned long val; - char buf[8]; int ret, i; if (!irq_ptr) return 0; - if (count >= sizeof(buf)) - return -EINVAL; - if (copy_from_user(&buf, ubuf, count)) - return -EFAULT; - buf[count] = 0; - - ret = strict_strtoul(buf, 10, &val); - if (ret < 0) + + ret = kstrtoul_from_user(ubuf, count, 10, &val); + if (ret) return ret; switch (val) { diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 570d4da1069..288c9140290 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -15,7 +15,7 @@ #include <linux/delay.h> #include <linux/gfp.h> #include <linux/kernel_stat.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/debug.h> #include <asm/qdio.h> @@ -313,7 +313,7 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) unsigned long schid = *((u32 *) &q->irq_ptr->schid); unsigned int fc = QDIO_SIGA_WRITE; u64 start_time = 0; - int cc; + int retries = 0, cc; if (is_qebsm(q)) { schid = q->irq_ptr->sch_token; @@ -325,6 +325,7 @@ again: /* hipersocket busy condition */ if (unlikely(*busy_bit)) { WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2); + retries++; if (!start_time) { start_time = get_clock(); @@ -333,6 +334,11 @@ again: if ((get_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE) goto again; } + if (retries) { + DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, + "%4x cc2 BB1:%1d", SCH_NO(q), q->nr); + DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "count:%u", retries); + } return cc; } @@ -728,13 +734,14 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) static int qdio_kick_outbound_q(struct qdio_q *q) { + int retries = 0, cc; unsigned int busy_bit; - int cc; if (!need_siga_out(q)) return 0; DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); +retry: qperf_inc(q, siga_write); cc = qdio_siga_output(q, &busy_bit); @@ -743,7 +750,11 @@ static int qdio_kick_outbound_q(struct qdio_q *q) break; case 2: if (busy_bit) { - DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); + while (++retries < QDIO_BUSY_BIT_RETRIES) { + mdelay(QDIO_BUSY_BIT_RETRY_DELAY); + goto retry; + } + DBF_ERROR("%4x cc2 BBC:%1d", SCH_NO(q), q->nr); cc |= QDIO_ERROR_SIGA_BUSY; } else DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr); @@ -753,6 +764,10 @@ static int qdio_kick_outbound_q(struct qdio_q *q) DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc); break; } + if (retries) { + DBF_ERROR("%4x cc2 BB2:%1d", SCH_NO(q), q->nr); + DBF_ERROR("count:%u", retries); + } return cc; } diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5c4e741d822..2a1d4dfaf85 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -9,7 +9,7 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/kernel_stat.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/debug.h> #include <asm/qdio.h> #include <asm/airq.h> @@ -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 16e4a25596e..b77ae519d79 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. * @@ -40,7 +41,7 @@ #include <linux/mutex.h> #include <asm/reset.h> #include <asm/airq.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/system.h> #include <asm/isc.h> #include <linux/hrtimer.h> @@ -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 08b9738285b..d960a6309ee 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 diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 8e65447f76b..88ad33ed5d3 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -36,7 +36,7 @@ #include <linux/seq_file.h> #include <linux/compat.h> #include <linux/slab.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/uaccess.h> #include <linux/hw_random.h> diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 2176d00b395..da171b5f399 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -30,7 +30,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/err.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/uaccess.h> #include "ap_bus.h" diff --git a/drivers/s390/crypto/zcrypt_mono.c b/drivers/s390/crypto/zcrypt_mono.c index 44253fdd413..eb313c3fb2d 100644 --- a/drivers/s390/crypto/zcrypt_mono.c +++ b/drivers/s390/crypto/zcrypt_mono.c @@ -32,7 +32,7 @@ #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/compat.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/uaccess.h> #include "ap_bus.h" diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 1afb69c75fe..d84816f144d 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -30,7 +30,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/err.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/uaccess.h> #include "ap_bus.h" diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index aa4c050a569..bdbdbe19299 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -30,7 +30,7 @@ #include <linux/init.h> #include <linux/gfp.h> #include <linux/err.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/uaccess.h> #include "ap_bus.h" diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 4f85eb725f4..dd4737808e0 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -31,7 +31,7 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/slab.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/uaccess.h> #include "ap_bus.h" diff --git a/drivers/s390/net/ctcm_mpc.h b/drivers/s390/net/ctcm_mpc.h index 5336120cddf..1fa07b0c11c 100644 --- a/drivers/s390/net/ctcm_mpc.h +++ b/drivers/s390/net/ctcm_mpc.h @@ -12,6 +12,7 @@ #ifndef _CTC_MPC_H_ #define _CTC_MPC_H_ +#include <linux/interrupt.h> #include <linux/skbuff.h> #include "fsm.h" diff --git a/drivers/s390/net/fsm.h b/drivers/s390/net/fsm.h index 1e8b235d95b..a4510cf5903 100644 --- a/drivers/s390/net/fsm.h +++ b/drivers/s390/net/fsm.h @@ -8,7 +8,7 @@ #include <linux/slab.h> #include <linux/sched.h> #include <linux/string.h> -#include <asm/atomic.h> +#include <linux/atomic.h> /** * Define this to get debugging messages. diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index d3cee33e554..26a4110eeb2 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -720,7 +720,7 @@ struct qeth_card { wait_queue_head_t wait_q; spinlock_t vlanlock; spinlock_t mclock; - struct vlan_group *vlangrp; + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; struct list_head vid_list; struct list_head mc_list; struct work_struct kernel_thread_starter; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index dd08f7b42fb..4550573c25e 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1097,7 +1097,6 @@ static int qeth_setup_card(struct qeth_card *card) card->dev = NULL; spin_lock_init(&card->vlanlock); spin_lock_init(&card->mclock); - card->vlangrp = NULL; spin_lock_init(&card->lock); spin_lock_init(&card->ip_lock); spin_lock_init(&card->thread_mask_lock); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index fd69da3fa6b..fafb8c29954 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/bitops.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/kernel.h> @@ -23,6 +24,7 @@ #include <linux/inetdevice.h> #include <linux/igmp.h> #include <linux/slab.h> +#include <linux/if_vlan.h> #include <net/ip.h> #include <net/arp.h> @@ -1696,16 +1698,18 @@ static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev) static void qeth_l3_add_vlan_mc(struct qeth_card *card) { struct in_device *in_dev; - struct vlan_group *vg; - int i; + u16 vid; QETH_CARD_TEXT(card, 4, "addmcvl"); - if (!qeth_is_supported(card, IPA_FULL_VLAN) || (card->vlangrp == NULL)) + if (!qeth_is_supported(card, IPA_FULL_VLAN)) return; - vg = card->vlangrp; - for (i = 0; i < VLAN_N_VID; i++) { - struct net_device *netdev = vlan_group_get_device(vg, i); + for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { + struct net_device *netdev; + + rcu_read_lock(); + netdev = __vlan_find_dev_deep(card->dev, vid); + rcu_read_unlock(); if (netdev == NULL || !(netdev->flags & IFF_UP)) continue; @@ -1759,16 +1763,16 @@ static void qeth_l3_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev) static void qeth_l3_add_vlan_mc6(struct qeth_card *card) { struct inet6_dev *in_dev; - struct vlan_group *vg; - int i; + u16 vid; QETH_CARD_TEXT(card, 4, "admc6vl"); - if (!qeth_is_supported(card, IPA_FULL_VLAN) || (card->vlangrp == NULL)) + if (!qeth_is_supported(card, IPA_FULL_VLAN)) return; - vg = card->vlangrp; - for (i = 0; i < VLAN_N_VID; i++) { - struct net_device *netdev = vlan_group_get_device(vg, i); + for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { + struct net_device *netdev; + + netdev = __vlan_find_dev_deep(card->dev, vid); if (netdev == NULL || !(netdev->flags & IFF_UP)) continue; @@ -1806,10 +1810,12 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card, struct in_device *in_dev; struct in_ifaddr *ifa; struct qeth_ipaddr *addr; + struct net_device *netdev; QETH_CARD_TEXT(card, 4, "frvaddr4"); - in_dev = in_dev_get(vlan_group_get_device(card->vlangrp, vid)); + netdev = __vlan_find_dev_deep(card->dev, vid); + in_dev = in_dev_get(netdev); if (!in_dev) return; for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { @@ -1832,10 +1838,12 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card, struct inet6_dev *in6_dev; struct inet6_ifaddr *ifa; struct qeth_ipaddr *addr; + struct net_device *netdev; QETH_CARD_TEXT(card, 4, "frvaddr6"); - in6_dev = in6_dev_get(vlan_group_get_device(card->vlangrp, vid)); + netdev = __vlan_find_dev_deep(card->dev, vid); + in6_dev = in6_dev_get(netdev); if (!in6_dev) return; list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { @@ -1856,26 +1864,15 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card, static void qeth_l3_free_vlan_addresses(struct qeth_card *card, unsigned short vid) { - if (!card->vlangrp) - return; qeth_l3_free_vlan_addresses4(card, vid); qeth_l3_free_vlan_addresses6(card, vid); } -static void qeth_l3_vlan_rx_register(struct net_device *dev, - struct vlan_group *grp) +static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { struct qeth_card *card = dev->ml_priv; - unsigned long flags; - - QETH_CARD_TEXT(card, 4, "vlanreg"); - spin_lock_irqsave(&card->vlanlock, flags); - card->vlangrp = grp; - spin_unlock_irqrestore(&card->vlanlock, flags); -} -static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) -{ + set_bit(vid, card->active_vlans); return; } @@ -1892,7 +1889,7 @@ static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) spin_lock_irqsave(&card->vlanlock, flags); /* unregister IP addresses of vlan device */ qeth_l3_free_vlan_addresses(card, vid); - vlan_group_set_device(card->vlangrp, vid, NULL); + clear_bit(vid, card->active_vlans); spin_unlock_irqrestore(&card->vlanlock, flags); qeth_l3_set_multicast_list(card->dev); } @@ -2014,10 +2011,8 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card, &vlan_tag); len = skb->len; if (is_vlan && !card->options.sniffer) - vlan_gro_receive(&card->napi, card->vlangrp, - vlan_tag, skb); - else - napi_gro_receive(&card->napi, skb); + __vlan_hwaccel_put_tag(skb, vlan_tag); + napi_gro_receive(&card->napi, skb); break; case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */ skb->pkt_type = PACKET_HOST; @@ -2118,15 +2113,15 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev, struct qeth_card *card) { int rc = 0; - struct vlan_group *vg; - int i; + u16 vid; - vg = card->vlangrp; - if (!vg) - return rc; + for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { + struct net_device *netdev; - for (i = 0; i < VLAN_N_VID; i++) { - if (vlan_group_get_device(vg, i) == dev) { + rcu_read_lock(); + netdev = __vlan_find_dev_deep(dev, vid); + rcu_read_unlock(); + if (netdev == dev) { rc = QETH_VLAN_CARD; break; } @@ -2742,9 +2737,14 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb) { int cast_type = RTN_UNSPEC; - - if (skb_dst(skb) && skb_dst(skb)->neighbour) { - cast_type = skb_dst(skb)->neighbour->type; + struct neighbour *n = NULL; + struct dst_entry *dst; + + dst = skb_dst(skb); + if (dst) + n = dst_get_neighbour(dst); + if (n) { + cast_type = n->type; if ((cast_type == RTN_BROADCAST) || (cast_type == RTN_MULTICAST) || (cast_type == RTN_ANYCAST)) @@ -2787,6 +2787,9 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb) static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, struct sk_buff *skb, int ipv, int cast_type) { + struct neighbour *n = NULL; + struct dst_entry *dst; + memset(hdr, 0, sizeof(struct qeth_hdr)); hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; hdr->hdr.l3.ext_flags = 0; @@ -2795,7 +2798,7 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, * before we're going to overwrite this location with next hop ip. * v6 uses passthrough, v4 sets the tag in the QDIO header. */ - if (card->vlangrp && vlan_tx_tag_present(skb)) { + if (vlan_tx_tag_present(skb)) { if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD)) hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME; else @@ -2804,13 +2807,16 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, } hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr); + dst = skb_dst(skb); + if (dst) + n = dst_get_neighbour(dst); if (ipv == 4) { /* IPv4 */ hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type); memset(hdr->hdr.l3.dest_addr, 0, 12); - if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) { + if (n) { *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = - *((u32 *) skb_dst(skb)->neighbour->primary_key); + *((u32 *) n->primary_key); } else { /* fill in destination address used in ip header */ *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = @@ -2821,9 +2827,9 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type); if (card->info.type == QETH_CARD_TYPE_IQD) hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU; - if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) { + if (n) { memcpy(hdr->hdr.l3.dest_addr, - skb_dst(skb)->neighbour->primary_key, 16); + n->primary_key, 16); } else { /* fill in destination address used in ip header */ memcpy(hdr->hdr.l3.dest_addr, @@ -2977,8 +2983,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) skb_pull(new_skb, ETH_HLEN); } - if (ipv != 4 && card->vlangrp && - vlan_tx_tag_present(new_skb)) { + if (ipv != 4 && vlan_tx_tag_present(new_skb)) { skb_push(new_skb, VLAN_HLEN); skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4); skb_copy_to_linear_data_offset(new_skb, 4, @@ -3222,14 +3227,13 @@ static const struct net_device_ops qeth_l3_netdev_ops = { .ndo_start_xmit = qeth_l3_hard_start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_multicast_list = qeth_l3_set_multicast_list, - .ndo_do_ioctl = qeth_l3_do_ioctl, - .ndo_change_mtu = qeth_change_mtu, - .ndo_fix_features = qeth_l3_fix_features, - .ndo_set_features = qeth_l3_set_features, - .ndo_vlan_rx_register = qeth_l3_vlan_rx_register, + .ndo_do_ioctl = qeth_l3_do_ioctl, + .ndo_change_mtu = qeth_change_mtu, + .ndo_fix_features = qeth_l3_fix_features, + .ndo_set_features = qeth_l3_set_features, .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid, - .ndo_tx_timeout = qeth_tx_timeout, + .ndo_tx_timeout = qeth_tx_timeout, }; static const struct net_device_ops qeth_l3_osa_netdev_ops = { @@ -3239,14 +3243,13 @@ static const struct net_device_ops qeth_l3_osa_netdev_ops = { .ndo_start_xmit = qeth_l3_hard_start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_multicast_list = qeth_l3_set_multicast_list, - .ndo_do_ioctl = qeth_l3_do_ioctl, - .ndo_change_mtu = qeth_change_mtu, - .ndo_fix_features = qeth_l3_fix_features, - .ndo_set_features = qeth_l3_set_features, - .ndo_vlan_rx_register = qeth_l3_vlan_rx_register, + .ndo_do_ioctl = qeth_l3_do_ioctl, + .ndo_change_mtu = qeth_change_mtu, + .ndo_fix_features = qeth_l3_fix_features, + .ndo_set_features = qeth_l3_set_features, .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid, - .ndo_tx_timeout = qeth_tx_timeout, + .ndo_tx_timeout = qeth_tx_timeout, .ndo_neigh_setup = qeth_l3_neigh_setup, }; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 2a4991d6d4d..7cac873c738 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -13,7 +13,7 @@ #include <linux/slab.h> #include <scsi/fc/fc_fcp.h> #include <scsi/scsi_eh.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "zfcp_ext.h" #include "zfcp_dbf.h" #include "zfcp_fc.h" |