summaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/Makefile2
-rw-r--r--drivers/s390/block/dasd_proc.c16
-rw-r--r--drivers/s390/block/dcssblk.c8
-rw-r--r--drivers/s390/char/con3215.c5
-rw-r--r--drivers/s390/char/sclp_config.c17
-rw-r--r--drivers/s390/char/sclp_tty.c4
-rw-r--r--drivers/s390/char/sclp_vt220.c6
-rw-r--r--drivers/s390/char/tape_proc.c9
-rw-r--r--drivers/s390/char/tty3270.c3
-rw-r--r--drivers/s390/cio/blacklist.c7
-rw-r--r--drivers/s390/cio/ccwgroup.c103
-rw-r--r--drivers/s390/cio/cio.c9
-rw-r--r--drivers/s390/cio/cio.h3
-rw-r--r--drivers/s390/cio/cmf.c11
-rw-r--r--drivers/s390/cio/css.c10
-rw-r--r--drivers/s390/cio/device.c17
-rw-r--r--drivers/s390/cio/device_fsm.c10
-rw-r--r--drivers/s390/cio/device_ops.c2
-rw-r--r--drivers/s390/cio/qdio.c12
-rw-r--r--drivers/s390/kvm/Makefile9
-rw-r--r--drivers/s390/kvm/kvm_virtio.c339
-rw-r--r--drivers/s390/net/cu3088.c20
-rw-r--r--drivers/s390/net/lcs.c3
-rw-r--r--drivers/s390/net/netiucv.c3
-rw-r--r--drivers/s390/net/qeth_core.h50
-rw-r--r--drivers/s390/net/qeth_core_main.c200
-rw-r--r--drivers/s390/net/qeth_l2_main.c30
-rw-r--r--drivers/s390/net/qeth_l3.h3
-rw-r--r--drivers/s390/net/qeth_l3_main.c30
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c2
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c41
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h18
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c114
33 files changed, 835 insertions, 281 deletions
diff --git a/drivers/s390/Makefile b/drivers/s390/Makefile
index 5a888704a8d..4f4e7cf105d 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 556063e8f7a..03c0e40a92f 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 04787eab101..bb52d2fbac1 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 0e1f35c9ed9..3e5653c92f4 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 b8f35bc52b7..9e784d5f7f5 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 e3b3d390b4a..40b11521cd2 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 ed507594e62..35707c04e61 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 c9b96d51b28..e7c888c14e7 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 70b1980a08b..c1f2adefad4 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 e8597ec9224..40ef948fcb3 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 fe1ad172215..26a930e832b 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -152,44 +152,89 @@ __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
return 0;
}
+static int __get_next_bus_id(const char **buf, char *bus_id)
+{
+ int rc, len;
+ char *start, *end;
+
+ start = (char *)*buf;
+ end = strchr(start, ',');
+ if (!end) {
+ /* Last entry. Strip trailing newline, if applicable. */
+ end = strchr(start, '\n');
+ if (end)
+ *end = '\0';
+ len = strlen(start) + 1;
+ } else {
+ len = end - start + 1;
+ end++;
+ }
+ if (len < BUS_ID_SIZE) {
+ strlcpy(bus_id, start, len);
+ rc = 0;
+ } else
+ rc = -EINVAL;
+ *buf = end;
+ return rc;
+}
+
+static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE])
+{
+ int cssid, ssid, devno;
+
+ /* Must be of form %x.%x.%04x */
+ if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3)
+ return 0;
+ return 1;
+}
+
/**
- * ccwgroup_create() - create and register a ccw group device
+ * ccwgroup_create_from_string() - create and register a ccw group device
* @root: parent device for the new device
* @creator_id: identifier of creating driver
* @cdrv: ccw driver of slave devices
- * @argc: number of slave devices
- * @argv: bus ids of slave devices
+ * @num_devices: number of slave devices
+ * @buf: buffer containing comma separated bus ids of slave devices
*
* Create and register a new ccw group device as a child of @root. Slave
- * devices are obtained from the list of bus ids given in @argv[] and must all
+ * devices are obtained from the list of bus ids given in @buf and must all
* belong to @cdrv.
* Returns:
* %0 on success and an error code on failure.
* Context:
* non-atomic
*/
-int ccwgroup_create(struct device *root, unsigned int creator_id,
- struct ccw_driver *cdrv, int argc, char *argv[])
+int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
+ struct ccw_driver *cdrv, int num_devices,
+ const char *buf)
{
struct ccwgroup_device *gdev;
- int i;
- int rc;
+ int rc, i;
+ char tmp_bus_id[BUS_ID_SIZE];
+ const char *curr_buf;
- if (argc > 256) /* disallow dumb users */
- return -EINVAL;
-
- gdev = kzalloc(sizeof(*gdev) + argc*sizeof(gdev->cdev[0]), GFP_KERNEL);
+ gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
+ GFP_KERNEL);
if (!gdev)
return -ENOMEM;
atomic_set(&gdev->onoff, 0);
mutex_init(&gdev->reg_mutex);
mutex_lock(&gdev->reg_mutex);
- for (i = 0; i < argc; i++) {
- gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
-
- /* all devices have to be of the same type in
- * order to be grouped */
+ curr_buf = buf;
+ for (i = 0; i < num_devices && curr_buf; i++) {
+ rc = __get_next_bus_id(&curr_buf, tmp_bus_id);
+ if (rc != 0)
+ goto error;
+ if (!__is_valid_bus_id(tmp_bus_id)) {
+ rc = -EINVAL;
+ goto error;
+ }
+ gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id);
+ /*
+ * All devices have to be of the same type in
+ * order to be grouped.
+ */
if (!gdev->cdev[i]
|| gdev->cdev[i]->id.driver_info !=
gdev->cdev[0]->id.driver_info) {
@@ -203,9 +248,18 @@ int ccwgroup_create(struct device *root, unsigned int creator_id,
}
dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
}
-
+ /* Check for sufficient number of bus ids. */
+ if (i < num_devices && !curr_buf) {
+ rc = -EINVAL;
+ goto error;
+ }
+ /* Check for trailing stuff. */
+ if (i == num_devices && strlen(curr_buf) > 0) {
+ rc = -EINVAL;
+ goto error;
+ }
gdev->creator_id = creator_id;
- gdev->count = argc;
+ gdev->count = num_devices;
gdev->dev.bus = &ccwgroup_bus_type;
gdev->dev.parent = root;
gdev->dev.release = ccwgroup_release;
@@ -233,7 +287,7 @@ int ccwgroup_create(struct device *root, unsigned int creator_id,
device_remove_file(&gdev->dev, &dev_attr_ungroup);
device_unregister(&gdev->dev);
error:
- for (i = 0; i < argc; i++)
+ for (i = 0; i < num_devices; i++)
if (gdev->cdev[i]) {
if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
@@ -243,6 +297,7 @@ error:
put_device(&gdev->dev);
return rc;
}
+EXPORT_SYMBOL(ccwgroup_create_from_string);
static int __init
init_ccwgroup (void)
@@ -318,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);
@@ -329,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);
@@ -337,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;
}
@@ -518,6 +576,5 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccwgroup_driver_register);
EXPORT_SYMBOL(ccwgroup_driver_unregister);
-EXPORT_SYMBOL(ccwgroup_create);
EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
EXPORT_SYMBOL(ccwgroup_remove_ccwdev);
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 23ffcc4768a..08a57816130 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 08f2235c5a6..3c75412904d 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 f4c132ab39e..2808b6833b9 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 c1afab5f72d..595e327d2f7 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 e0c7adb8958..abfd601d237 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 4b92c84fb43..99403b0a97a 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 a1718a0aa53..f308ad55a6d 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 10aa1e78080..445cf364e46 100644
--- a/drivers/s390/cio/qdio.c
+++ b/drivers/s390/cio/qdio.c
@@ -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 00000000000..4a5ec39f9ca
--- /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 00000000000..47a7e6200b2
--- /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/net/cu3088.c b/drivers/s390/net/cu3088.c
index 76728ae4b84..8e7697305a4 100644
--- a/drivers/s390/net/cu3088.c
+++ b/drivers/s390/net/cu3088.c
@@ -62,30 +62,14 @@ static struct device *cu3088_root_dev;
static ssize_t
group_write(struct device_driver *drv, const char *buf, size_t count)
{
- const char *start, *end;
- char bus_ids[2][BUS_ID_SIZE], *argv[2];
- int i;
int ret;
struct ccwgroup_driver *cdrv;
cdrv = to_ccwgroupdrv(drv);
if (!cdrv)
return -EINVAL;
- start = buf;
- for (i=0; i<2; i++) {
- static const char delim[] = {',', '\n'};
- int len;
-
- if (!(end = strchr(start, delim[i])))
- return -EINVAL;
- len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start + 1);
- strlcpy (bus_ids[i], start, len);
- argv[i] = bus_ids[i];
- start = end + 1;
- }
-
- ret = ccwgroup_create(cu3088_root_dev, cdrv->driver_id,
- &cu3088_driver, 2, argv);
+ ret = ccwgroup_create_from_string(cu3088_root_dev, cdrv->driver_id,
+ &cu3088_driver, 2, buf);
return (ret == 0) ? count : ret;
}
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index f51ed997258..dd22f4b3703 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1793,7 +1793,8 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
skb->protocol = card->lan_type_trans(skb, card->dev);
card->stats.rx_bytes += skb_len;
card->stats.rx_packets++;
- *((__u32 *)skb->cb) = ++card->pkt_seq;
+ if (skb->protocol == htons(ETH_P_802_2))
+ *((__u32 *)skb->cb) = ++card->pkt_seq;
netif_rx(skb);
}
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 8f876f6ab36..e4ba6a0372a 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -1313,8 +1313,6 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
* and throw away packet.
*/
if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
- if (!in_atomic())
- fsm_event(privptr->fsm, DEV_EVENT_START, dev);
dev_kfree_skb(skb);
privptr->stats.tx_dropped++;
privptr->stats.tx_errors++;
@@ -2147,6 +2145,7 @@ static int __init netiucv_init(void)
if (rc)
goto out_dbf;
IUCV_DBF_TEXT(trace, 3, __func__);
+ netiucv_driver.groups = netiucv_drv_attr_groups;
rc = driver_register(&netiucv_driver);
if (rc) {
PRINT_ERR("NETIUCV: failed to register driver.\n");
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 66f4f12503c..699ac11debd 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -72,22 +72,7 @@ struct qeth_dbf_info {
debug_sprintf_event(qeth_dbf[QETH_DBF_MSG].id, level, text)
#define QETH_DBF_TEXT_(name, level, text...) \
- do { \
- if (qeth_dbf_passes(qeth_dbf[QETH_DBF_##name].id, level)) { \
- char *dbf_txt_buf = \
- get_cpu_var(QETH_DBF_TXT_BUF); \
- sprintf(dbf_txt_buf, text); \
- debug_text_event(qeth_dbf[QETH_DBF_##name].id, \
- level, dbf_txt_buf); \
- put_cpu_var(QETH_DBF_TXT_BUF); \
- } \
- } while (0)
-
-/* Allow to sort out low debug levels early to avoid wasted sprints */
-static inline int qeth_dbf_passes(debug_info_t *dbf_grp, int level)
-{
- return (level <= dbf_grp->level);
-}
+ qeth_dbf_longtext(QETH_DBF_##name, level, text)
/**
* some more debug stuff
@@ -773,27 +758,6 @@ static inline int qeth_get_micros(void)
return (int) (get_clock() >> 12);
}
-static inline void *qeth_push_skb(struct qeth_card *card, struct sk_buff *skb,
- int size)
-{
- void *hdr;
-
- hdr = (void *) skb_push(skb, size);
- /*
- * sanity check, the Linux memory allocation scheme should
- * never present us cases like this one (the qdio header size plus
- * the first 40 bytes of the paket cross a 4k boundary)
- */
- if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) !=
- (((unsigned long) hdr + size +
- QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
- PRINT_ERR("Misaligned packet on interface %s. Discarded.",
- QETH_CARD_IFNAME(card));
- return NULL;
- }
- return hdr;
-}
-
static inline int qeth_get_ip_version(struct sk_buff *skb)
{
switch (skb->protocol) {
@@ -806,6 +770,12 @@ static inline int qeth_get_ip_version(struct sk_buff *skb)
}
}
+static inline void qeth_put_buffer_pool_entry(struct qeth_card *card,
+ struct qeth_buffer_pool_entry *entry)
+{
+ list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list);
+}
+
struct qeth_eddp_context;
extern struct ccwgroup_driver qeth_l2_ccwgroup_driver;
extern struct ccwgroup_driver qeth_l3_ccwgroup_driver;
@@ -843,8 +813,6 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *,
int qeth_query_setadapterparms(struct qeth_card *);
int qeth_check_qdio_errors(struct qdio_buffer *, unsigned int,
unsigned int, const char *);
-void qeth_put_buffer_pool_entry(struct qeth_card *,
- struct qeth_buffer_pool_entry *);
void qeth_queue_input_buffer(struct qeth_card *, int);
struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
struct qdio_buffer *, struct qdio_buffer_element **, int *,
@@ -880,8 +848,6 @@ int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
void *reply_param);
int qeth_get_cast_type(struct qeth_card *, struct sk_buff *);
int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);
-struct sk_buff *qeth_prepare_skb(struct qeth_card *, struct sk_buff *,
- struct qeth_hdr **);
int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int);
int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,
struct sk_buff *, struct qeth_hdr *, int,
@@ -894,6 +860,8 @@ void qeth_core_get_ethtool_stats(struct net_device *,
struct ethtool_stats *, u64 *);
void qeth_core_get_strings(struct net_device *, u32, u8 *);
void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
+void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...);
+int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *);
/* exports for OSN */
int qeth_osn_assist(struct net_device *, void *, int);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 055f5c3e7b5..436bf1f6d4a 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -26,9 +26,6 @@
#include "qeth_core.h"
#include "qeth_core_offl.h"
-static DEFINE_PER_CPU(char[256], qeth_core_dbf_txt_buf);
-#define QETH_DBF_TXT_BUF qeth_core_dbf_txt_buf
-
struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = {
/* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */
/* N P A M L V H */
@@ -2255,14 +2252,6 @@ void qeth_print_status_message(struct qeth_card *card)
}
EXPORT_SYMBOL_GPL(qeth_print_status_message);
-void qeth_put_buffer_pool_entry(struct qeth_card *card,
- struct qeth_buffer_pool_entry *entry)
-{
- QETH_DBF_TEXT(TRACE, 6, "ptbfplen");
- list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list);
-}
-EXPORT_SYMBOL_GPL(qeth_put_buffer_pool_entry);
-
static void qeth_initialize_working_pool_list(struct qeth_card *card)
{
struct qeth_buffer_pool_entry *entry;
@@ -2603,7 +2592,6 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)
int rc;
int newcount = 0;
- QETH_DBF_TEXT(TRACE, 6, "queinbuf");
count = (index < queue->next_buf_to_init)?
card->qdio.in_buf_pool.buf_count -
(queue->next_buf_to_init - index) :
@@ -2792,8 +2780,6 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
int i;
unsigned int qdio_flags;
- QETH_DBF_TEXT(TRACE, 6, "flushbuf");
-
for (i = index; i < index + count; ++i) {
buf = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
buf->buffer->element[buf->next_element_to_fill - 1].flags |=
@@ -3037,49 +3023,6 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
}
EXPORT_SYMBOL_GPL(qeth_get_priority_queue);
-static void __qeth_free_new_skb(struct sk_buff *orig_skb,
- struct sk_buff *new_skb)
-{
- if (orig_skb != new_skb)
- dev_kfree_skb_any(new_skb);
-}
-
-static inline struct sk_buff *qeth_realloc_headroom(struct qeth_card *card,
- struct sk_buff *skb, int size)
-{
- struct sk_buff *new_skb = skb;
-
- if (skb_headroom(skb) >= size)
- return skb;
- new_skb = skb_realloc_headroom(skb, size);
- if (!new_skb)
- PRINT_ERR("Could not realloc headroom for qeth_hdr "
- "on interface %s", QETH_CARD_IFNAME(card));
- return new_skb;
-}
-
-struct sk_buff *qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr **hdr)
-{
- struct sk_buff *new_skb;
-
- QETH_DBF_TEXT(TRACE, 6, "prepskb");
-
- new_skb = qeth_realloc_headroom(card, skb,
- sizeof(struct qeth_hdr));
- if (!new_skb)
- return NULL;
-
- *hdr = ((struct qeth_hdr *)qeth_push_skb(card, new_skb,
- sizeof(struct qeth_hdr)));
- if (*hdr == NULL) {
- __qeth_free_new_skb(skb, new_skb);
- return NULL;
- }
- return new_skb;
-}
-EXPORT_SYMBOL_GPL(qeth_prepare_skb);
-
int qeth_get_elements_no(struct qeth_card *card, void *hdr,
struct sk_buff *skb, int elems)
{
@@ -3100,8 +3043,8 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,
}
EXPORT_SYMBOL_GPL(qeth_get_elements_no);
-static void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
- int is_tso, int *next_element_to_fill)
+static inline void __qeth_fill_buffer(struct sk_buff *skb,
+ struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill)
{
int length = skb->len;
int length_here;
@@ -3143,15 +3086,13 @@ static void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
*next_element_to_fill = element;
}
-static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
+static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
struct qeth_qdio_out_buffer *buf, struct sk_buff *skb)
{
struct qdio_buffer *buffer;
struct qeth_hdr_tso *hdr;
int flush_cnt = 0, hdr_len, large_send = 0;
- QETH_DBF_TEXT(TRACE, 6, "qdfillbf");
-
buffer = buf->buffer;
atomic_inc(&skb->users);
skb_queue_tail(&buf->skb_list, skb);
@@ -3210,8 +3151,6 @@ int qeth_do_send_packet_fast(struct qeth_card *card,
int flush_cnt = 0;
int index;
- QETH_DBF_TEXT(TRACE, 6, "dosndpfa");
-
/* spin until we get the queue ... */
while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
@@ -3263,8 +3202,6 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
int tmp;
int rc = 0;
- QETH_DBF_TEXT(TRACE, 6, "dosndpkt");
-
/* spin until we get the queue ... */
while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
@@ -3827,27 +3764,8 @@ static struct ccw_driver qeth_ccw_driver = {
static int qeth_core_driver_group(const char *buf, struct device *root_dev,
unsigned long driver_id)
{
- const char *start, *end;
- char bus_ids[3][BUS_ID_SIZE], *argv[3];
- int i;
-
- start = buf;
- for (i = 0; i < 3; i++) {
- static const char delim[] = { ',', ',', '\n' };
- int len;
-
- end = strchr(start, delim[i]);
- if (!end)
- return -EINVAL;
- len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start);
- strncpy(bus_ids[i], start, len);
- bus_ids[i][len] = '\0';
- start = end + 1;
- argv[i] = bus_ids[i];
- }
-
- return (ccwgroup_create(root_dev, driver_id,
- &qeth_ccw_driver, 3, argv));
+ return ccwgroup_create_from_string(root_dev, driver_id,
+ &qeth_ccw_driver, 3, buf);
}
int qeth_core_hardsetup_card(struct qeth_card *card)
@@ -3885,8 +3803,9 @@ retry:
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
return rc;
}
-
- mpno = QETH_MAX_PORTNO;
+ mpno = qdio_get_ssqd_pct(CARD_DDEV(card));
+ if (mpno)
+ mpno = min(mpno - 1, QETH_MAX_PORTNO);
if (card->info.portno > mpno) {
PRINT_ERR("Device %s does not offer port number %d \n.",
CARD_BUS_ID(card), card->info.portno);
@@ -3980,7 +3899,6 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
int use_rx_sg = 0;
int frag = 0;
- QETH_DBF_TEXT(TRACE, 6, "nextskb");
/* qeth_hdr must not cross element boundaries */
if (element->length < offset + sizeof(struct qeth_hdr)) {
if (qeth_is_last_sbale(element))
@@ -4086,6 +4004,18 @@ static void qeth_unregister_dbf_views(void)
}
}
+void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...)
+{
+ char dbf_txt_buf[32];
+
+ if (level > (qeth_dbf[dbf_nix].id)->level)
+ return;
+ snprintf(dbf_txt_buf, sizeof(dbf_txt_buf), text);
+ debug_text_event(qeth_dbf[dbf_nix].id, level, dbf_txt_buf);
+
+}
+EXPORT_SYMBOL_GPL(qeth_dbf_longtext);
+
static int qeth_register_dbf_views(void)
{
int ret;
@@ -4433,6 +4363,96 @@ void qeth_core_get_drvinfo(struct net_device *dev,
}
EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo);
+int qeth_core_ethtool_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct qeth_card *card = netdev_priv(netdev);
+ enum qeth_link_types link_type;
+
+ if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
+ link_type = QETH_LINK_TYPE_10GBIT_ETH;
+ else
+ link_type = card->info.link_type;
+
+ ecmd->transceiver = XCVR_INTERNAL;
+ ecmd->supported = SUPPORTED_Autoneg;
+ ecmd->advertising = ADVERTISED_Autoneg;
+ ecmd->duplex = DUPLEX_FULL;
+ ecmd->autoneg = AUTONEG_ENABLE;
+
+ switch (link_type) {
+ case QETH_LINK_TYPE_FAST_ETH:
+ case QETH_LINK_TYPE_LANE_ETH100:
+ ecmd->supported |= SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_TP;
+ ecmd->advertising |= ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_TP;
+ ecmd->speed = SPEED_100;
+ ecmd->port = PORT_TP;
+ break;
+
+ case QETH_LINK_TYPE_GBIT_ETH:
+ case QETH_LINK_TYPE_LANE_ETH1000:
+ ecmd->supported |= SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_FIBRE;
+ ecmd->advertising |= ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_FIBRE;
+ ecmd->speed = SPEED_1000;
+ ecmd->port = PORT_FIBRE;
+ break;
+
+ case QETH_LINK_TYPE_10GBIT_ETH:
+ ecmd->supported |= SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_10000baseT_Full |
+ SUPPORTED_FIBRE;
+ ecmd->advertising |= ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_10000baseT_Full |
+ ADVERTISED_FIBRE;
+ ecmd->speed = SPEED_10000;
+ ecmd->port = PORT_FIBRE;
+ break;
+
+ default:
+ ecmd->supported |= SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_TP;
+ ecmd->advertising |= ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_TP;
+ ecmd->speed = SPEED_10;
+ ecmd->port = PORT_TP;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings);
+
static int __init qeth_core_init(void)
{
int rc;
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 3921d1631a7..86ec50ddae1 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -22,9 +22,6 @@
#include "qeth_core.h"
#include "qeth_core_offl.h"
-#define QETH_DBF_TXT_BUF qeth_l2_dbf_txt_buf
-static DEFINE_PER_CPU(char[256], qeth_l2_dbf_txt_buf);
-
static int qeth_l2_set_offline(struct ccwgroup_device *);
static int qeth_l2_stop(struct net_device *);
static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
@@ -635,8 +632,6 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
struct qeth_eddp_context *ctx = NULL;
- QETH_DBF_TEXT(TRACE, 6, "l2xmit");
-
if ((card->state != CARD_STATE_UP) || !card->lan_online) {
card->stats.tx_carrier_errors++;
goto tx_drop;
@@ -658,9 +653,12 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (card->info.type == QETH_CARD_TYPE_OSN)
hdr = (struct qeth_hdr *)skb->data;
else {
- new_skb = qeth_prepare_skb(card, skb, &hdr);
+ /* create a clone with writeable headroom */
+ new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr));
if (!new_skb)
goto tx_drop;
+ hdr = (struct qeth_hdr *)skb_push(new_skb,
+ sizeof(struct qeth_hdr));
qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
}
@@ -747,7 +745,6 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
int index;
int i;
- QETH_DBF_TEXT(TRACE, 6, "qdinput");
card = (struct qeth_card *) card_ptr;
net_dev = card->dev;
if (card->options.performance_stats) {
@@ -852,6 +849,22 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
return;
}
+static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data)
+{
+ struct qeth_card *card = netdev_priv(dev);
+
+ if (data) {
+ if (card->options.large_send == QETH_LARGE_SEND_NO) {
+ card->options.large_send = QETH_LARGE_SEND_EDDP;
+ dev->features |= NETIF_F_TSO;
+ }
+ } else {
+ dev->features &= ~NETIF_F_TSO;
+ card->options.large_send = QETH_LARGE_SEND_NO;
+ }
+ return 0;
+}
+
static struct ethtool_ops qeth_l2_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_tx_csum = ethtool_op_get_tx_csum,
@@ -859,11 +872,12 @@ static struct ethtool_ops qeth_l2_ethtool_ops = {
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
- .set_tso = ethtool_op_set_tso,
+ .set_tso = qeth_l2_ethtool_set_tso,
.get_strings = qeth_core_get_strings,
.get_ethtool_stats = qeth_core_get_ethtool_stats,
.get_stats_count = qeth_core_get_stats_count,
.get_drvinfo = qeth_core_get_drvinfo,
+ .get_settings = qeth_core_ethtool_get_settings,
};
static struct ethtool_ops qeth_l2_osn_ops = {
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
index 1be353593a5..9f143c83bba 100644
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -13,9 +13,6 @@
#include "qeth_core.h"
-#define QETH_DBF_TXT_BUF qeth_l3_dbf_txt_buf
-DECLARE_PER_CPU(char[256], qeth_l3_dbf_txt_buf);
-
struct qeth_ipaddr {
struct list_head entry;
enum qeth_ip_types type;
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index e1bfe56087d..94a8ead64ed 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -28,8 +28,6 @@
#include "qeth_l3.h"
#include "qeth_core_offl.h"
-DEFINE_PER_CPU(char[256], qeth_l3_dbf_txt_buf);
-
static int qeth_l3_set_offline(struct ccwgroup_device *);
static int qeth_l3_recover(void *);
static int qeth_l3_stop(struct net_device *);
@@ -2093,6 +2091,11 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
(card->state == CARD_STATE_UP)) {
if (recovery_mode)
qeth_l3_stop(card->dev);
+ else {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
if (!card->use_hard_stop) {
rc = qeth_send_stoplan(card);
if (rc)
@@ -2559,8 +2562,6 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
struct sk_buff *skb, int ipv, int cast_type)
{
- QETH_DBF_TEXT(TRACE, 6, "fillhdr");
-
memset(hdr, 0, sizeof(struct qeth_hdr));
hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
hdr->hdr.l3.ext_flags = 0;
@@ -2570,9 +2571,10 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
* v6 uses passthrough, v4 sets the tag in the QDIO header.
*/
if (card->vlangrp && vlan_tx_tag_present(skb)) {
- hdr->hdr.l3.ext_flags = (ipv == 4) ?
- QETH_HDR_EXT_VLAN_FRAME :
- QETH_HDR_EXT_INCLUDE_VLAN_TAG;
+ if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD))
+ hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME;
+ else
+ hdr->hdr.l3.ext_flags = QETH_HDR_EXT_INCLUDE_VLAN_TAG;
hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb);
}
@@ -2638,8 +2640,6 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
struct qeth_eddp_context *ctx = NULL;
- QETH_DBF_TEXT(TRACE, 6, "l3xmit");
-
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
(skb->protocol != htons(ETH_P_IPV6)) &&
(skb->protocol != htons(ETH_P_IP)))
@@ -2890,6 +2890,7 @@ static struct ethtool_ops qeth_l3_ethtool_ops = {
.get_ethtool_stats = qeth_core_get_ethtool_stats,
.get_stats_count = qeth_core_get_stats_count,
.get_drvinfo = qeth_core_get_drvinfo,
+ .get_settings = qeth_core_ethtool_get_settings,
};
/*
@@ -2982,7 +2983,6 @@ static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev,
int index;
int i;
- QETH_DBF_TEXT(TRACE, 6, "qdinput");
card = (struct qeth_card *) card_ptr;
net_dev = card->dev;
if (card->options.performance_stats) {
@@ -3140,9 +3140,15 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
netif_carrier_on(card->dev);
qeth_set_allowed_threads(card, 0xffffffff, 0);
- if ((recover_flag == CARD_STATE_RECOVER) && recovery_mode) {
+ if (recover_flag == CARD_STATE_RECOVER) {
+ if (recovery_mode)
qeth_l3_open(card->dev);
- qeth_l3_set_multicast_list(card->dev);
+ else {
+ rtnl_lock();
+ dev_open(card->dev);
+ rtnl_unlock();
+ }
+ qeth_l3_set_multicast_list(card->dev);
}
/* let user_space know that device is online */
kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 37b85c67b11..c8bad675dbd 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -1055,7 +1055,7 @@ static void zfcp_scsi_dbf_event(const char *tag, const char *tag2, int level,
rec->scsi_result = scsi_cmnd->result;
rec->scsi_cmnd = (unsigned long)scsi_cmnd;
rec->scsi_serial = scsi_cmnd->serial_number;
- memcpy(rec->scsi_opcode, &scsi_cmnd->cmnd,
+ memcpy(rec->scsi_opcode, scsi_cmnd->cmnd,
min((int)scsi_cmnd->cmd_len,
ZFCP_DBF_SCSI_OPCODE));
rec->scsi_retries = scsi_cmnd->retries;
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 7c3f02816e9..b2ea4ea051f 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:
@@ -4011,7 +4014,7 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req)
ZFCP_LOG_TRACE("scpnt->result =0x%x, command was:\n",
scpnt->result);
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
- (void *) &scpnt->cmnd, scpnt->cmd_len);
+ scpnt->cmnd, scpnt->cmd_len);
ZFCP_LOG_TRACE("%i bytes sense data provided by FCP\n",
fcp_rsp_iu->fcp_sns_len);
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index 8cce5cc11d5..099970b2700 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 f81850624ee..01687559dc0 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