aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/cio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/blacklist.c11
-rw-r--r--drivers/s390/cio/ccwgroup.c25
-rw-r--r--drivers/s390/cio/chp.c10
-rw-r--r--drivers/s390/cio/chsc_sch.c2
-rw-r--r--drivers/s390/cio/cio.c61
-rw-r--r--drivers/s390/cio/cio.h4
-rw-r--r--drivers/s390/cio/css.c37
-rw-r--r--drivers/s390/cio/device.c156
-rw-r--r--drivers/s390/cio/device.h3
-rw-r--r--drivers/s390/cio/device_fsm.c44
-rw-r--r--drivers/s390/cio/device_ops.c2
-rw-r--r--drivers/s390/cio/idset.c8
-rw-r--r--drivers/s390/cio/io_sch.h22
-rw-r--r--drivers/s390/cio/ioasm.h49
-rw-r--r--drivers/s390/cio/qdio.h11
-rw-r--r--drivers/s390/cio/qdio_debug.c2
-rw-r--r--drivers/s390/cio/qdio_debug.h6
-rw-r--r--drivers/s390/cio/qdio_main.c108
-rw-r--r--drivers/s390/cio/qdio_perf.c2
-rw-r--r--drivers/s390/cio/qdio_setup.c63
-rw-r--r--drivers/s390/cio/qdio_thinint.c6
21 files changed, 372 insertions, 260 deletions
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 0bfcbbe375c..2f547b840ef 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -24,6 +24,7 @@
#include "cio.h"
#include "cio_debug.h"
#include "css.h"
+#include "device.h"
/*
* "Blacklisting" of certain devices:
@@ -191,9 +192,9 @@ static int blacklist_parse_parameters(char *str, range_action action,
rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
msgtrigger);
if (rc)
- totalrc = 1;
+ totalrc = -EINVAL;
} else
- totalrc = 1;
+ totalrc = -EINVAL;
}
return totalrc;
@@ -240,8 +241,10 @@ static int blacklist_parse_proc_parameters(char *buf)
rc = blacklist_parse_parameters(buf, free, 0);
else if (strcmp("add", parm) == 0)
rc = blacklist_parse_parameters(buf, add, 0);
+ else if (strcmp("purge", parm) == 0)
+ return ccw_purge_blacklisted();
else
- return 1;
+ return -EINVAL;
css_schedule_reprobe();
@@ -353,7 +356,7 @@ cio_ignore_write(struct file *file, const char __user *user_buf,
}
ret = blacklist_parse_proc_parameters(buf);
if (ret)
- rc = -EINVAL;
+ rc = ret;
else
rc = user_len;
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 26a930e832b..3ac2c2019f5 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -112,8 +112,11 @@ ccwgroup_release (struct device *dev)
gdev = to_ccwgroupdev(dev);
for (i = 0; i < gdev->count; i++) {
- dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
- put_device(&gdev->cdev[i]->dev);
+ if (gdev->cdev[i]) {
+ if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
+ dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
+ put_device(&gdev->cdev[i]->dev);
+ }
}
kfree(gdev);
}
@@ -221,6 +224,13 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
atomic_set(&gdev->onoff, 0);
mutex_init(&gdev->reg_mutex);
mutex_lock(&gdev->reg_mutex);
+ gdev->creator_id = creator_id;
+ gdev->count = num_devices;
+ gdev->dev.bus = &ccwgroup_bus_type;
+ gdev->dev.parent = root;
+ gdev->dev.release = ccwgroup_release;
+ device_initialize(&gdev->dev);
+
curr_buf = buf;
for (i = 0; i < num_devices && curr_buf; i++) {
rc = __get_next_bus_id(&curr_buf, tmp_bus_id);
@@ -258,16 +268,10 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
rc = -EINVAL;
goto error;
}
- gdev->creator_id = creator_id;
- gdev->count = num_devices;
- gdev->dev.bus = &ccwgroup_bus_type;
- gdev->dev.parent = root;
- gdev->dev.release = ccwgroup_release;
- snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
- gdev->cdev[0]->dev.bus_id);
+ dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
- rc = device_register(&gdev->dev);
+ rc = device_add(&gdev->dev);
if (rc)
goto error;
get_device(&gdev->dev);
@@ -292,6 +296,7 @@ error:
if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
put_device(&gdev->cdev[i]->dev);
+ gdev->cdev[i] = NULL;
}
mutex_unlock(&gdev->reg_mutex);
put_device(&gdev->dev);
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index db00b059173..1246f61a533 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -393,8 +393,7 @@ int chp_new(struct chp_id chpid)
chp->state = 1;
chp->dev.parent = &channel_subsystems[chpid.cssid]->device;
chp->dev.release = chp_release;
- snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid,
- chpid.id);
+ dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id);
/* Obtain channel path description and fill it in. */
ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc);
@@ -423,7 +422,7 @@ int chp_new(struct chp_id chpid)
ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
if (ret) {
device_unregister(&chp->dev);
- goto out_free;
+ goto out;
}
mutex_lock(&channel_subsystems[chpid.cssid]->mutex);
if (channel_subsystems[chpid.cssid]->cm_enabled) {
@@ -432,14 +431,15 @@ int chp_new(struct chp_id chpid)
sysfs_remove_group(&chp->dev.kobj, &chp_attr_group);
device_unregister(&chp->dev);
mutex_unlock(&channel_subsystems[chpid.cssid]->mutex);
- goto out_free;
+ goto out;
}
}
channel_subsystems[chpid.cssid]->chps[chpid.id] = chp;
mutex_unlock(&channel_subsystems[chpid.cssid]->mutex);
- return ret;
+ goto out;
out_free:
kfree(chp);
+out:
return ret;
}
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index 91ca87aa9f9..f49f0e502b8 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -261,7 +261,7 @@ static int chsc_examine_irb(struct chsc_request *request)
{
int backed_up;
- if (!scsw_stctl(&request->irb.scsw) & SCSW_STCTL_STATUS_PEND)
+ if (!(scsw_stctl(&request->irb.scsw) & SCSW_STCTL_STATUS_PEND))
return -EIO;
backed_up = scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHAIN_CHECK;
request->irb.scsw.cmd.cstat &= ~SCHN_STAT_CHAIN_CHECK;
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 33bff8fec7d..3db2c386546 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -114,6 +114,7 @@ cio_tpi(void)
struct tpi_info *tpi_info;
struct subchannel *sch;
struct irb *irb;
+ int irq_context;
tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
if (tpi (NULL) != 1)
@@ -126,7 +127,9 @@ cio_tpi(void)
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
if (!sch)
return 1;
- local_bh_disable();
+ irq_context = in_interrupt();
+ if (!irq_context)
+ local_bh_disable();
irq_enter ();
spin_lock(sch->lock);
memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
@@ -134,7 +137,8 @@ cio_tpi(void)
sch->driver->irq(sch);
spin_unlock(sch->lock);
irq_exit ();
- _local_bh_enable();
+ if (!irq_context)
+ _local_bh_enable();
return 1;
}
@@ -153,7 +157,7 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
"subchannel 0.%x.%04x!\n", sch->schid.ssid,
sch->schid.sch_no);
- sprintf(dbf_text, "no%s", sch->dev.bus_id);
+ sprintf(dbf_text, "no%s", dev_name(&sch->dev));
CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
@@ -171,9 +175,10 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
union orb *orb;
CIO_TRACE_EVENT(4, "stIO");
- CIO_TRACE_EVENT(4, sch->dev.bus_id);
+ CIO_TRACE_EVENT(4, dev_name(&sch->dev));
orb = &to_io_private(sch)->orb;
+ memset(orb, 0, sizeof(union orb));
/* sch is always under 2G. */
orb->cmd.intparm = (u32)(addr_t)sch;
orb->cmd.fmt = 1;
@@ -208,8 +213,10 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
case 1: /* status pending */
case 2: /* busy */
return -EBUSY;
- default: /* device/path not operational */
+ case 3: /* device/path not operational */
return cio_start_handle_notoper(sch, lpm);
+ default:
+ return ccode;
}
}
@@ -229,7 +236,7 @@ cio_resume (struct subchannel *sch)
int ccode;
CIO_TRACE_EVENT (4, "resIO");
- CIO_TRACE_EVENT (4, sch->dev.bus_id);
+ CIO_TRACE_EVENT(4, dev_name(&sch->dev));
ccode = rsch (sch->schid);
@@ -266,7 +273,7 @@ cio_halt(struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "haltIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
/*
* Issue "Halt subchannel" and process condition code
@@ -301,7 +308,7 @@ cio_clear(struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "clearIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
/*
* Issue "Clear subchannel" and process condition code
@@ -337,7 +344,7 @@ cio_cancel (struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "cancelIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
ccode = xsch (sch->schid);
@@ -401,7 +408,7 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
int ret;
CIO_TRACE_EVENT (2, "ensch");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
if (sch_is_pseudo_sch(sch))
return -EINVAL;
@@ -451,7 +458,7 @@ int cio_disable_subchannel(struct subchannel *sch)
int ret;
CIO_TRACE_EVENT (2, "dissch");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
if (sch_is_pseudo_sch(sch))
return 0;
@@ -568,8 +575,10 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
}
mutex_init(&sch->reg_mutex);
/* Set a name for the subchannel */
- snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
- schid.sch_no);
+ if (cio_is_console(schid))
+ sch->dev.init_name = cio_get_console_sch_name(schid);
+ else
+ dev_set_name(&sch->dev, "0.%x.%04x", schid.ssid, schid.sch_no);
/*
* The first subchannel that is not-operational (ccode==3)
@@ -674,6 +683,7 @@ do_IRQ (struct pt_regs *regs)
#ifdef CONFIG_CCW_CONSOLE
static struct subchannel console_subchannel;
+static char console_sch_name[10] = "0.x.xxxx";
static struct io_subchannel_private console_priv;
static int console_subchannel_in_use;
@@ -824,6 +834,12 @@ cio_get_console_subchannel(void)
return &console_subchannel;
}
+const char *cio_get_console_sch_name(struct subchannel_id schid)
+{
+ snprintf(console_sch_name, 10, "0.%x.%04x", schid.ssid, schid.sch_no);
+ return (const char *)console_sch_name;
+}
+
#endif
static int
__disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
@@ -843,19 +859,6 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
return -EBUSY; /* uhm... */
}
-/* we can't use the normal udelay here, since it enables external interrupts */
-
-static void udelay_reset(unsigned long usecs)
-{
- uint64_t start_cc, end_cc;
-
- asm volatile ("STCK %0" : "=m" (start_cc));
- do {
- cpu_relax();
- asm volatile ("STCK %0" : "=m" (end_cc));
- } while (((end_cc - start_cc)/4096) < usecs);
-}
-
static int
__clear_io_subchannel_easy(struct subchannel_id schid)
{
@@ -871,7 +874,7 @@ __clear_io_subchannel_easy(struct subchannel_id schid)
if (schid_equal(&ti.schid, &schid))
return 0;
}
- udelay_reset(100);
+ udelay_simple(100);
}
return -EBUSY;
}
@@ -879,7 +882,7 @@ __clear_io_subchannel_easy(struct subchannel_id schid)
static void __clear_chsc_subchannel_easy(void)
{
/* It seems we can only wait for a bit here :/ */
- udelay_reset(100);
+ udelay_simple(100);
}
static int pgm_check_occured;
@@ -889,7 +892,7 @@ static void cio_reset_pgm_check_handler(void)
pgm_check_occured = 1;
}
-static int stsch_reset(struct subchannel_id schid, volatile struct schib *addr)
+static int stsch_reset(struct subchannel_id schid, struct schib *addr)
{
int rc;
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 3b236d20e83..0fb24784e92 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -117,11 +117,15 @@ extern int cio_is_console(struct subchannel_id);
extern struct subchannel *cio_get_console_subchannel(void);
extern spinlock_t * cio_get_console_lock(void);
extern void *cio_get_console_priv(void);
+extern const char *cio_get_console_sch_name(struct subchannel_id schid);
+extern const char *cio_get_console_cdev_name(struct subchannel *sch);
#else
#define cio_is_console(schid) 0
#define cio_get_console_subchannel() NULL
#define cio_get_console_lock() NULL
#define cio_get_console_priv() NULL
+#define cio_get_console_sch_name(schid) NULL
+#define cio_get_console_cdev_name(sch) NULL
#endif
#endif
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 46c021d880d..76bbb1e74c2 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -477,7 +477,6 @@ void css_schedule_eval_all(void)
void css_wait_for_slow_path(void)
{
- flush_workqueue(ccw_device_notify_work);
flush_workqueue(slow_path_wq);
}
@@ -634,6 +633,11 @@ channel_subsystem_release(struct device *dev)
css = to_css(dev);
mutex_destroy(&css->mutex);
+ if (css->pseudo_subchannel) {
+ /* Implies that it has been generated but never registered. */
+ css_subchannel_release(&css->pseudo_subchannel->dev);
+ css->pseudo_subchannel = NULL;
+ }
kfree(css);
}
@@ -694,7 +698,7 @@ static int __init setup_css(int nr)
return -ENOMEM;
css->pseudo_subchannel->dev.parent = &css->device;
css->pseudo_subchannel->dev.release = css_subchannel_release;
- sprintf(css->pseudo_subchannel->dev.bus_id, "defunct");
+ dev_set_name(&css->pseudo_subchannel->dev, "defunct");
ret = cio_create_sch_lock(css->pseudo_subchannel);
if (ret) {
kfree(css->pseudo_subchannel);
@@ -703,7 +707,7 @@ static int __init setup_css(int nr)
mutex_init(&css->mutex);
css->valid = 1;
css->cssid = nr;
- sprintf(css->device.bus_id, "css%x", nr);
+ dev_set_name(&css->device, "css%x", nr);
css->device.release = channel_subsystem_release;
tod_high = (u32) (get_clock() >> 32);
css_generate_pgid(css, tod_high);
@@ -786,11 +790,15 @@ init_channel_subsystem (void)
}
channel_subsystems[i] = css;
ret = setup_css(i);
- if (ret)
- goto out_free;
+ if (ret) {
+ kfree(channel_subsystems[i]);
+ goto out_unregister;
+ }
ret = device_register(&css->device);
- if (ret)
- goto out_free_all;
+ if (ret) {
+ put_device(&css->device);
+ goto out_unregister;
+ }
if (css_chsc_characteristics.secm) {
ret = device_create_file(&css->device,
&dev_attr_cm_enable);
@@ -803,7 +811,7 @@ init_channel_subsystem (void)
}
ret = register_reboot_notifier(&css_reboot_notifier);
if (ret)
- goto out_pseudo;
+ goto out_unregister;
css_init_done = 1;
/* Enable default isc for I/O subchannels. */
@@ -811,18 +819,12 @@ init_channel_subsystem (void)
for_each_subchannel(__init_channel_subsystem, NULL);
return 0;
-out_pseudo:
- device_unregister(&channel_subsystems[i]->pseudo_subchannel->dev);
out_file:
- device_remove_file(&channel_subsystems[i]->device,
- &dev_attr_cm_enable);
+ if (css_chsc_characteristics.secm)
+ device_remove_file(&channel_subsystems[i]->device,
+ &dev_attr_cm_enable);
out_device:
device_unregister(&channel_subsystems[i]->device);
-out_free_all:
- kfree(channel_subsystems[i]->pseudo_subchannel->lock);
- kfree(channel_subsystems[i]->pseudo_subchannel);
-out_free:
- kfree(channel_subsystems[i]);
out_unregister:
while (i > 0) {
struct channel_subsystem *css;
@@ -830,6 +832,7 @@ out_unregister:
i--;
css = channel_subsystems[i];
device_unregister(&css->pseudo_subchannel->dev);
+ css->pseudo_subchannel = NULL;
if (css_chsc_characteristics.secm)
device_remove_file(&css->device,
&dev_attr_cm_enable);
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index e818d0c54c0..4e78c82194b 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -31,6 +31,7 @@
#include "device.h"
#include "ioasm.h"
#include "io_sch.h"
+#include "blacklist.h"
static struct timer_list recovery_timer;
static DEFINE_SPINLOCK(recovery_lock);
@@ -150,7 +151,6 @@ static struct css_driver io_subchannel_driver = {
};
struct workqueue_struct *ccw_device_work;
-struct workqueue_struct *ccw_device_notify_work;
wait_queue_head_t ccw_device_init_wq;
atomic_t ccw_device_init_count;
@@ -168,11 +168,6 @@ init_ccw_bus_type (void)
ccw_device_work = create_singlethread_workqueue("cio");
if (!ccw_device_work)
return -ENOMEM; /* FIXME: better errno ? */
- ccw_device_notify_work = create_singlethread_workqueue("cio_notify");
- if (!ccw_device_notify_work) {
- ret = -ENOMEM; /* FIXME: better errno ? */
- goto out_err;
- }
slow_path_wq = create_singlethread_workqueue("kslowcrw");
if (!slow_path_wq) {
ret = -ENOMEM; /* FIXME: better errno ? */
@@ -192,8 +187,6 @@ init_ccw_bus_type (void)
out_err:
if (ccw_device_work)
destroy_workqueue(ccw_device_work);
- if (ccw_device_notify_work)
- destroy_workqueue(ccw_device_notify_work);
if (slow_path_wq)
destroy_workqueue(slow_path_wq);
return ret;
@@ -204,7 +197,6 @@ cleanup_ccw_bus_type (void)
{
css_driver_unregister(&io_subchannel_driver);
bus_unregister(&ccw_bus_type);
- destroy_workqueue(ccw_device_notify_work);
destroy_workqueue(ccw_device_work);
}
@@ -305,36 +297,33 @@ static void ccw_device_unregister(struct ccw_device *cdev)
device_del(&cdev->dev);
}
-static void ccw_device_remove_orphan_cb(struct device *dev)
+static void ccw_device_remove_orphan_cb(struct work_struct *work)
{
- struct ccw_device *cdev = to_ccwdev(dev);
+ struct ccw_device_private *priv;
+ struct ccw_device *cdev;
+ priv = container_of(work, struct ccw_device_private, kick_work);
+ cdev = priv->cdev;
ccw_device_unregister(cdev);
put_device(&cdev->dev);
+ /* Release cdev reference for workqueue processing. */
+ put_device(&cdev->dev);
}
-static void ccw_device_remove_sch_cb(struct device *dev)
-{
- struct subchannel *sch;
-
- sch = to_subchannel(dev);
- css_sch_device_unregister(sch);
- /* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
- put_device(&sch->dev);
-}
+static void ccw_device_call_sch_unregister(struct work_struct *work);
static void
ccw_device_remove_disconnected(struct ccw_device *cdev)
{
unsigned long flags;
- int rc;
/*
* Forced offline in disconnected state means
* 'throw away device'.
*/
+ /* Get cdev reference for workqueue processing. */
+ if (!get_device(&cdev->dev))
+ return;
if (ccw_device_is_orphan(cdev)) {
/*
* Deregister ccw device.
@@ -344,23 +333,13 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
spin_lock_irqsave(cdev->ccwlock, flags);
cdev->private->state = DEV_STATE_NOT_OPER;
spin_unlock_irqrestore(cdev->ccwlock, flags);
- rc = device_schedule_callback(&cdev->dev,
- ccw_device_remove_orphan_cb);
- if (rc)
- CIO_MSG_EVENT(0, "Couldn't unregister orphan "
- "0.%x.%04x\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
- return;
- }
- /* Deregister subchannel, which will kill the ccw device. */
- rc = device_schedule_callback(cdev->dev.parent,
- ccw_device_remove_sch_cb);
- if (rc)
- CIO_MSG_EVENT(0, "Couldn't unregister disconnected device "
- "0.%x.%04x\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_remove_orphan_cb);
+ } else
+ /* Deregister subchannel, which will kill the ccw device. */
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_call_sch_unregister);
+ queue_work(slow_path_wq, &cdev->private->kick_work);
}
/**
@@ -979,12 +958,17 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
+ /* Get subchannel reference for local processing. */
+ if (!get_device(cdev->dev.parent))
+ return;
sch = to_subchannel(cdev->dev.parent);
css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
+ /* Release cdev reference for workqueue processing.*/
put_device(&cdev->dev);
+ /* Release subchannel reference for local processing. */
put_device(&sch->dev);
}
@@ -1010,6 +994,8 @@ io_subchannel_recog_done(struct ccw_device *cdev)
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_call_sch_unregister);
queue_work(slow_path_wq, &cdev->private->kick_work);
+ /* Release subchannel reference for asynchronous recognition. */
+ put_device(&sch->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
break;
@@ -1049,8 +1035,11 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
init_timer(&priv->timer);
/* Set an initial name for the device. */
- snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
- sch->schid.ssid, sch->schib.pmcw.dev);
+ if (cio_is_console(sch->schid))
+ cdev->dev.init_name = cio_get_console_cdev_name(sch);
+ else
+ dev_set_name(&cdev->dev, "0.%x.%04x",
+ sch->schid.ssid, sch->schib.pmcw.dev);
/* Increase counter of devices currently in recognition. */
atomic_inc(&ccw_device_init_count);
@@ -1115,7 +1104,7 @@ static void io_subchannel_irq(struct subchannel *sch)
cdev = sch_get_cdev(sch);
CIO_TRACE_EVENT(3, "IRQ");
- CIO_TRACE_EVENT(3, sch->dev.bus_id);
+ CIO_TRACE_EVENT(3, dev_name(&sch->dev));
if (cdev)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
}
@@ -1485,6 +1474,45 @@ static void ccw_device_schedule_recovery(void)
spin_unlock_irqrestore(&recovery_lock, flags);
}
+static int purge_fn(struct device *dev, void *data)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct ccw_device_private *priv = cdev->private;
+ int unreg;
+
+ spin_lock_irq(cdev->ccwlock);
+ unreg = is_blacklisted(priv->dev_id.ssid, priv->dev_id.devno) &&
+ (priv->state == DEV_STATE_OFFLINE);
+ spin_unlock_irq(cdev->ccwlock);
+ if (!unreg)
+ goto out;
+ if (!get_device(&cdev->dev))
+ goto out;
+ CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid,
+ priv->dev_id.devno);
+ PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister);
+ queue_work(slow_path_wq, &cdev->private->kick_work);
+
+out:
+ /* Abort loop in case of pending signal. */
+ if (signal_pending(current))
+ return -EINTR;
+
+ return 0;
+}
+
+/**
+ * ccw_purge_blacklisted - purge unused, blacklisted devices
+ *
+ * Unregister all ccw devices that are offline and on the blacklist.
+ */
+int ccw_purge_blacklisted(void)
+{
+ CIO_MSG_EVENT(2, "ccw: purging blacklisted devices\n");
+ bus_for_each_dev(&ccw_bus_type, NULL, NULL, purge_fn);
+ return 0;
+}
+
static void device_set_disconnected(struct ccw_device *cdev)
{
if (!cdev)
@@ -1496,11 +1524,22 @@ static void device_set_disconnected(struct ccw_device *cdev)
ccw_device_schedule_recovery();
}
+void ccw_device_set_notoper(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+ CIO_TRACE_EVENT(2, "notoper");
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
+ ccw_device_set_timeout(cdev, 0);
+ cio_disable_subchannel(sch);
+ cdev->private->state = DEV_STATE_NOT_OPER;
+}
+
static int io_subchannel_sch_event(struct subchannel *sch, int slow)
{
int event, ret, disc;
unsigned long flags;
- enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
+ enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE, DISC } action;
struct ccw_device *cdev;
spin_lock_irqsave(sch->lock, flags);
@@ -1535,16 +1574,11 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
}
/* fall through */
case CIO_GONE:
- /* Prevent unwanted effects when opening lock. */
- cio_disable_subchannel(sch);
- device_set_disconnected(cdev);
/* Ask driver what to do with device. */
- action = UNREGISTER;
- spin_unlock_irqrestore(sch->lock, flags);
- ret = io_subchannel_notify(sch, event);
- spin_lock_irqsave(sch->lock, flags);
- if (ret)
- action = NONE;
+ if (io_subchannel_notify(sch, event))
+ action = DISC;
+ else
+ action = UNREGISTER;
break;
case CIO_REVALIDATE:
/* Device will be removed, so no notify necessary. */
@@ -1565,6 +1599,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
switch (action) {
case UNREGISTER:
case UNREGISTER_PROBE:
+ ccw_device_set_notoper(cdev);
/* Unregister device (will use subchannel lock). */
spin_unlock_irqrestore(sch->lock, flags);
css_sch_device_unregister(sch);
@@ -1577,6 +1612,9 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
case REPROBE:
ccw_device_trigger_reprobe(cdev);
break;
+ case DISC:
+ device_set_disconnected(cdev);
+ break;
default:
break;
}
@@ -1590,6 +1628,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
#ifdef CONFIG_CCW_CONSOLE
static struct ccw_device console_cdev;
+static char console_cdev_name[10] = "0.x.xxxx";
static struct ccw_device_private console_private;
static int console_cdev_in_use;
@@ -1660,6 +1699,14 @@ ccw_device_probe_console(void)
console_cdev.online = 1;
return &console_cdev;
}
+
+
+const char *cio_get_console_cdev_name(struct subchannel *sch)
+{
+ snprintf(console_cdev_name, 10, "0.%x.%04x",
+ sch->schid.ssid, sch->schib.pmcw.dev);
+ return (const char *)console_cdev_name;
+}
#endif
/*
@@ -1672,7 +1719,7 @@ __ccwdev_check_busid(struct device *dev, void *id)
bus_id = id;
- return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0);
+ return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0);
}
@@ -1828,5 +1875,4 @@ EXPORT_SYMBOL(ccw_driver_unregister);
EXPORT_SYMBOL(get_ccwdev_by_busid);
EXPORT_SYMBOL(ccw_bus_type);
EXPORT_SYMBOL(ccw_device_work);
-EXPORT_SYMBOL(ccw_device_notify_work);
EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 9800a8335a3..104ed669db4 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -72,7 +72,6 @@ dev_fsm_final_state(struct ccw_device *cdev)
}
extern struct workqueue_struct *ccw_device_work;
-extern struct workqueue_struct *ccw_device_notify_work;
extern wait_queue_head_t ccw_device_init_wq;
extern atomic_t ccw_device_init_count;
@@ -87,6 +86,7 @@ int ccw_device_is_orphan(struct ccw_device *);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
+int ccw_purge_blacklisted(void);
/* Function prototypes for device status and basic sense stuff. */
void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
@@ -120,6 +120,7 @@ int ccw_device_stlck(struct ccw_device *);
void ccw_device_trigger_reprobe(struct ccw_device *);
void ccw_device_kill_io(struct ccw_device *);
int ccw_device_notify(struct ccw_device *, int);
+void ccw_device_set_notoper(struct ccw_device *cdev);
/* qdio needs this. */
void ccw_device_set_timeout(struct ccw_device *, int);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 8b5fe57fb2f..10bc03940fb 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -52,8 +52,10 @@ static void ccw_timeout_log(struct ccw_device *cdev)
printk(KERN_WARNING "cio: orb:\n");
print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
orb, sizeof(*orb), 0);
- printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
- printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
+ printk(KERN_WARNING "cio: ccw device bus id: %s\n",
+ dev_name(&cdev->dev));
+ printk(KERN_WARNING "cio: subchannel bus id: %s\n",
+ dev_name(&sch->dev));
printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
"vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
@@ -337,26 +339,34 @@ int ccw_device_notify(struct ccw_device *cdev, int event)
return 0;
if (!cdev->online)
return 0;
+ CIO_MSG_EVENT(2, "notify called for 0.%x.%04x, event=%d\n",
+ cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
+ event);
return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
}
-static void
-ccw_device_oper_notify(struct work_struct *work)
+static void cmf_reenable_delayed(struct work_struct *work)
{
struct ccw_device_private *priv;
struct ccw_device *cdev;
- int ret;
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
- ret = ccw_device_notify(cdev, CIO_OPER);
- if (ret) {
+ cmf_reenable(cdev);
+}
+
+static void ccw_device_oper_notify(struct ccw_device *cdev)
+{
+ if (ccw_device_notify(cdev, CIO_OPER)) {
/* Reenable channel measurements, if needed. */
- cmf_reenable(cdev);
- wake_up(&cdev->private->wait_q);
- } else
- /* Driver doesn't want device back. */
- ccw_device_do_unreg_rereg(work);
+ PREPARE_WORK(&cdev->private->kick_work, cmf_reenable_delayed);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ return;
+ }
+ /* Driver doesn't want device back. */
+ ccw_device_set_notoper(cdev);
+ PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unreg_rereg);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
}
/*
@@ -386,8 +396,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
if (cdev->private->flags.donotify) {
cdev->private->flags.donotify = 0;
- PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify);
- queue_work(ccw_device_notify_work, &cdev->private->kick_work);
+ ccw_device_oper_notify(cdev);
}
wake_up(&cdev->private->wait_q);
@@ -651,6 +660,13 @@ ccw_device_offline(struct ccw_device *cdev)
{
struct subchannel *sch;
+ /* Allow ccw_device_offline while disconnected. */
+ if (cdev->private->state == DEV_STATE_DISCONNECTED ||
+ cdev->private->state == DEV_STATE_NOT_OPER) {
+ cdev->private->flags.donotify = 0;
+ ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+ return 0;
+ }
if (ccw_device_is_orphan(cdev)) {
ccw_device_done(cdev, DEV_STATE_OFFLINE);
return 0;
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index ee1a28310fb..eabcc42d63d 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -498,7 +498,7 @@ ccw_device_stlck(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
CIO_TRACE_EVENT(2, "stl lock");
- CIO_TRACE_EVENT(2, cdev->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&cdev->dev));
buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
if (!buf)
diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c
index ef7bc0a125e..cf8f24a4b5e 100644
--- a/drivers/s390/cio/idset.c
+++ b/drivers/s390/cio/idset.c
@@ -5,7 +5,7 @@
* Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
-#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <linux/bitops.h>
#include "idset.h"
#include "css.h"
@@ -25,18 +25,18 @@ static struct idset *idset_new(int num_ssid, int num_id)
{
struct idset *set;
- set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id),
- GFP_KERNEL);
+ set = vmalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id));
if (set) {
set->num_ssid = num_ssid;
set->num_id = num_id;
+ memset(set->bitmap, 0, bitmap_size(num_ssid, num_id));
}
return set;
}
void idset_free(struct idset *set)
{
- kfree(set);
+ vfree(set);
}
void idset_clear(struct idset *set)
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 3f8f1cf69c7..c4f3e7c9a85 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -123,7 +123,7 @@ struct ccw_device_private {
void *cmb_wait; /* deferred cmb enable/disable */
};
-static inline int ssch(struct subchannel_id schid, volatile union orb *addr)
+static inline int ssch(struct subchannel_id schid, union orb *addr)
{
register struct subchannel_id reg1 asm("1") = schid;
int ccode = -EIO;
@@ -134,7 +134,9 @@ static inline int ssch(struct subchannel_id schid, volatile union orb *addr)
" srl %0,28\n"
"1:\n"
EX_TABLE(0b, 1b)
- : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "+d" (ccode)
+ : "d" (reg1), "a" (addr), "m" (*addr)
+ : "cc", "memory");
return ccode;
}
@@ -147,7 +149,9 @@ static inline int rsch(struct subchannel_id schid)
" rsch\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc", "memory");
return ccode;
}
@@ -160,7 +164,9 @@ static inline int csch(struct subchannel_id schid)
" csch\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
return ccode;
}
@@ -173,7 +179,9 @@ static inline int hsch(struct subchannel_id schid)
" hsch\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
return ccode;
}
@@ -186,7 +194,9 @@ static inline int xsch(struct subchannel_id schid)
" .insn rre,0xb2760000,%1,0\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
return ccode;
}
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 9fa2ac13ac8..75926279263 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -23,38 +23,39 @@ struct tpi_info {
* Some S390 specific IO instructions as inline
*/
-static inline int stsch(struct subchannel_id schid,
- volatile struct schib *addr)
+static inline int stsch(struct subchannel_id schid, struct schib *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
asm volatile(
- " stsch 0(%2)\n"
+ " stsch 0(%3)\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "=d" (ccode), "=m" (*addr)
+ : "d" (reg1), "a" (addr)
+ : "cc");
return ccode;
}
-static inline int stsch_err(struct subchannel_id schid,
- volatile struct schib *addr)
+static inline int stsch_err(struct subchannel_id schid, struct schib *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode = -EIO;
asm volatile(
- " stsch 0(%2)\n"
+ " stsch 0(%3)\n"
"0: ipm %0\n"
" srl %0,28\n"
"1:\n"
EX_TABLE(0b,1b)
- : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "+d" (ccode), "=m" (*addr)
+ : "d" (reg1), "a" (addr)
+ : "cc");
return ccode;
}
-static inline int msch(struct subchannel_id schid,
- volatile struct schib *addr)
+static inline int msch(struct subchannel_id schid, struct schib *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
@@ -63,12 +64,13 @@ static inline int msch(struct subchannel_id schid,
" msch 0(%2)\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1), "a" (addr), "m" (*addr)
+ : "cc");
return ccode;
}
-static inline int msch_err(struct subchannel_id schid,
- volatile struct schib *addr)
+static inline int msch_err(struct subchannel_id schid, struct schib *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode = -EIO;
@@ -79,33 +81,38 @@ static inline int msch_err(struct subchannel_id schid,
" srl %0,28\n"
"1:\n"
EX_TABLE(0b,1b)
- : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "+d" (ccode)
+ : "d" (reg1), "a" (addr), "m" (*addr)
+ : "cc");
return ccode;
}
-static inline int tsch(struct subchannel_id schid,
- volatile struct irb *addr)
+static inline int tsch(struct subchannel_id schid, struct irb *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
asm volatile(
- " tsch 0(%2)\n"
+ " tsch 0(%3)\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "=d" (ccode), "=m" (*addr)
+ : "d" (reg1), "a" (addr)
+ : "cc");
return ccode;
}
-static inline int tpi( volatile struct tpi_info *addr)
+static inline int tpi(struct tpi_info *addr)
{
int ccode;
asm volatile(
- " tpi 0(%1)\n"
+ " tpi 0(%2)\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "a" (addr), "m" (*addr) : "cc");
+ : "=d" (ccode), "=m" (*addr)
+ : "a" (addr)
+ : "cc");
return ccode;
}
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index c1a70985abf..e3ea1d5f281 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -16,6 +16,14 @@
#define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */
#define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */
+/*
+ * if an asynchronous HiperSockets queue runs full, the 10 seconds timer wait
+ * till next initiative to give transmitted skbs back to the stack is too long.
+ * Therefore polling is started in case of multicast queue is filled more
+ * than 50 percent.
+ */
+#define QDIO_IQDIO_POLL_LVL 65 /* HS multicast queue */
+
enum qdio_irq_states {
QDIO_IRQ_STATE_INACTIVE,
QDIO_IRQ_STATE_ESTABLISHED,
@@ -195,6 +203,9 @@ struct qdio_output_q {
/* PCIs are enabled for the queue */
int pci_out_enabled;
+ /* IQDIO: output multiple buffers (enhanced SIGA) */
+ int use_enh_siga;
+
/* timer to check for more outbound work */
struct timer_list timer;
};
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index 337aa3087a7..b5390821434 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -155,7 +155,7 @@ static int qstat_seq_open(struct inode *inode, struct file *filp)
static void get_queue_name(struct qdio_q *q, struct ccw_device *cdev, char *name)
{
memset(name, 0, sizeof(name));
- sprintf(name, "%s", cdev->dev.bus_id);
+ sprintf(name, "%s", dev_name(&cdev->dev));
if (q->is_input_q)
sprintf(name + strlen(name), "_input");
else
diff --git a/drivers/s390/cio/qdio_debug.h b/drivers/s390/cio/qdio_debug.h
index 8484b83698e..5a4d85b829a 100644
--- a/drivers/s390/cio/qdio_debug.h
+++ b/drivers/s390/cio/qdio_debug.h
@@ -61,18 +61,18 @@
/* s390dbf views */
#define QDIO_DBF_SETUP_LEN 8
-#define QDIO_DBF_SETUP_PAGES 4
+#define QDIO_DBF_SETUP_PAGES 8
#define QDIO_DBF_SETUP_NR_AREAS 1
#define QDIO_DBF_TRACE_LEN 8
#define QDIO_DBF_TRACE_NR_AREAS 2
#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_TRACE_PAGES 16
+#define QDIO_DBF_TRACE_PAGES 32
#define QDIO_DBF_SETUP_LEVEL 6
#define QDIO_DBF_TRACE_LEVEL 4
#else /* !CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_TRACE_PAGES 4
+#define QDIO_DBF_TRACE_PAGES 8
#define QDIO_DBF_SETUP_LEVEL 2
#define QDIO_DBF_TRACE_LEVEL 2
#endif /* CONFIG_QDIO_DEBUG */
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index d10c73cc168..a50682d2a0f 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -316,6 +316,9 @@ static inline int qdio_do_siga_output(struct qdio_q *q, unsigned int *busy_bit)
unsigned int fc = 0;
unsigned long schid;
+ if (q->u.out.use_enh_siga) {
+ fc = 3;
+ }
if (!is_qebsm(q))
schid = *((u32 *)&q->irq_ptr->schid);
else {
@@ -330,6 +333,7 @@ static int qdio_siga_output(struct qdio_q *q)
int cc;
u32 busy_bit;
u64 start_time = 0;
+ char dbf_text[15];
QDIO_DBF_TEXT5(0, trace, "sigaout");
QDIO_DBF_HEX5(0, trace, &q, sizeof(void *));
@@ -338,6 +342,9 @@ static int qdio_siga_output(struct qdio_q *q)
again:
cc = qdio_do_siga_output(q, &busy_bit);
if (queue_type(q) == QDIO_IQDIO_QFMT && cc == 2 && busy_bit) {
+ sprintf(dbf_text, "bb%4x%2x", q->irq_ptr->schid.sch_no, q->nr);
+ QDIO_DBF_TEXT3(0, trace, dbf_text);
+
if (!start_time)
start_time = get_usecs();
else if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
@@ -748,16 +755,18 @@ static void qdio_kick_outbound_q(struct qdio_q *q)
rc = qdio_siga_output(q);
switch (rc) {
case 0:
- /* went smooth this time, reset timestamp */
- q->u.out.timestamp = 0;
-
/* TODO: improve error handling for CC=0 case */
#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT3(0, trace, "cc2reslv");
- sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no, q->nr,
- atomic_read(&q->u.out.busy_siga_counter));
- QDIO_DBF_TEXT3(0, trace, dbf_text);
+ if (q->u.out.timestamp) {
+ QDIO_DBF_TEXT3(0, trace, "cc2reslv");
+ sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no,
+ q->nr,
+ atomic_read(&q->u.out.busy_siga_counter));
+ QDIO_DBF_TEXT3(0, trace, dbf_text);
+ }
#endif /* CONFIG_QDIO_DEBUG */
+ /* went smooth this time, reset timestamp */
+ q->u.out.timestamp = 0;
break;
/* cc=2 and busy bit */
case (2 | QDIO_ERROR_SIGA_BUSY):
@@ -845,6 +854,12 @@ static void __qdio_outbound_processing(struct qdio_q *q)
if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q))
return;
+ if ((queue_type(q) == QDIO_IQDIO_QFMT) &&
+ (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL) {
+ tasklet_schedule(&q->tasklet);
+ return;
+ }
+
if (q->u.out.pci_out_enabled)
return;
@@ -950,7 +965,7 @@ static void qdio_handle_activate_check(struct ccw_device *cdev,
char dbf_text[15];
QDIO_DBF_TEXT2(1, trace, "ick2");
- sprintf(dbf_text, "%s", cdev->dev.bus_id);
+ sprintf(dbf_text, "%s", dev_name(&cdev->dev));
QDIO_DBF_TEXT2(1, trace, dbf_text);
QDIO_DBF_HEX2(0, trace, &intparm, sizeof(int));
QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int));
@@ -1066,14 +1081,12 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) {
case -EIO:
- sprintf(dbf_text, "ierr%4x",
- cdev->private->schid.sch_no);
+ sprintf(dbf_text, "ierr%4x", irq_ptr->schid.sch_no);
QDIO_DBF_TEXT2(1, setup, dbf_text);
qdio_int_error(cdev);
return;
case -ETIMEDOUT:
- sprintf(dbf_text, "qtoh%4x",
- cdev->private->schid.sch_no);
+ sprintf(dbf_text, "qtoh%4x", irq_ptr->schid.sch_no);
QDIO_DBF_TEXT2(1, setup, dbf_text);
qdio_int_error(cdev);
return;
@@ -1124,8 +1137,10 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev)
{
struct qdio_irq *irq_ptr;
+ char dbf_text[15];
- QDIO_DBF_TEXT0(0, setup, "getssqd");
+ sprintf(dbf_text, "qssq%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
@@ -1149,14 +1164,13 @@ int qdio_cleanup(struct ccw_device *cdev, int how)
char dbf_text[15];
int rc;
+ sprintf(dbf_text, "qcln%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
- sprintf(dbf_text, "qcln%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT1(0, trace, dbf_text);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
rc = qdio_shutdown(cdev, how);
if (rc == 0)
rc = qdio_free(cdev);
@@ -1191,6 +1205,9 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
unsigned long flags;
char dbf_text[15];
+ sprintf(dbf_text, "qshu%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
@@ -1205,10 +1222,6 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
return 0;
}
- sprintf(dbf_text, "qsqs%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT1(0, trace, dbf_text);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
tiqdio_remove_input_queues(irq_ptr);
qdio_shutdown_queues(cdev);
qdio_shutdown_debug_entries(irq_ptr, cdev);
@@ -1247,7 +1260,6 @@ no_cleanup:
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
mutex_unlock(&irq_ptr->setup_mutex);
- module_put(THIS_MODULE);
if (rc)
return rc;
return 0;
@@ -1263,16 +1275,14 @@ int qdio_free(struct ccw_device *cdev)
struct qdio_irq *irq_ptr;
char dbf_text[15];
+ sprintf(dbf_text, "qfre%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
mutex_lock(&irq_ptr->setup_mutex);
-
- sprintf(dbf_text, "qfqs%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT1(0, trace, dbf_text);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
cdev->private->qdio_data = NULL;
mutex_unlock(&irq_ptr->setup_mutex);
@@ -1295,7 +1305,6 @@ int qdio_initialize(struct qdio_initialize *init_data)
sprintf(dbf_text, "qini%4x", init_data->cdev->private->schid.sch_no);
QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_TEXT0(0, trace, dbf_text);
rc = qdio_allocate(init_data);
if (rc)
@@ -1319,7 +1328,6 @@ int qdio_allocate(struct qdio_initialize *init_data)
sprintf(dbf_text, "qalc%4x", init_data->cdev->private->schid.sch_no);
QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_TEXT0(0, trace, dbf_text);
if ((init_data->no_input_qs && !init_data->input_handler) ||
(init_data->no_output_qs && !init_data->output_handler))
@@ -1355,7 +1363,7 @@ int qdio_allocate(struct qdio_initialize *init_data)
goto out_rel;
/* qdr is used in ccw1.cda which is u32 */
- irq_ptr->qdr = kzalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA);
+ irq_ptr->qdr = (struct qdr *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!irq_ptr->qdr)
goto out_rel;
WARN_ON((unsigned long)irq_ptr->qdr & 0xfff);
@@ -1389,6 +1397,9 @@ int qdio_establish(struct qdio_initialize *init_data)
unsigned long saveflags;
int rc;
+ sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
@@ -1396,13 +1407,6 @@ int qdio_establish(struct qdio_initialize *init_data)
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
- if (!try_module_get(THIS_MODULE))
- return -EINVAL;
-
- sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_TEXT0(0, trace, dbf_text);
-
mutex_lock(&irq_ptr->setup_mutex);
qdio_setup_irq(init_data);
@@ -1448,6 +1452,8 @@ int qdio_establish(struct qdio_initialize *init_data)
}
qdio_setup_ssqd_info(irq_ptr);
+ sprintf(dbf_text, "qDmmwc%2x", irq_ptr->ssqd_desc.mmwc);
+ QDIO_DBF_TEXT2(0, setup, dbf_text);
sprintf(dbf_text, "qib ac%2x", irq_ptr->qib.ac);
QDIO_DBF_TEXT2(0, setup, dbf_text);
@@ -1472,6 +1478,9 @@ int qdio_activate(struct ccw_device *cdev)
unsigned long saveflags;
char dbf_text[20];
+ sprintf(dbf_text, "qact%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
@@ -1485,10 +1494,6 @@ int qdio_activate(struct ccw_device *cdev)
goto out;
}
- sprintf(dbf_text, "qact%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
- QDIO_DBF_TEXT2(0, trace, dbf_text);
-
irq_ptr->ccw.cmd_code = irq_ptr->aqueue.cmd;
irq_ptr->ccw.flags = CCW_FLAG_SLI;
irq_ptr->ccw.count = irq_ptr->aqueue.count;
@@ -1621,12 +1626,21 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
if (multicast_outbound(q))
qdio_kick_outbound_q(q);
else
- /*
- * One siga-w per buffer required for unicast
- * HiperSockets.
- */
- while (count--)
+ if ((q->irq_ptr->ssqd_desc.mmwc > 1) &&
+ (count > 1) &&
+ (count <= q->irq_ptr->ssqd_desc.mmwc)) {
+ /* exploit enhanced SIGA */
+ q->u.out.use_enh_siga = 1;
qdio_kick_outbound_q(q);
+ } else {
+ /*
+ * One siga-w per buffer required for unicast
+ * HiperSockets.
+ */
+ q->u.out.use_enh_siga = 0;
+ while (count--)
+ qdio_kick_outbound_q(q);
+ }
goto out;
}
@@ -1663,7 +1677,7 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
#ifdef CONFIG_QDIO_DEBUG
char dbf_text[20];
- sprintf(dbf_text, "doQD%04x", cdev->private->schid.sch_no);
+ sprintf(dbf_text, "doQD%4x", cdev->private->schid.sch_no);
QDIO_DBF_TEXT3(0, trace, dbf_text);
#endif /* CONFIG_QDIO_DEBUG */
diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c
index ea01b85b1cc..ec5c4a41423 100644
--- a/drivers/s390/cio/qdio_perf.c
+++ b/drivers/s390/cio/qdio_perf.c
@@ -142,7 +142,7 @@ int __init qdio_setup_perf_stats(void)
return 0;
}
-void __exit qdio_remove_perf_stats(void)
+void qdio_remove_perf_stats(void)
{
#ifdef CONFIG_PROC_FS
remove_proc_entry("qdio_perf", NULL);
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index f0923a8aced..a0b6b46e746 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -165,7 +165,7 @@ static void setup_queues(struct qdio_irq *irq_ptr,
void **output_sbal_array = qdio_init->output_sbal_addr_array;
int i;
- sprintf(dbf_text, "qfqs%4x", qdio_init->cdev->private->schid.sch_no);
+ sprintf(dbf_text, "qset%4x", qdio_init->cdev->private->schid.sch_no);
QDIO_DBF_TEXT0(0, setup, dbf_text);
for_each_input_queue(irq_ptr, q, i) {
@@ -285,7 +285,7 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
rc = __get_ssqd_info(irq_ptr);
if (rc) {
QDIO_DBF_TEXT2(0, setup, "ssqdasig");
- sprintf(dbf_text, "schno%x", irq_ptr->schid.sch_no);
+ sprintf(dbf_text, "schn%4x", irq_ptr->schid.sch_no);
QDIO_DBF_TEXT2(0, setup, dbf_text);
sprintf(dbf_text, "rc:%d", rc);
QDIO_DBF_TEXT2(0, setup, dbf_text);
@@ -325,7 +325,7 @@ void qdio_release_memory(struct qdio_irq *irq_ptr)
kmem_cache_free(qdio_q_cache, q);
}
}
- kfree(irq_ptr->qdr);
+ free_page((unsigned long) irq_ptr->qdr);
free_page(irq_ptr->chsc_page);
free_page((unsigned long) irq_ptr);
}
@@ -447,51 +447,36 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
{
char s[80];
- sprintf(s, "%s ", cdev->dev.bus_id);
-
+ sprintf(s, "qdio: %s ", dev_name(&cdev->dev));
switch (irq_ptr->qib.qfmt) {
case QDIO_QETH_QFMT:
- sprintf(s + strlen(s), "OSADE ");
+ sprintf(s + strlen(s), "OSA ");
break;
case QDIO_ZFCP_QFMT:
sprintf(s + strlen(s), "ZFCP ");
break;
case QDIO_IQDIO_QFMT:
- sprintf(s + strlen(s), "HiperSockets ");
+ sprintf(s + strlen(s), "HS ");
break;
}
- sprintf(s + strlen(s), "using: ");
-
- if (!is_thinint_irq(irq_ptr))
- sprintf(s + strlen(s), "no");
- sprintf(s + strlen(s), "AdapterInterrupts ");
- if (!(irq_ptr->sch_token != 0))
- sprintf(s + strlen(s), "no");
- sprintf(s + strlen(s), "QEBSM ");
- if (!(irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED))
- sprintf(s + strlen(s), "no");
- sprintf(s + strlen(s), "OutboundPCI ");
- if (!css_general_characteristics.aif_tdd)
- sprintf(s + strlen(s), "no");
- sprintf(s + strlen(s), "TDD\n");
- printk(KERN_INFO "qdio: %s", s);
-
- memset(s, 0, sizeof(s));
- sprintf(s, "%s SIGA required: ", cdev->dev.bus_id);
- if (irq_ptr->siga_flag.input)
- sprintf(s + strlen(s), "Read ");
- if (irq_ptr->siga_flag.output)
- sprintf(s + strlen(s), "Write ");
- if (irq_ptr->siga_flag.sync)
- sprintf(s + strlen(s), "Sync ");
- if (!irq_ptr->siga_flag.no_sync_ti)
- sprintf(s + strlen(s), "SyncAI ");
- if (!irq_ptr->siga_flag.no_sync_out_ti)
- sprintf(s + strlen(s), "SyncOutAI ");
- if (!irq_ptr->siga_flag.no_sync_out_pci)
- sprintf(s + strlen(s), "SyncOutPCI");
+ sprintf(s + strlen(s), "on SC %x using ", irq_ptr->schid.sch_no);
+ sprintf(s + strlen(s), "AI:%d ", is_thinint_irq(irq_ptr));
+ sprintf(s + strlen(s), "QEBSM:%d ", (irq_ptr->sch_token) ? 1 : 0);
+ sprintf(s + strlen(s), "PCI:%d ",
+ (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0);
+ sprintf(s + strlen(s), "TDD:%d ", css_general_characteristics.aif_tdd);
+ sprintf(s + strlen(s), "SIGA:");
+ sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.input) ? "R" : " ");
+ sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.output) ? "W" : " ");
+ sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.sync) ? "S" : " ");
+ sprintf(s + strlen(s), "%s",
+ (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ");
+ sprintf(s + strlen(s), "%s",
+ (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ");
+ sprintf(s + strlen(s), "%s",
+ (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " ");
sprintf(s + strlen(s), "\n");
- printk(KERN_INFO "qdio: %s", s);
+ printk(KERN_INFO "%s", s);
}
int __init qdio_setup_init(void)
@@ -515,7 +500,7 @@ int __init qdio_setup_init(void)
return 0;
}
-void __exit qdio_setup_exit(void)
+void qdio_setup_exit(void)
{
kmem_cache_destroy(qdio_q_cache);
}
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 9291a771d81..ea7f6140026 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -113,7 +113,11 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
struct qdio_q *q;
int i;
- for_each_input_queue(irq_ptr, q, i) {
+ for (i = 0; i < irq_ptr->nr_input_qs; i++) {
+ q = irq_ptr->input_qs[i];
+ /* if establish triggered an error */
+ if (!q || !q->entry.prev || !q->entry.next)
+ continue;
list_del_rcu(&q->entry);
synchronize_rcu();
}