diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/block/dasd_proc.c | 16 | ||||
-rw-r--r-- | drivers/s390/block/dcssblk.c | 8 | ||||
-rw-r--r-- | drivers/s390/char/con3215.c | 5 | ||||
-rw-r--r-- | drivers/s390/char/sclp_config.c | 17 | ||||
-rw-r--r-- | drivers/s390/char/sclp_tty.c | 4 | ||||
-rw-r--r-- | drivers/s390/char/sclp_vt220.c | 6 | ||||
-rw-r--r-- | drivers/s390/char/tape_proc.c | 9 | ||||
-rw-r--r-- | drivers/s390/char/tty3270.c | 3 | ||||
-rw-r--r-- | drivers/s390/cio/blacklist.c | 7 | ||||
-rw-r--r-- | drivers/s390/cio/ccwgroup.c | 8 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 9 | ||||
-rw-r--r-- | drivers/s390/cio/cio.h | 3 | ||||
-rw-r--r-- | drivers/s390/cio/cmf.c | 11 | ||||
-rw-r--r-- | drivers/s390/cio/css.c | 10 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 17 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 10 | ||||
-rw-r--r-- | drivers/s390/cio/device_ops.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/qdio.c | 14 | ||||
-rw-r--r-- | drivers/s390/kvm/Makefile | 9 | ||||
-rw-r--r-- | drivers/s390/kvm/kvm_virtio.c | 339 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_fsf.c | 39 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_fsf.h | 18 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_scsi.c | 114 |
24 files changed, 595 insertions, 85 deletions
diff --git a/drivers/s390/Makefile b/drivers/s390/Makefile index 5a888704a8d0..4f4e7cf105d4 100644 --- a/drivers/s390/Makefile +++ b/drivers/s390/Makefile @@ -5,7 +5,7 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w obj-y += s390mach.o sysinfo.o s390_rdev.o -obj-y += cio/ block/ char/ crypto/ net/ scsi/ +obj-y += cio/ block/ char/ crypto/ net/ scsi/ kvm/ drivers-y += drivers/s390/built-in.o diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 556063e8f7a9..03c0e40a92ff 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -157,6 +157,7 @@ static int dasd_devices_open(struct inode *inode, struct file *file) } static const struct file_operations dasd_devices_file_ops = { + .owner = THIS_MODULE, .open = dasd_devices_open, .read = seq_read, .llseek = seq_lseek, @@ -311,17 +312,16 @@ out_error: int dasd_proc_init(void) { - dasd_proc_root_entry = proc_mkdir("dasd", &proc_root); + dasd_proc_root_entry = proc_mkdir("dasd", NULL); if (!dasd_proc_root_entry) goto out_nodasd; dasd_proc_root_entry->owner = THIS_MODULE; - dasd_devices_entry = create_proc_entry("devices", - S_IFREG | S_IRUGO | S_IWUSR, - dasd_proc_root_entry); + dasd_devices_entry = proc_create("devices", + S_IFREG | S_IRUGO | S_IWUSR, + dasd_proc_root_entry, + &dasd_devices_file_ops); if (!dasd_devices_entry) goto out_nodevices; - dasd_devices_entry->proc_fops = &dasd_devices_file_ops; - dasd_devices_entry->owner = THIS_MODULE; dasd_statistics_entry = create_proc_entry("statistics", S_IFREG | S_IRUGO | S_IWUSR, dasd_proc_root_entry); @@ -335,7 +335,7 @@ dasd_proc_init(void) out_nostatistics: remove_proc_entry("devices", dasd_proc_root_entry); out_nodevices: - remove_proc_entry("dasd", &proc_root); + remove_proc_entry("dasd", NULL); out_nodasd: return -ENOENT; } @@ -345,5 +345,5 @@ dasd_proc_exit(void) { remove_proc_entry("devices", dasd_proc_root_entry); remove_proc_entry("statistics", dasd_proc_root_entry); - remove_proc_entry("dasd", &proc_root); + remove_proc_entry("dasd", NULL); } diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 04787eab1016..bb52d2fbac18 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -36,7 +36,7 @@ static int dcssblk_open(struct inode *inode, struct file *filp); static int dcssblk_release(struct inode *inode, struct file *filp); static int dcssblk_make_request(struct request_queue *q, struct bio *bio); static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, - unsigned long *data); + void **kaddr, unsigned long *pfn); static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; @@ -636,7 +636,7 @@ fail: static int dcssblk_direct_access (struct block_device *bdev, sector_t secnum, - unsigned long *data) + void **kaddr, unsigned long *pfn) { struct dcssblk_dev_info *dev_info; unsigned long pgoff; @@ -649,7 +649,9 @@ dcssblk_direct_access (struct block_device *bdev, sector_t secnum, pgoff = secnum / (PAGE_SIZE / 512); if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) return -ERANGE; - *data = (unsigned long) (dev_info->start+pgoff*PAGE_SIZE); + *kaddr = (void *) (dev_info->start+pgoff*PAGE_SIZE); + *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; + return 0; } diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 0e1f35c9ed9d..3e5653c92f4b 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -982,15 +982,16 @@ tty3215_write(struct tty_struct * tty, /* * Put character routine for 3215 ttys */ -static void +static int tty3215_put_char(struct tty_struct *tty, unsigned char ch) { struct raw3215_info *raw; if (!tty) - return; + return 0; raw = (struct raw3215_info *) tty->driver_data; raw3215_putchar(raw, ch); + return 1; } static void diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index b8f35bc52b7b..9e784d5f7f57 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -10,6 +10,7 @@ #include <linux/cpu.h> #include <linux/sysdev.h> #include <linux/workqueue.h> +#include <asm/smp.h> #include "sclp.h" #define TAG "sclp_config: " @@ -19,9 +20,11 @@ struct conf_mgm_data { u8 ev_qualifier; } __attribute__((packed)); +#define EV_QUAL_CPU_CHANGE 1 #define EV_QUAL_CAP_CHANGE 3 static struct work_struct sclp_cpu_capability_work; +static struct work_struct sclp_cpu_change_work; static void sclp_cpu_capability_notify(struct work_struct *work) { @@ -37,13 +40,24 @@ static void sclp_cpu_capability_notify(struct work_struct *work) put_online_cpus(); } +static void sclp_cpu_change_notify(struct work_struct *work) +{ + smp_rescan_cpus(); +} + static void sclp_conf_receiver_fn(struct evbuf_header *evbuf) { struct conf_mgm_data *cdata; cdata = (struct conf_mgm_data *)(evbuf + 1); - if (cdata->ev_qualifier == EV_QUAL_CAP_CHANGE) + switch (cdata->ev_qualifier) { + case EV_QUAL_CPU_CHANGE: + schedule_work(&sclp_cpu_change_work); + break; + case EV_QUAL_CAP_CHANGE: schedule_work(&sclp_cpu_capability_work); + break; + } } static struct sclp_register sclp_conf_register = @@ -57,6 +71,7 @@ static int __init sclp_conf_init(void) int rc; INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify); + INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify); rc = sclp_register(&sclp_conf_register); if (rc) { diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index e3b3d390b4a3..40b11521cd20 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -412,14 +412,14 @@ sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) * - including previous characters from sclp_tty_put_char() and strings from * sclp_write() without final '\n' - will be written. */ -static void +static int sclp_tty_put_char(struct tty_struct *tty, unsigned char ch) { sclp_tty_chars[sclp_tty_chars_count++] = ch; if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); sclp_tty_chars_count = 0; - } + } return 1; } /* diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index ed507594e62b..35707c04e613 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -524,11 +524,15 @@ sclp_vt220_close(struct tty_struct *tty, struct file *filp) * NOTE: include/linux/tty_driver.h specifies that a character should be * ignored if there is no room in the queue. This driver implements a different * semantic in that it will block when there is no more room left. + * + * FIXME: putchar can currently be called from BH and other non blocking + * handlers so this semantic isn't a good idea. */ -static void +static int sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch) { __sclp_vt220_write(&ch, 1, 0, 0, 1); + return 1; } /* diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c index c9b96d51b28f..e7c888c14e71 100644 --- a/drivers/s390/char/tape_proc.c +++ b/drivers/s390/char/tape_proc.c @@ -111,6 +111,7 @@ static int tape_proc_open(struct inode *inode, struct file *file) static const struct file_operations tape_proc_ops = { + .owner = THIS_MODULE, .open = tape_proc_open, .read = seq_read, .llseek = seq_lseek, @@ -124,14 +125,12 @@ void tape_proc_init(void) { tape_proc_devices = - create_proc_entry ("tapedevices", S_IFREG | S_IRUGO | S_IWUSR, - &proc_root); + proc_create("tapedevices", S_IFREG | S_IRUGO | S_IWUSR, NULL, + &tape_proc_ops); if (tape_proc_devices == NULL) { PRINT_WARN("tape: Cannot register procfs entry tapedevices\n"); return; } - tape_proc_devices->proc_fops = &tape_proc_ops; - tape_proc_devices->owner = THIS_MODULE; } /* @@ -141,5 +140,5 @@ void tape_proc_cleanup(void) { if (tape_proc_devices != NULL) - remove_proc_entry ("tapedevices", &proc_root); + remove_proc_entry ("tapedevices", NULL); } diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 70b1980a08b6..c1f2adefad41 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -965,7 +965,7 @@ tty3270_write_room(struct tty_struct *tty) * Insert character into the screen at the current position with the * current color and highlight. This function does NOT do cursor movement. */ -static void +static int tty3270_put_character(struct tty3270 *tp, char ch) { struct tty3270_line *line; @@ -986,6 +986,7 @@ tty3270_put_character(struct tty3270 *tp, char ch) cell->character = tp->view.ascebc[(unsigned int) ch]; cell->highlight = tp->highlight; cell->f_color = tp->f_color; + return 1; } /* diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index e8597ec92247..40ef948fcb3a 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -374,13 +374,10 @@ cio_ignore_proc_init (void) { struct proc_dir_entry *entry; - entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, - &proc_root); + entry = proc_create("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, NULL, + &cio_ignore_proc_fops); if (!entry) return -ENOENT; - - entry->proc_fops = &cio_ignore_proc_fops; - return 0; } diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index f38923e38e92..26a930e832bd 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -16,7 +16,6 @@ #include <linux/ctype.h> #include <linux/dcache.h> -#include <asm/semaphore.h> #include <asm/ccwdev.h> #include <asm/ccwgroup.h> @@ -374,7 +373,7 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const { struct ccwgroup_device *gdev; struct ccwgroup_driver *gdrv; - unsigned int value; + unsigned long value; int ret; gdev = to_ccwgroupdev(dev); @@ -385,7 +384,9 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const if (!try_module_get(gdrv->owner)) return -EINVAL; - value = simple_strtoul(buf, NULL, 0); + ret = strict_strtoul(buf, 0, &value); + if (ret) + goto out; ret = count; if (value == 1) ccwgroup_set_online(gdev); @@ -393,6 +394,7 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const ccwgroup_set_offline(gdev); else ret = -EINVAL; +out: module_put(gdrv->owner); return ret; } diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 23ffcc4768a7..08a578161306 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -407,8 +407,7 @@ cio_modify (struct subchannel *sch) /* * Enable subchannel. */ -int cio_enable_subchannel(struct subchannel *sch, unsigned int isc, - u32 intparm) +int cio_enable_subchannel(struct subchannel *sch, u32 intparm) { char dbf_txt[15]; int ccode; @@ -426,7 +425,7 @@ int cio_enable_subchannel(struct subchannel *sch, unsigned int isc, for (retry = 5, ret = 0; retry > 0; retry--) { sch->schib.pmcw.ena = 1; - sch->schib.pmcw.isc = isc; + sch->schib.pmcw.isc = sch->isc; sch->schib.pmcw.intparm = intparm; ret = cio_modify(sch); if (ret == -ENODEV) @@ -600,6 +599,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) else sch->opm = chp_get_sch_opm(sch); sch->lpm = sch->schib.pmcw.pam & sch->opm; + sch->isc = 3; CIO_DEBUG(KERN_INFO, 0, "Detected device %04x on subchannel 0.%x.%04X" @@ -610,13 +610,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) /* * We now have to initially ... - * ... set "interruption subclass" * ... enable "concurrent sense" * ... enable "multipath mode" if more than one * CHPID is available. This is done regardless * whether multiple paths are available for us. */ - sch->schib.pmcw.isc = 3; /* could be smth. else */ sch->schib.pmcw.csense = 1; /* concurrent sense */ sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) @@ -812,6 +810,7 @@ cio_probe_console(void) * enable console I/O-interrupt subclass 7 */ ctl_set_bit(6, 24); + console_subchannel.isc = 7; console_subchannel.schib.pmcw.isc = 7; console_subchannel.schib.pmcw.intparm = (u32)(addr_t)&console_subchannel; diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 08f2235c5a6f..3c75412904dc 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -74,6 +74,7 @@ struct subchannel { __u8 lpm; /* logical path mask */ __u8 opm; /* operational path mask */ struct schib schib; /* subchannel information block */ + int isc; /* desired interruption subclass */ struct chsc_ssd_info ssd_info; /* subchannel description */ struct device dev; /* entry in device tree */ struct css_driver *driver; @@ -85,7 +86,7 @@ struct subchannel { #define to_subchannel(n) container_of(n, struct subchannel, dev) extern int cio_validate_subchannel (struct subchannel *, struct subchannel_id); -extern int cio_enable_subchannel(struct subchannel *, unsigned int, u32); +extern int cio_enable_subchannel(struct subchannel *, u32); extern int cio_disable_subchannel (struct subchannel *); extern int cio_cancel (struct subchannel *); extern int cio_clear (struct subchannel *); diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index f4c132ab39ed..2808b6833b9e 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -1219,16 +1219,21 @@ static ssize_t cmb_enable_store(struct device *dev, { struct ccw_device *cdev; int ret; + unsigned long val; + + ret = strict_strtoul(buf, 16, &val); + if (ret) + return ret; cdev = to_ccwdev(dev); - switch (buf[0]) { - case '0': + switch (val) { + case 0: ret = disable_cmf(cdev); if (ret) dev_info(&cdev->dev, "disable_cmf failed (%d)\n", ret); break; - case '1': + case 1: ret = enable_cmf(cdev); if (ret && ret != -EBUSY) dev_info(&cdev->dev, "enable_cmf failed (%d)\n", ret); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index c1afab5f72d6..595e327d2f76 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -705,13 +705,17 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr, { struct channel_subsystem *css = to_css(dev); int ret; + unsigned long val; + ret = strict_strtoul(buf, 16, &val); + if (ret) + return ret; mutex_lock(&css->mutex); - switch (buf[0]) { - case '0': + switch (val) { + case 0: ret = css->cm_enabled ? chsc_secm(css, 0) : 0; break; - case '1': + case 1: ret = css->cm_enabled ? 0 : chsc_secm(css, 1); break; default: diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e0c7adb8958e..abfd601d237a 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -512,8 +512,8 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ccw_device *cdev = to_ccwdev(dev); - int i, force; - char *tmp; + int force, ret; + unsigned long i; if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) return -EAGAIN; @@ -525,25 +525,30 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, if (!strncmp(buf, "force\n", count)) { force = 1; i = 1; + ret = 0; } else { force = 0; - i = simple_strtoul(buf, &tmp, 16); + ret = strict_strtoul(buf, 16, &i); } - + if (ret) + goto out; switch (i) { case 0: online_store_handle_offline(cdev); + ret = count; break; case 1: online_store_handle_online(cdev, force); + ret = count; break; default: - count = -EINVAL; + ret = -EINVAL; } +out: if (cdev->drv) module_put(cdev->drv->owner); atomic_set(&cdev->private->onoff, 0); - return count; + return ret; } static ssize_t diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 4b92c84fb438..99403b0a97a7 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -555,8 +555,7 @@ ccw_device_recognition(struct ccw_device *cdev) (cdev->private->state != DEV_STATE_BOXED)) return -EINVAL; sch = to_subchannel(cdev->dev.parent); - ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc, - (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); if (ret != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return ret; @@ -667,8 +666,7 @@ ccw_device_online(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); if (css_init_done && !get_device(&cdev->dev)) return -ENODEV; - ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc, - (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); if (ret != 0) { /* Couldn't enable the subchannel for i/o. Sick device. */ if (ret == -ENODEV) @@ -1048,8 +1046,7 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); - if (cio_enable_subchannel(sch, sch->schib.pmcw.isc, - (u32)(addr_t)sch) != 0) + if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return; @@ -1082,7 +1079,6 @@ device_trigger_reprobe(struct subchannel *sch) */ sch->lpm = sch->schib.pmcw.pam & sch->opm; /* Re-set some bits in the pmcw that were lost. */ - sch->schib.pmcw.isc = 3; sch->schib.pmcw.csense = 1; sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index a1718a0aa539..f308ad55a6d5 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -508,7 +508,7 @@ ccw_device_stlck(struct ccw_device *cdev) return -ENOMEM; } spin_lock_irqsave(sch->lock, flags); - ret = cio_enable_subchannel(sch, 3, (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); if (ret) goto out_unlock; /* diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index c359386708e9..445cf364e461 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -38,11 +38,11 @@ #include <linux/proc_fs.h> #include <linux/timer.h> #include <linux/mempool.h> +#include <linux/semaphore.h> #include <asm/ccwdev.h> #include <asm/io.h> #include <asm/atomic.h> -#include <asm/semaphore.h> #include <asm/timex.h> #include <asm/debug.h> @@ -3632,7 +3632,7 @@ qdio_add_procfs_entry(void) { proc_perf_file_registration=0; qdio_perf_proc_file=create_proc_entry(QDIO_PERF, - S_IFREG|0444,&proc_root); + S_IFREG|0444,NULL); if (qdio_perf_proc_file) { qdio_perf_proc_file->read_proc=&qdio_perf_procfile_read; } else proc_perf_file_registration=-1; @@ -3647,7 +3647,7 @@ static void qdio_remove_procfs_entry(void) { if (!proc_perf_file_registration) /* means if it went ok earlier */ - remove_proc_entry(QDIO_PERF,&proc_root); + remove_proc_entry(QDIO_PERF,NULL); } /** @@ -3663,11 +3663,11 @@ qdio_performance_stats_show(struct bus_type *bus, char *buf) static ssize_t qdio_performance_stats_store(struct bus_type *bus, const char *buf, size_t count) { - char *tmp; - int i; + unsigned long i; + int ret; - i = simple_strtoul(buf, &tmp, 16); - if ((i == 0) || (i == 1)) { + ret = strict_strtoul(buf, 16, &i); + if (!ret && ((i == 0) || (i == 1))) { if (i == qdio_performance_stats) return count; qdio_performance_stats = i; diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile new file mode 100644 index 000000000000..4a5ec39f9ca6 --- /dev/null +++ b/drivers/s390/kvm/Makefile @@ -0,0 +1,9 @@ +# Makefile for kvm guest drivers on s390 +# +# Copyright IBM Corp. 2008 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (version 2 only) +# as published by the Free Software Foundation. + +obj-$(CONFIG_VIRTIO) += kvm_virtio.o diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c new file mode 100644 index 000000000000..47a7e6200b26 --- /dev/null +++ b/drivers/s390/kvm/kvm_virtio.c @@ -0,0 +1,339 @@ +/* + * kvm_virtio.c - virtio for kvm on s390 + * + * Copyright IBM Corp. 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> + */ + +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/err.h> +#include <linux/virtio.h> +#include <linux/virtio_config.h> +#include <linux/interrupt.h> +#include <linux/virtio_ring.h> +#include <linux/pfn.h> +#include <asm/io.h> +#include <asm/kvm_para.h> +#include <asm/kvm_virtio.h> +#include <asm/setup.h> +#include <asm/s390_ext.h> + +#define VIRTIO_SUBCODE_64 0x0D00 + +/* + * The pointer to our (page) of device descriptions. + */ +static void *kvm_devices; + +/* + * Unique numbering for kvm devices. + */ +static unsigned int dev_index; + +struct kvm_device { + struct virtio_device vdev; + struct kvm_device_desc *desc; +}; + +#define to_kvmdev(vd) container_of(vd, struct kvm_device, vdev) + +/* + * memory layout: + * - kvm_device_descriptor + * struct kvm_device_desc + * - configuration + * struct kvm_vqconfig + * - feature bits + * - config space + */ +static struct kvm_vqconfig *kvm_vq_config(const struct kvm_device_desc *desc) +{ + return (struct kvm_vqconfig *)(desc + 1); +} + +static u8 *kvm_vq_features(const struct kvm_device_desc *desc) +{ + return (u8 *)(kvm_vq_config(desc) + desc->num_vq); +} + +static u8 *kvm_vq_configspace(const struct kvm_device_desc *desc) +{ + return kvm_vq_features(desc) + desc->feature_len * 2; +} + +/* + * The total size of the config page used by this device (incl. desc) + */ +static unsigned desc_size(const struct kvm_device_desc *desc) +{ + return sizeof(*desc) + + desc->num_vq * sizeof(struct kvm_vqconfig) + + desc->feature_len * 2 + + desc->config_len; +} + +/* + * This tests (and acknowleges) a feature bit. + */ +static bool kvm_feature(struct virtio_device *vdev, unsigned fbit) +{ + struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; + u8 *features; + + if (fbit / 8 > desc->feature_len) + return false; + + features = kvm_vq_features(desc); + if (!(features[fbit / 8] & (1 << (fbit % 8)))) + return false; + + /* + * We set the matching bit in the other half of the bitmap to tell the + * Host we want to use this feature. + */ + features[desc->feature_len + fbit / 8] |= (1 << (fbit % 8)); + return true; +} + +/* + * Reading and writing elements in config space + */ +static void kvm_get(struct virtio_device *vdev, unsigned int offset, + void *buf, unsigned len) +{ + struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; + + BUG_ON(offset + len > desc->config_len); + memcpy(buf, kvm_vq_configspace(desc) + offset, len); +} + +static void kvm_set(struct virtio_device *vdev, unsigned int offset, + const void *buf, unsigned len) +{ + struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; + + BUG_ON(offset + len > desc->config_len); + memcpy(kvm_vq_configspace(desc) + offset, buf, len); +} + +/* + * The operations to get and set the status word just access + * the status field of the device descriptor. set_status will also + * make a hypercall to the host, to tell about status changes + */ +static u8 kvm_get_status(struct virtio_device *vdev) +{ + return to_kvmdev(vdev)->desc->status; +} + +static void kvm_set_status(struct virtio_device *vdev, u8 status) +{ + BUG_ON(!status); + to_kvmdev(vdev)->desc->status = status; + kvm_hypercall1(KVM_S390_VIRTIO_SET_STATUS, + (unsigned long) to_kvmdev(vdev)->desc); +} + +/* + * To reset the device, we use the KVM_VIRTIO_RESET hypercall, using the + * descriptor address. The Host will zero the status and all the + * features. + */ +static void kvm_reset(struct virtio_device *vdev) +{ + kvm_hypercall1(KVM_S390_VIRTIO_RESET, + (unsigned long) to_kvmdev(vdev)->desc); +} + +/* + * When the virtio_ring code wants to notify the Host, it calls us here and we + * make a hypercall. We hand the address of the virtqueue so the Host + * knows which virtqueue we're talking about. + */ +static void kvm_notify(struct virtqueue *vq) +{ + struct kvm_vqconfig *config = vq->priv; + + kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, config->address); +} + +/* + * This routine finds the first virtqueue described in the configuration of + * this device and sets it up. + */ +static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, + unsigned index, + void (*callback)(struct virtqueue *vq)) +{ + struct kvm_device *kdev = to_kvmdev(vdev); + struct kvm_vqconfig *config; + struct virtqueue *vq; + int err; + + if (index >= kdev->desc->num_vq) + return ERR_PTR(-ENOENT); + + config = kvm_vq_config(kdev->desc)+index; + + err = vmem_add_mapping(config->address, + vring_size(config->num, PAGE_SIZE)); + if (err) + goto out; + + vq = vring_new_virtqueue(config->num, vdev, (void *) config->address, + kvm_notify, callback); + if (!vq) { + err = -ENOMEM; + goto unmap; + } + + /* + * register a callback token + * The host will sent this via the external interrupt parameter + */ + config->token = (u64) vq; + + vq->priv = config; + return vq; +unmap: + vmem_remove_mapping(config->address, + vring_size(config->num, PAGE_SIZE)); +out: + return ERR_PTR(err); +} + +static void kvm_del_vq(struct virtqueue *vq) +{ + struct kvm_vqconfig *config = vq->priv; + + vring_del_virtqueue(vq); + vmem_remove_mapping(config->address, + vring_size(config->num, PAGE_SIZE)); +} + +/* + * The config ops structure as defined by virtio config + */ +static struct virtio_config_ops kvm_vq_configspace_ops = { + .feature = kvm_feature, + .get = kvm_get, + .set = kvm_set, + .get_status = kvm_get_status, + .set_status = kvm_set_status, + .reset = kvm_reset, + .find_vq = kvm_find_vq, + .del_vq = kvm_del_vq, +}; + +/* + * The root device for the kvm virtio devices. + * This makes them appear as /sys/devices/kvm_s390/0,1,2 not /sys/devices/0,1,2. + */ +static struct device kvm_root = { + .parent = NULL, + .bus_id = "kvm_s390", +}; + +/* + * adds a new device and register it with virtio + * appropriate drivers are loaded by the device model + */ +static void add_kvm_device(struct kvm_device_desc *d) +{ + struct kvm_device *kdev; + + kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); + if (!kdev) { + printk(KERN_EMERG "Cannot allocate kvm dev %u\n", + dev_index++); + return; + } + + kdev->vdev.dev.parent = &kvm_root; + kdev->vdev.index = dev_index++; + kdev->vdev.id.device = d->type; + kdev->vdev.config = &kvm_vq_configspace_ops; + kdev->desc = d; + + if (register_virtio_device(&kdev->vdev) != 0) { + printk(KERN_ERR "Failed to register kvm device %u\n", + kdev->vdev.index); + kfree(kdev); + } +} + +/* + * scan_devices() simply iterates through the device page. + * The type 0 is reserved to mean "end of devices". + */ +static void scan_devices(void) +{ + unsigned int i; + struct kvm_device_desc *d; + + for (i = 0; i < PAGE_SIZE; i += desc_size(d)) { + d = kvm_devices + i; + + if (d->type == 0) + break; + + add_kvm_device(d); + } +} + +/* + * we emulate the request_irq behaviour on top of s390 extints + */ +static void kvm_extint_handler(u16 code) +{ + void *data = (void *) *(long *) __LC_PFAULT_INTPARM; + u16 subcode = S390_lowcore.cpu_addr; + + if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) + return; + + vring_interrupt(0, data); +} + +/* + * Init function for virtio + * devices are in a single page above top of "normal" mem + */ +static int __init kvm_devices_init(void) +{ + int rc; + + if (!MACHINE_IS_KVM) + return -ENODEV; + + rc = device_register(&kvm_root); + if (rc) { + printk(KERN_ERR "Could not register kvm_s390 root device"); + return rc; + } + + rc = vmem_add_mapping(PFN_PHYS(max_pfn), PAGE_SIZE); + if (rc) { + device_unregister(&kvm_root); + return rc; + } + + kvm_devices = (void *) PFN_PHYS(max_pfn); + + ctl_set_bit(0, 9); + register_external_interrupt(0x2603, kvm_extint_handler); + + scan_devices(); + return 0; +} + +/* + * We do this after core stuff, but before the drivers. + */ +postcore_initcall(kvm_devices_init); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 7c3f02816e95..9af2330f07a2 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1927,7 +1927,8 @@ zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter, /* setup new FSF request */ retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA, - 0, NULL, &lock_flags, &fsf_req); + ZFCP_WAIT_FOR_SBAL, NULL, &lock_flags, + &fsf_req); if (retval) { ZFCP_LOG_INFO("error: Could not create exchange configuration " "data request for adapter %s.\n", @@ -2035,21 +2036,21 @@ zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) min(FC_SERIAL_NUMBER_SIZE, 17)); } - ZFCP_LOG_NORMAL("The adapter %s reported the following " - "characteristics:\n" - "WWNN 0x%016Lx, " - "WWPN 0x%016Lx, " - "S_ID 0x%06x,\n" - "adapter version 0x%x, " - "LIC version 0x%x, " - "FC link speed %d Gb/s\n", - zfcp_get_busid_by_adapter(adapter), - (wwn_t) fc_host_node_name(shost), - (wwn_t) fc_host_port_name(shost), - fc_host_port_id(shost), - adapter->hydra_version, - adapter->fsf_lic_version, - fc_host_speed(shost)); + if (fsf_req->erp_action) + ZFCP_LOG_NORMAL("The adapter %s reported the following " + "characteristics:\n" + "WWNN 0x%016Lx, WWPN 0x%016Lx, " + "S_ID 0x%06x,\n" + "adapter version 0x%x, " + "LIC version 0x%x, " + "FC link speed %d Gb/s\n", + zfcp_get_busid_by_adapter(adapter), + (wwn_t) fc_host_node_name(shost), + (wwn_t) fc_host_port_name(shost), + fc_host_port_id(shost), + adapter->hydra_version, + adapter->fsf_lic_version, + fc_host_speed(shost)); if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) { ZFCP_LOG_NORMAL("error: the adapter %s " "only supports newer control block " @@ -2114,8 +2115,10 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_shutdown(adapter, 0, 127, fsf_req); return -EIO; case FC_PORTTYPE_NPORT: - ZFCP_LOG_NORMAL("Switched fabric fibrechannel " - "network detected at adapter %s.\n", + if (fsf_req->erp_action) + ZFCP_LOG_NORMAL("Switched fabric fibrechannel " + "network detected at adapter " + "%s.\n", zfcp_get_busid_by_adapter(adapter)); break; default: diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 8cce5cc11d50..099970b27001 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -213,6 +213,7 @@ #define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010 #define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 #define FSF_FEATURE_UPDATE_ALERT 0x00000100 +#define FSF_FEATURE_MEASUREMENT_DATA 0x00000200 /* host connection features */ #define FSF_FEATURE_NPIV_MODE 0x00000001 @@ -340,6 +341,15 @@ struct fsf_qtcb_prefix { u8 res1[20]; } __attribute__ ((packed)); +struct fsf_statistics_info { + u64 input_req; + u64 output_req; + u64 control_req; + u64 input_mb; + u64 output_mb; + u64 seconds_act; +} __attribute__ ((packed)); + union fsf_status_qual { u8 byte[FSF_STATUS_QUALIFIER_SIZE]; u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)]; @@ -436,7 +446,8 @@ struct fsf_qtcb_bottom_config { u32 hardware_version; u8 serial_number[32]; struct fsf_nport_serv_param plogi_payload; - u8 res4[160]; + struct fsf_statistics_info stat_info; + u8 res4[112]; } __attribute__ ((packed)); struct fsf_qtcb_bottom_port { @@ -469,7 +480,10 @@ struct fsf_qtcb_bottom_port { u64 control_requests; u64 input_mb; /* where 1 MByte == 1.000.000 Bytes */ u64 output_mb; /* where 1 MByte == 1.000.000 Bytes */ - u8 res2[256]; + u8 cp_util; + u8 cb_util; + u8 a_util; + u8 res2[253]; } __attribute__ ((packed)); union fsf_qtcb_bottom { diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index f81850624eed..01687559dc06 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -40,6 +40,7 @@ static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, unsigned int, unsigned int); static struct device_attribute *zfcp_sysfs_sdev_attrs[]; +static struct device_attribute *zfcp_a_stats_attrs[]; struct zfcp_data zfcp_data = { .scsi_host_template = { @@ -61,6 +62,7 @@ struct zfcp_data zfcp_data = { .use_clustering = 1, .sdev_attrs = zfcp_sysfs_sdev_attrs, .max_sectors = ZFCP_MAX_SECTORS, + .shost_attrs = zfcp_a_stats_attrs, }, .driver_version = ZFCP_VERSION, }; @@ -809,4 +811,116 @@ static struct device_attribute *zfcp_sysfs_sdev_attrs[] = { NULL }; +static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *scsi_host = dev_to_shost(dev); + struct fsf_qtcb_bottom_port *qtcb_port; + int retval; + struct zfcp_adapter *adapter; + + adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; + if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)) + return -EOPNOTSUPP; + + qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL); + if (!qtcb_port) + return -ENOMEM; + + retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port); + if (!retval) + retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util, + qtcb_port->cb_util, qtcb_port->a_util); + kfree(qtcb_port); + return retval; +} + +static int zfcp_sysfs_adapter_ex_config(struct device *dev, + struct fsf_statistics_info *stat_inf) +{ + int retval; + struct fsf_qtcb_bottom_config *qtcb_config; + struct Scsi_Host *scsi_host = dev_to_shost(dev); + struct zfcp_adapter *adapter; + + adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; + if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)) + return -EOPNOTSUPP; + + qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config), + GFP_KERNEL); + if (!qtcb_config) + return -ENOMEM; + + retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config); + if (!retval) + *stat_inf = qtcb_config->stat_info; + + kfree(qtcb_config); + return retval; +} + +static ssize_t zfcp_sysfs_adapter_request_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fsf_statistics_info stat_info; + int retval; + + retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); + if (retval) + return retval; + + return sprintf(buf, "%llu %llu %llu\n", + (unsigned long long) stat_info.input_req, + (unsigned long long) stat_info.output_req, + (unsigned long long) stat_info.control_req); +} + +static ssize_t zfcp_sysfs_adapter_mb_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fsf_statistics_info stat_info; + int retval; + + retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); + if (retval) + return retval; + + return sprintf(buf, "%llu %llu\n", + (unsigned long long) stat_info.input_mb, + (unsigned long long) stat_info.output_mb); +} + +static ssize_t zfcp_sysfs_adapter_sec_active_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fsf_statistics_info stat_info; + int retval; + + retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); + if (retval) + return retval; + + return sprintf(buf, "%llu\n", + (unsigned long long) stat_info.seconds_act); +} + +static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL); +static DEVICE_ATTR(requests, S_IRUGO, zfcp_sysfs_adapter_request_show, NULL); +static DEVICE_ATTR(megabytes, S_IRUGO, zfcp_sysfs_adapter_mb_show, NULL); +static DEVICE_ATTR(seconds_active, S_IRUGO, + zfcp_sysfs_adapter_sec_active_show, NULL); + +static struct device_attribute *zfcp_a_stats_attrs[] = { + &dev_attr_utilization, + &dev_attr_requests, + &dev_attr_megabytes, + &dev_attr_seconds_active, + NULL +}; + #undef ZFCP_LOG_AREA |