summaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/blacklist.c14
-rw-r--r--drivers/s390/cio/ccwgroup.c26
-rw-r--r--drivers/s390/cio/chsc.c8
-rw-r--r--drivers/s390/cio/chsc_sch.c2
-rw-r--r--drivers/s390/cio/cio.c245
-rw-r--r--drivers/s390/cio/cio.h18
-rw-r--r--drivers/s390/cio/cmf.c63
-rw-r--r--drivers/s390/cio/css.c12
-rw-r--r--drivers/s390/cio/device.c237
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_fsm.c46
-rw-r--r--drivers/s390/cio/device_pgid.c2
-rw-r--r--drivers/s390/cio/device_status.c4
-rw-r--r--drivers/s390/cio/qdio.h33
-rw-r--r--drivers/s390/cio/qdio_debug.c104
-rw-r--r--drivers/s390/cio/qdio_debug.h112
-rw-r--r--drivers/s390/cio/qdio_main.c648
-rw-r--r--drivers/s390/cio/qdio_perf.c8
-rw-r--r--drivers/s390/cio/qdio_perf.h5
-rw-r--r--drivers/s390/cio/qdio_setup.c145
-rw-r--r--drivers/s390/cio/qdio_thinint.c29
21 files changed, 872 insertions, 890 deletions
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 2f547b840ef0..fe00be3675cd 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -9,6 +9,9 @@
* Arnd Bergmann (arndb@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
@@ -50,9 +53,10 @@ static int blacklist_range(range_action action, unsigned int from_ssid,
{
if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
if (msgtrigger)
- printk(KERN_WARNING "cio: Invalid cio_ignore range "
- "0.%x.%04x-0.%x.%04x\n", from_ssid, from,
- to_ssid, to);
+ pr_warning("0.%x.%04x to 0.%x.%04x is not a valid "
+ "range for cio_ignore\n", from_ssid, from,
+ to_ssid, to);
+
return 1;
}
@@ -140,8 +144,8 @@ static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
rc = 0;
out:
if (rc && msgtrigger)
- printk(KERN_WARNING "cio: Invalid cio_ignore device '%s'\n",
- str);
+ pr_warning("%s is not a valid device for the cio_ignore "
+ "kernel parameter\n", str);
return rc;
}
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 3ac2c2019f5e..918e6fce2573 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -19,6 +19,8 @@
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
+#define CCW_BUS_ID_SIZE 20
+
/* In Linux 2.4, we had a channel device layer called "chandev"
* that did all sorts of obscure stuff for networking devices.
* This is another driver that serves as a replacement for just
@@ -89,15 +91,23 @@ ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const
gdev = to_ccwgroupdev(dev);
- if (gdev->state != CCWGROUP_OFFLINE)
- return -EINVAL;
-
+ /* Prevent concurrent online/offline processing and ungrouping. */
+ if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
+ return -EAGAIN;
+ if (gdev->state != CCWGROUP_OFFLINE) {
+ rc = -EINVAL;
+ goto out;
+ }
/* Note that we cannot unregister the device from one of its
* attribute methods, so we have to use this roundabout approach.
*/
rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
- if (rc)
- count = rc;
+out:
+ if (rc) {
+ /* Release onoff "lock" when ungrouping failed. */
+ atomic_set(&gdev->onoff, 0);
+ return rc;
+ }
return count;
}
@@ -172,7 +182,7 @@ static int __get_next_bus_id(const char **buf, char *bus_id)
len = end - start + 1;
end++;
}
- if (len < BUS_ID_SIZE) {
+ if (len < CCW_BUS_ID_SIZE) {
strlcpy(bus_id, start, len);
rc = 0;
} else
@@ -181,7 +191,7 @@ static int __get_next_bus_id(const char **buf, char *bus_id)
return rc;
}
-static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE])
+static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE])
{
int cssid, ssid, devno;
@@ -213,7 +223,7 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
{
struct ccwgroup_device *gdev;
int rc, i;
- char tmp_bus_id[BUS_ID_SIZE];
+ char tmp_bus_id[CCW_BUS_ID_SIZE];
const char *curr_buf;
gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 29826fdd47b8..ebab6ea4659b 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -8,6 +8,9 @@
* Arnd Bergmann (arndb@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -333,6 +336,7 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
struct chp_config_data *data;
struct chp_id chpid;
int num;
+ char *events[3] = {"configure", "deconfigure", "cancel deconfigure"};
CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
if (sei_area->rs != 0)
@@ -343,8 +347,8 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
if (!chp_test_bit(data->map, num))
continue;
chpid.id = num;
- printk(KERN_WARNING "cio: processing configure event %d for "
- "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
+ pr_notice("Processing %s for channel path %x.%02x\n",
+ events[data->op], chpid.cssid, chpid.id);
switch (data->op) {
case 0:
chp_cfg_schedule(chpid, 1);
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index f49f0e502b8d..0a2f2edafc03 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -61,7 +61,7 @@ static void chsc_subchannel_irq(struct subchannel *sch)
}
private->request = NULL;
memcpy(&request->irb, irb, sizeof(*irb));
- stsch(sch->schid, &sch->schib);
+ cio_update_schib(sch);
complete(&request->completion);
put_device(&sch->dev);
}
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 3db2c386546f..8a8df7552969 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -9,6 +9,9 @@
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -104,44 +107,6 @@ cio_get_options (struct subchannel *sch)
return flags;
}
-/*
- * Use tpi to get a pending interrupt, call the interrupt handler and
- * return a pointer to the subchannel structure.
- */
-static int
-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)
- return 0;
- irb = (struct irb *) __LC_IRB;
- /* Store interrupt response block to lowcore. */
- if (tsch (tpi_info->schid, irb) != 0)
- /* Not status pending or not operational. */
- return 1;
- sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
- if (!sch)
- return 1;
- 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));
- if (sch->driver && sch->driver->irq)
- sch->driver->irq(sch);
- spin_unlock(sch->lock);
- irq_exit ();
- if (!irq_context)
- _local_bh_enable();
- return 1;
-}
-
static int
cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
{
@@ -152,11 +117,13 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
else
sch->lpm = 0;
- stsch (sch->schid, &sch->schib);
-
CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
"subchannel 0.%x.%04x!\n", sch->schid.ssid,
sch->schid.sch_no);
+
+ if (cio_update_schib(sch))
+ return -ENODEV;
+
sprintf(dbf_text, "no%s", dev_name(&sch->dev));
CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
@@ -354,7 +321,8 @@ cio_cancel (struct subchannel *sch)
switch (ccode) {
case 0: /* success */
/* Update information in scsw. */
- stsch (sch->schid, &sch->schib);
+ if (cio_update_schib(sch))
+ return -ENODEV;
return 0;
case 1: /* status pending */
return -EBUSY;
@@ -365,30 +333,70 @@ cio_cancel (struct subchannel *sch)
}
}
+
+static void cio_apply_config(struct subchannel *sch, struct schib *schib)
+{
+ schib->pmcw.intparm = sch->config.intparm;
+ schib->pmcw.mbi = sch->config.mbi;
+ schib->pmcw.isc = sch->config.isc;
+ schib->pmcw.ena = sch->config.ena;
+ schib->pmcw.mme = sch->config.mme;
+ schib->pmcw.mp = sch->config.mp;
+ schib->pmcw.csense = sch->config.csense;
+ schib->pmcw.mbfc = sch->config.mbfc;
+ if (sch->config.mbfc)
+ schib->mba = sch->config.mba;
+}
+
+static int cio_check_config(struct subchannel *sch, struct schib *schib)
+{
+ return (schib->pmcw.intparm == sch->config.intparm) &&
+ (schib->pmcw.mbi == sch->config.mbi) &&
+ (schib->pmcw.isc == sch->config.isc) &&
+ (schib->pmcw.ena == sch->config.ena) &&
+ (schib->pmcw.mme == sch->config.mme) &&
+ (schib->pmcw.mp == sch->config.mp) &&
+ (schib->pmcw.csense == sch->config.csense) &&
+ (schib->pmcw.mbfc == sch->config.mbfc) &&
+ (!sch->config.mbfc || (schib->mba == sch->config.mba));
+}
+
/*
- * Function: cio_modify
- * Issues a "Modify Subchannel" on the specified subchannel
+ * cio_commit_config - apply configuration to the subchannel
*/
-int
-cio_modify (struct subchannel *sch)
+int cio_commit_config(struct subchannel *sch)
{
- int ccode, retry, ret;
+ struct schib schib;
+ int ccode, retry, ret = 0;
+
+ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ return -ENODEV;
- ret = 0;
for (retry = 0; retry < 5; retry++) {
- ccode = msch_err (sch->schid, &sch->schib);
- if (ccode < 0) /* -EIO if msch gets a program check. */
+ /* copy desired changes to local schib */
+ cio_apply_config(sch, &schib);
+ ccode = msch_err(sch->schid, &schib);
+ if (ccode < 0) /* -EIO if msch gets a program check. */
return ccode;
switch (ccode) {
case 0: /* successfull */
- return 0;
- case 1: /* status pending */
+ if (stsch(sch->schid, &schib) ||
+ !css_sch_is_valid(&schib))
+ return -ENODEV;
+ if (cio_check_config(sch, &schib)) {
+ /* commit changes from local schib */
+ memcpy(&sch->schib, &schib, sizeof(schib));
+ return 0;
+ }
+ ret = -EAGAIN;
+ break;
+ case 1: /* status pending */
return -EBUSY;
- case 2: /* busy */
- udelay (100); /* allow for recovery */
+ case 2: /* busy */
+ udelay(100); /* allow for recovery */
ret = -EBUSY;
break;
- case 3: /* not operational */
+ case 3: /* not operational */
return -ENODEV;
}
}
@@ -396,6 +404,23 @@ cio_modify (struct subchannel *sch)
}
/**
+ * cio_update_schib - Perform stsch and update schib if subchannel is valid.
+ * @sch: subchannel on which to perform stsch
+ * Return zero on success, -ENODEV otherwise.
+ */
+int cio_update_schib(struct subchannel *sch)
+{
+ struct schib schib;
+
+ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ return -ENODEV;
+
+ memcpy(&sch->schib, &schib, sizeof(schib));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cio_update_schib);
+
+/**
* cio_enable_subchannel - enable a subchannel.
* @sch: subchannel to be enabled
* @intparm: interruption parameter to set
@@ -403,7 +428,6 @@ cio_modify (struct subchannel *sch)
int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
{
char dbf_txt[15];
- int ccode;
int retry;
int ret;
@@ -412,33 +436,27 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
if (sch_is_pseudo_sch(sch))
return -EINVAL;
- ccode = stsch (sch->schid, &sch->schib);
- if (ccode)
+ if (cio_update_schib(sch))
return -ENODEV;
- for (retry = 5, ret = 0; retry > 0; retry--) {
- sch->schib.pmcw.ena = 1;
- sch->schib.pmcw.isc = sch->isc;
- sch->schib.pmcw.intparm = intparm;
- ret = cio_modify(sch);
- if (ret == -ENODEV)
- break;
- if (ret == -EIO)
+ sch->config.ena = 1;
+ sch->config.isc = sch->isc;
+ sch->config.intparm = intparm;
+
+ for (retry = 0; retry < 3; retry++) {
+ ret = cio_commit_config(sch);
+ if (ret == -EIO) {
/*
- * Got a program check in cio_modify. Try without
+ * Got a program check in msch. Try without
* the concurrent sense bit the next time.
*/
- sch->schib.pmcw.csense = 0;
- if (ret == 0) {
- stsch (sch->schid, &sch->schib);
- if (sch->schib.pmcw.ena)
- break;
- }
- if (ret == -EBUSY) {
+ sch->config.csense = 0;
+ } else if (ret == -EBUSY) {
struct irb irb;
if (tsch(sch->schid, &irb) != 0)
break;
- }
+ } else
+ break;
}
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
@@ -453,8 +471,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel);
int cio_disable_subchannel(struct subchannel *sch)
{
char dbf_txt[15];
- int ccode;
- int retry;
int ret;
CIO_TRACE_EVENT (2, "dissch");
@@ -462,8 +478,7 @@ int cio_disable_subchannel(struct subchannel *sch)
if (sch_is_pseudo_sch(sch))
return 0;
- ccode = stsch (sch->schid, &sch->schib);
- if (ccode == 3) /* Not operational. */
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_actl(&sch->schib.scsw) != 0)
@@ -473,24 +488,9 @@ int cio_disable_subchannel(struct subchannel *sch)
*/
return -EBUSY;
- for (retry = 5, ret = 0; retry > 0; retry--) {
- sch->schib.pmcw.ena = 0;
- ret = cio_modify(sch);
- if (ret == -ENODEV)
- break;
- if (ret == -EBUSY)
- /*
- * The subchannel is busy or status pending.
- * We'll disable when the next interrupt was delivered
- * via the state machine.
- */
- break;
- if (ret == 0) {
- stsch (sch->schid, &sch->schib);
- if (!sch->schib.pmcw.ena)
- break;
- }
- }
+ sch->config.ena = 0;
+ ret = cio_commit_config(sch);
+
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
return ret;
@@ -687,6 +687,43 @@ static char console_sch_name[10] = "0.x.xxxx";
static struct io_subchannel_private console_priv;
static int console_subchannel_in_use;
+/*
+ * Use tpi to get a pending interrupt, call the interrupt handler and
+ * return a pointer to the subchannel structure.
+ */
+static int 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)
+ return 0;
+ irb = (struct irb *) __LC_IRB;
+ /* Store interrupt response block to lowcore. */
+ if (tsch(tpi_info->schid, irb) != 0)
+ /* Not status pending or not operational. */
+ return 1;
+ sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
+ if (!sch)
+ return 1;
+ 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));
+ if (sch->driver && sch->driver->irq)
+ sch->driver->irq(sch);
+ spin_unlock(sch->lock);
+ irq_exit();
+ if (!irq_context)
+ _local_bh_enable();
+ return 1;
+}
+
void *cio_get_console_priv(void)
{
return &console_priv;
@@ -780,7 +817,7 @@ cio_probe_console(void)
sch_no = cio_get_console_sch_no();
if (sch_no == -1) {
console_subchannel_in_use = 0;
- printk(KERN_WARNING "cio: No ccw console found!\n");
+ pr_warning("No CCW console was found\n");
return ERR_PTR(-ENODEV);
}
memset(&console_subchannel, 0, sizeof(struct subchannel));
@@ -796,10 +833,9 @@ cio_probe_console(void)
* enable console I/O-interrupt subclass
*/
isc_register(CONSOLE_ISC);
- console_subchannel.schib.pmcw.isc = CONSOLE_ISC;
- console_subchannel.schib.pmcw.intparm =
- (u32)(addr_t)&console_subchannel;
- ret = cio_modify(&console_subchannel);
+ console_subchannel.config.isc = CONSOLE_ISC;
+ console_subchannel.config.intparm = (u32)(addr_t)&console_subchannel;
+ ret = cio_commit_config(&console_subchannel);
if (ret) {
isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
@@ -811,8 +847,8 @@ cio_probe_console(void)
void
cio_release_console(void)
{
- console_subchannel.schib.pmcw.intparm = 0;
- cio_modify(&console_subchannel);
+ console_subchannel.config.intparm = 0;
+ cio_commit_config(&console_subchannel);
isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
}
@@ -852,7 +888,8 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
cc = msch(schid, schib);
if (cc)
return (cc==3?-ENODEV:-EBUSY);
- stsch(schid, schib);
+ if (stsch(schid, schib) || !css_sch_is_valid(schib))
+ return -ENODEV;
if (!schib->pmcw.ena)
return 0;
}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 0fb24784e925..5150fba742ac 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -45,6 +45,19 @@ struct pmcw {
/* ... in an operand exception. */
} __attribute__ ((packed));
+/* Target SCHIB configuration. */
+struct schib_config {
+ u64 mba;
+ u32 intparm;
+ u16 mbi;
+ u32 isc:3;
+ u32 ena:1;
+ u32 mme:2;
+ u32 mp:1;
+ u32 csense:1;
+ u32 mbfc:1;
+} __attribute__ ((packed));
+
/*
* subchannel information block
*/
@@ -82,6 +95,8 @@ struct subchannel {
struct device dev; /* entry in device tree */
struct css_driver *driver;
void *private; /* private per subchannel type data */
+ struct work_struct work;
+ struct schib_config config;
} __attribute__ ((aligned(8)));
#define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */
@@ -100,7 +115,8 @@ extern int cio_start_key (struct subchannel *, struct ccw1 *, __u8, __u8);
extern int cio_cancel (struct subchannel *);
extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
-extern int cio_modify (struct subchannel *);
+extern int cio_update_schib(struct subchannel *sch);
+extern int cio_commit_config(struct subchannel *sch);
int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
int cio_tm_intrg(struct subchannel *sch);
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index a90b28c0be57..dc98b2c63862 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -25,6 +25,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/bootmem.h>
#include <linux/device.h>
#include <linux/init.h>
@@ -185,56 +188,19 @@ static inline void cmf_activate(void *area, unsigned int onoff)
static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
unsigned long address)
{
- int ret;
- int retry;
struct subchannel *sch;
- struct schib *schib;
sch = to_subchannel(cdev->dev.parent);
- schib = &sch->schib;
- /* msch can silently fail, so do it again if necessary */
- for (retry = 0; retry < 3; retry++) {
- /* prepare schib */
- stsch(sch->schid, schib);
- schib->pmcw.mme = mme;
- schib->pmcw.mbfc = mbfc;
- /* address can be either a block address or a block index */
- if (mbfc)
- schib->mba = address;
- else
- schib->pmcw.mbi = address;
-
- /* try to submit it */
- switch(ret = msch_err(sch->schid, schib)) {
- case 0:
- break;
- case 1:
- case 2: /* in I/O or status pending */
- ret = -EBUSY;
- break;
- case 3: /* subchannel is no longer valid */
- ret = -ENODEV;
- break;
- default: /* msch caught an exception */
- ret = -EINVAL;
- break;
- }
- stsch(sch->schid, schib); /* restore the schib */
-
- if (ret)
- break;
- /* check if it worked */
- if (schib->pmcw.mme == mme &&
- schib->pmcw.mbfc == mbfc &&
- (mbfc ? (schib->mba == address)
- : (schib->pmcw.mbi == address)))
- return 0;
+ sch->config.mme = mme;
+ sch->config.mbfc = mbfc;
+ /* address can be either a block address or a block index */
+ if (mbfc)
+ sch->config.mba = address;
+ else
+ sch->config.mbi = address;
- ret = -EINVAL;
- }
-
- return ret;
+ return cio_commit_config(sch);
}
struct set_schib_struct {
@@ -338,7 +304,7 @@ static int cmf_copy_block(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
- if (stsch(sch->schid, &sch->schib))
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) {
@@ -1359,9 +1325,8 @@ static int __init init_cmf(void)
default:
return 1;
}
-
- printk(KERN_INFO "cio: Channel measurement facility using %s "
- "format (%s)\n", format_string, detect_string);
+ pr_info("Channel measurement facility initialized using format "
+ "%s (mode %s)\n", format_string, detect_string);
return 0;
}
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 76bbb1e74c29..8019288bc6de 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -6,6 +6,10 @@
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
*/
+
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -128,8 +132,8 @@ css_free_subchannel(struct subchannel *sch)
{
if (sch) {
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
kfree(sch->lock);
kfree(sch);
}
@@ -844,8 +848,8 @@ out:
s390_unregister_crw_handler(CRW_RSC_CSS);
chsc_free_sei_area();
kfree(slow_subchannel_set);
- printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
- ret);
+ pr_alert("The CSS device driver initialization failed with "
+ "errno=%d\n", ret);
return ret;
}
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 4e4008325e28..23d5752349b5 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -376,19 +376,23 @@ int ccw_device_set_offline(struct ccw_device *cdev)
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
}
spin_unlock_irq(cdev->ccwlock);
+ /* Give up reference from ccw_device_set_online(). */
+ put_device(&cdev->dev);
return ret;
}
spin_unlock_irq(cdev->ccwlock);
- if (ret == 0)
+ if (ret == 0) {
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
- else {
+ /* Give up reference from ccw_device_set_online(). */
+ put_device(&cdev->dev);
+ } else {
CIO_MSG_EVENT(0, "ccw_device_offline returned %d, "
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
cdev->online = 1;
}
- return ret;
+ return ret;
}
/**
@@ -411,6 +415,9 @@ int ccw_device_set_online(struct ccw_device *cdev)
return -ENODEV;
if (cdev->online || !cdev->drv)
return -EINVAL;
+ /* Hold on to an extra reference while device is online. */
+ if (!get_device(&cdev->dev))
+ return -ENODEV;
spin_lock_irq(cdev->ccwlock);
ret = ccw_device_online(cdev);
@@ -422,10 +429,15 @@ int ccw_device_set_online(struct ccw_device *cdev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return ret;
}
- if (cdev->private->state != DEV_STATE_ONLINE)
+ if (cdev->private->state != DEV_STATE_ONLINE) {
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return -ENODEV;
+ }
if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) {
cdev->online = 1;
return 0;
@@ -440,6 +452,8 @@ int ccw_device_set_online(struct ccw_device *cdev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return (ret == 0) ? -ENODEV : ret;
}
@@ -704,6 +718,8 @@ ccw_device_release(struct device *dev)
struct ccw_device *cdev;
cdev = to_ccwdev(dev);
+ /* Release reference of parent subchannel. */
+ put_device(cdev->dev.parent);
kfree(cdev->private);
kfree(cdev);
}
@@ -735,8 +751,8 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
/* Do first half of device_register. */
device_initialize(&cdev->dev);
if (!get_device(&sch->dev)) {
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
+ /* Release reference from device_initialize(). */
+ put_device(&cdev->dev);
return -ENODEV;
}
return 0;
@@ -778,37 +794,55 @@ static void sch_attach_disconnected_device(struct subchannel *sch,
struct subchannel *other_sch;
int ret;
- other_sch = to_subchannel(get_device(cdev->dev.parent));
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
+ other_sch = to_subchannel(cdev->dev.parent);
+ /* Note: device_move() changes cdev->dev.parent */
ret = device_move(&cdev->dev, &sch->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed "
"(ret=%d)!\n", cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
- put_device(&other_sch->dev);
+ /* Put reference for new parent. */
+ put_device(&sch->dev);
return;
}
sch_set_cdev(other_sch, NULL);
/* No need to keep a subchannel without ccw device around. */
css_sch_device_unregister(other_sch);
- put_device(&other_sch->dev);
sch_attach_device(sch, cdev);
+ /* Put reference for old parent. */
+ put_device(&other_sch->dev);
}
static void sch_attach_orphaned_device(struct subchannel *sch,
struct ccw_device *cdev)
{
int ret;
+ struct subchannel *pseudo_sch;
- /* Try to move the ccw device to its new subchannel. */
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
+ pseudo_sch = to_subchannel(cdev->dev.parent);
+ /*
+ * Try to move the ccw device to its new subchannel.
+ * Note: device_move() changes cdev->dev.parent
+ */
ret = device_move(&cdev->dev, &sch->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
"failed (ret=%d)!\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
+ /* Put reference for new parent. */
+ put_device(&sch->dev);
return;
}
sch_attach_device(sch, cdev);
+ /* Put reference on pseudo subchannel. */
+ put_device(&pseudo_sch->dev);
}
static void sch_create_and_recog_new_device(struct subchannel *sch)
@@ -830,9 +864,11 @@ static void sch_create_and_recog_new_device(struct subchannel *sch)
spin_lock_irq(sch->lock);
sch_set_cdev(sch, NULL);
spin_unlock_irq(sch->lock);
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
css_sch_device_unregister(sch);
+ /* Put reference from io_subchannel_create_ccwdev(). */
+ put_device(&sch->dev);
+ /* Give up initial reference. */
+ put_device(&cdev->dev);
}
}
@@ -854,15 +890,20 @@ void ccw_device_move_to_orphanage(struct work_struct *work)
dev_id.devno = sch->schib.pmcw.dev;
dev_id.ssid = sch->schid.ssid;
+ /* Increase refcount for pseudo subchannel. */
+ get_device(&css->pseudo_subchannel->dev);
/*
* Move the orphaned ccw device to the orphanage so the replacing
* ccw device can take its place on the subchannel.
+ * Note: device_move() changes cdev->dev.parent
*/
ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
"(ret=%d)!\n", cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
+ /* Decrease refcount for pseudo subchannel again. */
+ put_device(&css->pseudo_subchannel->dev);
return;
}
cdev->ccwlock = css->pseudo_subchannel->lock;
@@ -875,17 +916,23 @@ void ccw_device_move_to_orphanage(struct work_struct *work)
if (replacing_cdev) {
sch_attach_disconnected_device(sch, replacing_cdev);
/* Release reference from get_disc_ccwdev_by_dev_id() */
- put_device(&cdev->dev);
+ put_device(&replacing_cdev->dev);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
return;
}
replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
if (replacing_cdev) {
sch_attach_orphaned_device(sch, replacing_cdev);
/* Release reference from get_orphaned_ccwdev_by_dev_id() */
- put_device(&cdev->dev);
+ put_device(&replacing_cdev->dev);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
return;
}
sch_create_and_recog_new_device(sch);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
}
/*
@@ -903,6 +950,14 @@ io_subchannel_register(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
sch = to_subchannel(cdev->dev.parent);
+ /*
+ * Check if subchannel is still registered. It may have become
+ * unregistered if a machine check hit us after finishing
+ * device recognition but before the register work could be
+ * queued.
+ */
+ if (!device_is_registered(&sch->dev))
+ goto out_err;
css_update_ssd_info(sch);
/*
* io_subchannel_register() will also be called after device
@@ -910,7 +965,7 @@ io_subchannel_register(struct work_struct *work)
* be registered). We need to reprobe since we may now have sense id
* information.
*/
- if (klist_node_attached(&cdev->dev.knode_parent)) {
+ if (device_is_registered(&cdev->dev)) {
if (!cdev->drv) {
ret = device_reprobe(&cdev->dev);
if (ret)
@@ -934,22 +989,19 @@ io_subchannel_register(struct work_struct *work)
CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
- put_device(&cdev->dev);
spin_lock_irqsave(sch->lock, flags);
sch_set_cdev(sch, NULL);
spin_unlock_irqrestore(sch->lock, flags);
- kfree (cdev->private);
- kfree (cdev);
- put_device(&sch->dev);
- if (atomic_dec_and_test(&ccw_device_init_count))
- wake_up(&ccw_device_init_wq);
- return;
+ /* Release initial device reference. */
+ put_device(&cdev->dev);
+ goto out_err;
}
- put_device(&cdev->dev);
out:
cdev->private->flags.recog_done = 1;
- put_device(&sch->dev);
wake_up(&cdev->private->wait_q);
+out_err:
+ /* Release reference for workqueue processing. */
+ put_device(&cdev->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
@@ -968,8 +1020,8 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
sch = to_subchannel(cdev->dev.parent);
css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
/* Release cdev reference for workqueue processing.*/
put_device(&cdev->dev);
/* Release subchannel reference for local processing. */
@@ -998,8 +1050,6 @@ 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;
@@ -1070,10 +1120,15 @@ static void ccw_device_move_to_sch(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
sch = priv->sch;
cdev = priv->cdev;
- former_parent = ccw_device_is_orphan(cdev) ?
- NULL : to_subchannel(get_device(cdev->dev.parent));
+ former_parent = to_subchannel(cdev->dev.parent);
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
mutex_lock(&sch->reg_mutex);
- /* Try to move the ccw device to its new subchannel. */
+ /*
+ * Try to move the ccw device to its new subchannel.
+ * Note: device_move() changes cdev->dev.parent
+ */
rc = device_move(&cdev->dev, &sch->dev);
mutex_unlock(&sch->reg_mutex);
if (rc) {
@@ -1083,21 +1138,23 @@ static void ccw_device_move_to_sch(struct work_struct *work)
cdev->private->dev_id.devno, sch->schid.ssid,
sch->schid.sch_no, rc);
css_sch_device_unregister(sch);
+ /* Put reference for new parent again. */
+ put_device(&sch->dev);
goto out;
}
- if (former_parent) {
+ if (!sch_is_pseudo_sch(former_parent)) {
spin_lock_irq(former_parent->lock);
sch_set_cdev(former_parent, NULL);
spin_unlock_irq(former_parent->lock);
css_sch_device_unregister(former_parent);
/* Reset intparm to zeroes. */
- former_parent->schib.pmcw.intparm = 0;
- cio_modify(former_parent);
+ former_parent->config.intparm = 0;
+ cio_commit_config(former_parent);
}
sch_attach_device(sch, cdev);
out:
- if (former_parent)
- put_device(&former_parent->dev);
+ /* Put reference for old parent. */
+ put_device(&former_parent->dev);
put_device(&cdev->dev);
}
@@ -1113,6 +1170,15 @@ static void io_subchannel_irq(struct subchannel *sch)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
}
+void io_subchannel_init_config(struct subchannel *sch)
+{
+ memset(&sch->config, 0, sizeof(sch->config));
+ sch->config.csense = 1;
+ /* Use subchannel mp mode when there is more than 1 installed CHPID. */
+ if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0)
+ sch->config.mp = 1;
+}
+
static void io_subchannel_init_fields(struct subchannel *sch)
{
if (cio_is_console(sch->schid))
@@ -1127,18 +1193,34 @@ static void io_subchannel_init_fields(struct subchannel *sch)
sch->schib.pmcw.dev, sch->schid.ssid,
sch->schid.sch_no, sch->schib.pmcw.pim,
sch->schib.pmcw.pam, sch->schib.pmcw.pom);
- /* Initially set up some fields in the pmcw. */
- sch->schib.pmcw.ena = 0;
- sch->schib.pmcw.csense = 1; /* concurrent sense */
- if ((sch->lpm & (sch->lpm - 1)) != 0)
- sch->schib.pmcw.mp = 1; /* multipath mode */
- /* clean up possible residual cmf stuff */
- sch->schib.pmcw.mme = 0;
- sch->schib.pmcw.mbfc = 0;
- sch->schib.pmcw.mbi = 0;
- sch->schib.mba = 0;
+
+ io_subchannel_init_config(sch);
}
+static void io_subchannel_do_unreg(struct work_struct *work)
+{
+ struct subchannel *sch;
+
+ sch = container_of(work, struct subchannel, work);
+ css_sch_device_unregister(sch);
+ /* Reset intparm to zeroes. */
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
+ put_device(&sch->dev);
+}
+
+/* Schedule unregister if we have no cdev. */
+static void io_subchannel_schedule_removal(struct subchannel *sch)
+{
+ get_device(&sch->dev);
+ INIT_WORK(&sch->work, io_subchannel_do_unreg);
+ queue_work(slow_path_wq, &sch->work);
+}
+
+/*
+ * Note: We always return 0 so that we bind to the device even on error.
+ * This is needed so that our remove function is called on unregister.
+ */
static int io_subchannel_probe(struct subchannel *sch)
{
struct ccw_device *cdev;
@@ -1168,9 +1250,8 @@ static int io_subchannel_probe(struct subchannel *sch)
ccw_device_register(cdev);
/*
* Check if the device is already online. If it is
- * the reference count needs to be corrected
- * (see ccw_device_online and css_init_done for the
- * ugly details).
+ * the reference count needs to be corrected since we
+ * didn't obtain a reference in ccw_device_set_online.
*/
if (cdev->private->state != DEV_STATE_NOT_OPER &&
cdev->private->state != DEV_STATE_OFFLINE &&
@@ -1179,23 +1260,24 @@ static int io_subchannel_probe(struct subchannel *sch)
return 0;
}
io_subchannel_init_fields(sch);
- /*
- * First check if a fitting device may be found amongst the
- * disconnected devices or in the orphanage.
- */
- dev_id.devno = sch->schib.pmcw.dev;
- dev_id.ssid = sch->schid.ssid;
+ rc = cio_commit_config(sch);
+ if (rc)
+ goto out_schedule;
rc = sysfs_create_group(&sch->dev.kobj,
&io_subchannel_attr_group);
if (rc)
- return rc;
+ goto out_schedule;
/* Allocate I/O subchannel private data. */
sch->private = kzalloc(sizeof(struct io_subchannel_private),
GFP_KERNEL | GFP_DMA);
- if (!sch->private) {
- rc = -ENOMEM;
+ if (!sch->private)
goto out_err;
- }
+ /*
+ * First check if a fitting device may be found amongst the
+ * disconnected devices or in the orphanage.
+ */
+ dev_id.devno = sch->schib.pmcw.dev;
+ dev_id.ssid = sch->schid.ssid;
cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
if (!cdev)
cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
@@ -1213,24 +1295,21 @@ static int io_subchannel_probe(struct subchannel *sch)
return 0;
}
cdev = io_subchannel_create_ccwdev(sch);
- if (IS_ERR(cdev)) {
- rc = PTR_ERR(cdev);
+ if (IS_ERR(cdev))
goto out_err;
- }
rc = io_subchannel_recog(cdev, sch);
if (rc) {
spin_lock_irqsave(sch->lock, flags);
- sch_set_cdev(sch, NULL);
+ io_subchannel_recog_done(cdev);
spin_unlock_irqrestore(sch->lock, flags);
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
- goto out_err;
}
return 0;
out_err:
kfree(sch->private);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
- return rc;
+out_schedule:
+ io_subchannel_schedule_removal(sch);
+ return 0;
}
static int
@@ -1275,10 +1354,7 @@ static void io_subchannel_verify(struct subchannel *sch)
static int check_for_io_on_path(struct subchannel *sch, int mask)
{
- int cc;
-
- cc = stsch(sch->schid, &sch->schib);
- if (cc)
+ if (cio_update_schib(sch))
return 0;
if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
return 1;
@@ -1347,15 +1423,13 @@ static int io_subchannel_chp_event(struct subchannel *sch,
io_subchannel_verify(sch);
break;
case CHP_OFFLINE:
- if (stsch(sch->schid, &sch->schib))
- return -ENXIO;
- if (!css_sch_is_valid(&sch->schib))
+ if (cio_update_schib(sch))
return -ENODEV;
io_subchannel_terminate_path(sch, mask);
break;
case CHP_ONLINE:
- if (stsch(sch->schid, &sch->schib))
- return -ENXIO;
+ if (cio_update_schib(sch))
+ return -ENODEV;
sch->lpm |= mask & sch->opm;
io_subchannel_verify(sch);
break;
@@ -1610,8 +1684,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
spin_lock_irqsave(sch->lock, flags);
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
break;
case REPROBE:
ccw_device_trigger_reprobe(cdev);
@@ -1652,6 +1726,9 @@ static int ccw_device_console_enable(struct ccw_device *cdev,
sch->private = cio_get_console_priv();
memset(sch->private, 0, sizeof(struct io_subchannel_private));
io_subchannel_init_fields(sch);
+ rc = cio_commit_config(sch);
+ if (rc)
+ return rc;
sch->driver = &io_subchannel_driver;
/* Initialize the ccw_device structure. */
cdev->dev.parent= &sch->dev;
@@ -1723,7 +1800,7 @@ __ccwdev_check_busid(struct device *dev, void *id)
bus_id = id;
- return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0);
+ return (strcmp(bus_id, dev_name(dev)) == 0);
}
@@ -1806,6 +1883,8 @@ ccw_device_remove (struct device *dev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up reference obtained in ccw_device_set_online(). */
+ put_device(&cdev->dev);
}
ccw_device_set_timeout(cdev, 0);
cdev->drv = NULL;
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 104ed669db43..0f2e63ea48de 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -76,6 +76,7 @@ extern wait_queue_head_t ccw_device_init_wq;
extern atomic_t ccw_device_init_count;
void io_subchannel_recog_done(struct ccw_device *cdev);
+void io_subchannel_init_config(struct subchannel *sch);
int ccw_device_cancel_halt_clear(struct ccw_device *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 10bc03940fb3..8df5eaafc5ab 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -140,8 +140,7 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
int ret;
sch = to_subchannel(cdev->dev.parent);
- ret = stsch(sch->schid, &sch->schib);
- if (ret || !sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return -ENODEV;
if (!sch->schib.pmcw.ena)
/* Not operational -> done. */
@@ -245,11 +244,13 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
* through ssch() and the path information is up to date.
*/
old_lpm = sch->lpm;
- stsch(sch->schid, &sch->schib);
- sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
/* Check since device may again have become not operational. */
- if (!sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
state = DEV_STATE_NOT_OPER;
+ else
+ sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID)
/* Force reprobe on all chpids. */
old_lpm = 0;
@@ -399,9 +400,6 @@ ccw_device_done(struct ccw_device *cdev, int state)
ccw_device_oper_notify(cdev);
}
wake_up(&cdev->private->wait_q);
-
- if (css_init_done && state != DEV_STATE_ONLINE)
- put_device (&cdev->dev);
}
static int cmp_pgid(struct pgid *p1, struct pgid *p2)
@@ -552,7 +550,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
sch = to_subchannel(cdev->dev.parent);
/* Update schib - pom may have changed. */
- stsch(sch->schid, &sch->schib);
+ if (cio_update_schib(sch)) {
+ cdev->private->flags.donotify = 0;
+ ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+ return;
+ }
/* Update lpm with verified path mask. */
sch->lpm = sch->vpm;
/* Repeat path verification? */
@@ -611,8 +613,6 @@ ccw_device_online(struct ccw_device *cdev)
(cdev->private->state != DEV_STATE_BOXED))
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
- if (css_init_done && !get_device(&cdev->dev))
- return -ENODEV;
ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
if (ret != 0) {
/* Couldn't enable the subchannel for i/o. Sick device. */
@@ -672,7 +672,7 @@ ccw_device_offline(struct ccw_device *cdev)
return 0;
}
sch = to_subchannel(cdev->dev.parent);
- if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_actl(&sch->schib.scsw) != 0)
return -EBUSY;
@@ -750,7 +750,10 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
* Since we might not just be coming from an interrupt from the
* subchannel we have to update the schib.
*/
- stsch(sch->schid, &sch->schib);
+ if (cio_update_schib(sch)) {
+ ccw_device_verify_done(cdev, -ENODEV);
+ return;
+ }
if (scsw_actl(&sch->schib.scsw) != 0 ||
(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
@@ -1016,20 +1019,21 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
/* Update some values. */
- if (stsch(sch->schid, &sch->schib))
- return;
- if (!sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return;
/*
* The pim, pam, pom values may not be accurate, but they are the best
* we have before performing device selection :/
*/
sch->lpm = sch->schib.pmcw.pam & sch->opm;
- /* Re-set some bits in the pmcw that were lost. */
- sch->schib.pmcw.csense = 1;
- sch->schib.pmcw.ena = 0;
- if ((sch->lpm & (sch->lpm - 1)) != 0)
- sch->schib.pmcw.mp = 1;
+ /*
+ * Use the initial configuration since we can't be shure that the old
+ * paths are valid.
+ */
+ io_subchannel_init_config(sch);
+ if (cio_commit_config(sch))
+ return;
+
/* We should also udate ssd info, but this has to wait. */
/* Check if this is another device which appeared on the same sch. */
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 86bc94eb607f..fc5ca1dd52b3 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -504,7 +504,7 @@ ccw_device_verify_start(struct ccw_device *cdev)
sch->vpm = 0;
/* Get current pam. */
- if (stsch(sch->schid, &sch->schib)) {
+ if (cio_update_schib(sch)) {
ccw_device_verify_done(cdev, -ENODEV);
return;
}
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 1b03c5423be2..5814dbee2410 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -56,7 +56,8 @@ ccw_device_path_notoper(struct ccw_device *cdev)
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
- stsch (sch->schid, &sch->schib);
+ if (cio_update_schib(sch))
+ goto doverify;
CIO_MSG_EVENT(0, "%s(0.%x.%04x) - path(s) %02x are "
"not operational \n", __func__,
@@ -64,6 +65,7 @@ ccw_device_path_notoper(struct ccw_device *cdev)
sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom;
+doverify:
cdev->private->flags.doverify = 1;
}
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index e3ea1d5f2810..42f2b09631b6 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -10,10 +10,10 @@
#include <asm/page.h>
#include <asm/schid.h>
+#include <asm/debug.h>
#include "chsc.h"
#define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */
-#define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */
#define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */
/*
@@ -111,12 +111,12 @@ static inline int do_sqbs(u64 token, unsigned char state, int queue,
}
static inline int do_eqbs(u64 token, unsigned char *state, int queue,
- int *start, int *count)
+ int *start, int *count, int ack)
{
register unsigned long _ccq asm ("0") = *count;
register unsigned long _token asm ("1") = token;
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
- unsigned long _state = 0;
+ unsigned long _state = (unsigned long)ack << 63;
asm volatile(
" .insn rrf,0xB99c0000,%1,%2,0,0"
@@ -133,7 +133,7 @@ static inline int do_eqbs(u64 token, unsigned char *state, int queue,
static inline int do_sqbs(u64 token, unsigned char state, int queue,
int *start, int *count) { return 0; }
static inline int do_eqbs(u64 token, unsigned char *state, int queue,
- int *start, int *count) { return 0; }
+ int *start, int *count, int ack) { return 0; }
#endif /* CONFIG_64BIT */
struct qdio_irq;
@@ -186,20 +186,14 @@ struct qdio_input_q {
/* input buffer acknowledgement flag */
int polling;
+ /* how much sbals are acknowledged with qebsm */
+ int ack_count;
+
/* last time of noticing incoming data */
u64 timestamp;
-
- /* lock for clearing the acknowledgement */
- spinlock_t lock;
};
struct qdio_output_q {
- /* failed siga-w attempts*/
- atomic_t busy_siga_counter;
-
- /* start time of busy condition */
- u64 timestamp;
-
/* PCIs are enabled for the queue */
int pci_out_enabled;
@@ -250,6 +244,7 @@ struct qdio_q {
struct qdio_irq *irq_ptr;
struct tasklet_struct tasklet;
+ spinlock_t lock;
/* error condition during a data transfer */
unsigned int qdio_error;
@@ -300,11 +295,13 @@ struct qdio_irq {
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
+ debug_info_t *debug_area;
struct mutex setup_mutex;
};
/* helper functions */
#define queue_type(q) q->irq_ptr->qib.qfmt
+#define SCH_NO(q) (q->irq_ptr->schid.sch_no)
#define is_thinint_irq(irq) \
(irq->qib.qfmt == QDIO_IQDIO_QFMT || \
@@ -348,10 +345,13 @@ static inline unsigned long long get_usecs(void)
((bufnr + 1) & QDIO_MAX_BUFFERS_MASK)
#define add_buf(bufnr, inc) \
((bufnr + inc) & QDIO_MAX_BUFFERS_MASK)
+#define sub_buf(bufnr, dec) \
+ ((bufnr - dec) & QDIO_MAX_BUFFERS_MASK)
/* prototypes for thin interrupt */
void qdio_sync_after_thinint(struct qdio_q *q);
-int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state);
+int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state,
+ int auto_ack);
void qdio_check_outbound_after_thinint(struct qdio_q *q);
int qdio_inbound_q_moved(struct qdio_q *q);
void qdio_kick_inbound_handler(struct qdio_q *q);
@@ -378,10 +378,15 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs,
int nr_output_qs);
void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr);
+int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
+ struct subchannel_id *schid,
+ struct qdio_ssqd_desc *data);
int qdio_setup_irq(struct qdio_initialize *init_data);
void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
void qdio_release_memory(struct qdio_irq *irq_ptr);
+int qdio_setup_create_sysfs(struct ccw_device *cdev);
+void qdio_setup_destroy_sysfs(struct ccw_device *cdev);
int qdio_setup_init(void);
void qdio_setup_exit(void);
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index f05590355be8..f8a3b6967f69 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -14,7 +14,7 @@
#include "qdio.h"
debug_info_t *qdio_dbf_setup;
-debug_info_t *qdio_dbf_trace;
+debug_info_t *qdio_dbf_error;
static struct dentry *debugfs_root;
#define MAX_DEBUGFS_QUEUES 32
@@ -22,59 +22,33 @@ static struct dentry *debugfs_queues[MAX_DEBUGFS_QUEUES] = { NULL };
static DEFINE_MUTEX(debugfs_mutex);
#define QDIO_DEBUGFS_NAME_LEN 40
-void qdio_allocate_do_dbf(struct qdio_initialize *init_data)
+void qdio_allocate_dbf(struct qdio_initialize *init_data,
+ struct qdio_irq *irq_ptr)
{
- char dbf_text[20];
-
- sprintf(dbf_text, "qfmt:%x", init_data->q_format);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, init_data->adapter_name, 8);
- sprintf(dbf_text, "qpff%4x", init_data->qib_param_field_format);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &init_data->qib_param_field, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->input_slib_elements, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_slib_elements, sizeof(void *));
- sprintf(dbf_text, "niq:%4x", init_data->no_input_qs);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- sprintf(dbf_text, "noq:%4x", init_data->no_output_qs);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &init_data->input_handler, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_handler, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->int_parm, sizeof(long));
- QDIO_DBF_HEX0(0, setup, &init_data->flags, sizeof(long));
- QDIO_DBF_HEX0(0, setup, &init_data->input_sbal_addr_array, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_sbal_addr_array, sizeof(void *));
-}
-
-static void qdio_unregister_dbf_views(void)
-{
- if (qdio_dbf_setup)
- debug_unregister(qdio_dbf_setup);
- if (qdio_dbf_trace)
- debug_unregister(qdio_dbf_trace);
-}
-
-static int qdio_register_dbf_views(void)
-{
- qdio_dbf_setup = debug_register("qdio_setup", QDIO_DBF_SETUP_PAGES,
- QDIO_DBF_SETUP_NR_AREAS,
- QDIO_DBF_SETUP_LEN);
- if (!qdio_dbf_setup)
- goto oom;
- debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
- debug_set_level(qdio_dbf_setup, QDIO_DBF_SETUP_LEVEL);
-
- qdio_dbf_trace = debug_register("qdio_trace", QDIO_DBF_TRACE_PAGES,
- QDIO_DBF_TRACE_NR_AREAS,
- QDIO_DBF_TRACE_LEN);
- if (!qdio_dbf_trace)
- goto oom;
- debug_register_view(qdio_dbf_trace, &debug_hex_ascii_view);
- debug_set_level(qdio_dbf_trace, QDIO_DBF_TRACE_LEVEL);
- return 0;
-oom:
- qdio_unregister_dbf_views();
- return -ENOMEM;
+ char text[20];
+
+ DBF_EVENT("qfmt:%1d", init_data->q_format);
+ DBF_HEX(init_data->adapter_name, 8);
+ DBF_EVENT("qpff%4x", init_data->qib_param_field_format);
+ DBF_HEX(&init_data->qib_param_field, sizeof(void *));
+ DBF_HEX(&init_data->input_slib_elements, sizeof(void *));
+ DBF_HEX(&init_data->output_slib_elements, sizeof(void *));
+ DBF_EVENT("niq:%1d noq:%1d", init_data->no_input_qs,
+ init_data->no_output_qs);
+ DBF_HEX(&init_data->input_handler, sizeof(void *));
+ DBF_HEX(&init_data->output_handler, sizeof(void *));
+ DBF_HEX(&init_data->int_parm, sizeof(long));
+ DBF_HEX(&init_data->flags, sizeof(long));
+ DBF_HEX(&init_data->input_sbal_addr_array, sizeof(void *));
+ DBF_HEX(&init_data->output_sbal_addr_array, sizeof(void *));
+ DBF_EVENT("irq:%8lx", (unsigned long)irq_ptr);
+
+ /* allocate trace view for the interface */
+ snprintf(text, 20, "qdio_%s", dev_name(&init_data->cdev->dev));
+ irq_ptr->debug_area = debug_register(text, 2, 1, 16);
+ debug_register_view(irq_ptr->debug_area, &debug_hex_ascii_view);
+ debug_set_level(irq_ptr->debug_area, DBF_WARN);
+ DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf created");
}
static int qstat_show(struct seq_file *m, void *v)
@@ -86,16 +60,18 @@ static int qstat_show(struct seq_file *m, void *v)
if (!q)
return 0;
- seq_printf(m, "device state indicator: %d\n", *q->irq_ptr->dsci);
+ seq_printf(m, "device state indicator: %d\n", *(u32 *)q->irq_ptr->dsci);
seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used));
seq_printf(m, "ftc: %d\n", q->first_to_check);
seq_printf(m, "last_move_ftc: %d\n", q->last_move_ftc);
seq_printf(m, "polling: %d\n", q->u.in.polling);
+ seq_printf(m, "ack count: %d\n", q->u.in.ack_count);
seq_printf(m, "slsb buffer states:\n");
+ seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n");
qdio_siga_sync_q(q);
for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
- get_buf_state(q, i, &state);
+ get_buf_state(q, i, &state, 0);
switch (state) {
case SLSB_P_INPUT_NOT_INIT:
case SLSB_P_OUTPUT_NOT_INIT:
@@ -127,6 +103,7 @@ static int qstat_show(struct seq_file *m, void *v)
seq_printf(m, "\n");
}
seq_printf(m, "\n");
+ seq_printf(m, "|64 |72 |80 |88 |96 |104 |112 | 127|\n");
return 0;
}
@@ -223,11 +200,24 @@ void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cd
int __init qdio_debug_init(void)
{
debugfs_root = debugfs_create_dir("qdio_queues", NULL);
- return qdio_register_dbf_views();
+
+ qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16);
+ debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
+ debug_set_level(qdio_dbf_setup, DBF_INFO);
+ DBF_EVENT("dbf created\n");
+
+ qdio_dbf_error = debug_register("qdio_error", 4, 1, 16);
+ debug_register_view(qdio_dbf_error, &debug_hex_ascii_view);
+ debug_set_level(qdio_dbf_error, DBF_INFO);
+ DBF_ERROR("dbf created\n");
+ return 0;
}
void qdio_debug_exit(void)
{
debugfs_remove(debugfs_root);
- qdio_unregister_dbf_views();
+ if (qdio_dbf_setup)
+ debug_unregister(qdio_dbf_setup);
+ if (qdio_dbf_error)
+ debug_unregister(qdio_dbf_error);
}
diff --git a/drivers/s390/cio/qdio_debug.h b/drivers/s390/cio/qdio_debug.h
index 5a4d85b829ad..5d70bd162ae9 100644
--- a/drivers/s390/cio/qdio_debug.h
+++ b/drivers/s390/cio/qdio_debug.h
@@ -12,80 +12,72 @@
#include <asm/qdio.h>
#include "qdio.h"
-#define QDIO_DBF_HEX(ex, name, level, addr, len) \
+/* that gives us 15 characters in the text event views */
+#define QDIO_DBF_LEN 16
+
+extern debug_info_t *qdio_dbf_setup;
+extern debug_info_t *qdio_dbf_error;
+
+/* sort out low debug levels early to avoid wasted sprints */
+static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
+{
+ return (level <= dbf_grp->level);
+}
+
+#define DBF_ERR 3 /* error conditions */
+#define DBF_WARN 4 /* warning conditions */
+#define DBF_INFO 6 /* informational */
+
+#undef DBF_EVENT
+#undef DBF_ERROR
+#undef DBF_DEV_EVENT
+
+#define DBF_EVENT(text...) \
do { \
- if (ex) \
- debug_exception(qdio_dbf_##name, level, (void *)(addr), len); \
- else \
- debug_event(qdio_dbf_##name, level, (void *)(addr), len); \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(qdio_dbf_setup, DBF_ERR, debug_buffer); \
} while (0)
-#define QDIO_DBF_TEXT(ex, name, level, text) \
+
+#define DBF_HEX(addr, len) \
do { \
- if (ex) \
- debug_text_exception(qdio_dbf_##name, level, text); \
- else \
- debug_text_event(qdio_dbf_##name, level, text); \
+ debug_event(qdio_dbf_setup, DBF_ERR, (void*)(addr), len); \
} while (0)
-#define QDIO_DBF_HEX0(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 0, addr, len)
-#define QDIO_DBF_HEX1(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 1, addr, len)
-#define QDIO_DBF_HEX2(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 2, addr, len)
-
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_HEX3(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 3, addr, len)
-#define QDIO_DBF_HEX4(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 4, addr, len)
-#define QDIO_DBF_HEX5(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 5, addr, len)
-#define QDIO_DBF_HEX6(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 6, addr, len)
-#else
-#define QDIO_DBF_HEX3(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX4(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX5(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX6(ex, name, addr, len) do {} while (0)
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_DBF_TEXT0(ex, name, text) QDIO_DBF_TEXT(ex, name, 0, text)
-#define QDIO_DBF_TEXT1(ex, name, text) QDIO_DBF_TEXT(ex, name, 1, text)
-#define QDIO_DBF_TEXT2(ex, name, text) QDIO_DBF_TEXT(ex, name, 2, text)
-
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_TEXT3(ex, name, text) QDIO_DBF_TEXT(ex, name, 3, text)
-#define QDIO_DBF_TEXT4(ex, name, text) QDIO_DBF_TEXT(ex, name, 4, text)
-#define QDIO_DBF_TEXT5(ex, name, text) QDIO_DBF_TEXT(ex, name, 5, text)
-#define QDIO_DBF_TEXT6(ex, name, text) QDIO_DBF_TEXT(ex, name, 6, text)
-#else
-#define QDIO_DBF_TEXT3(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT4(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT5(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT6(ex, name, text) do {} while (0)
-#endif /* CONFIG_QDIO_DEBUG */
+#define DBF_ERROR(text...) \
+ do { \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(qdio_dbf_error, DBF_ERR, debug_buffer); \
+ } while (0)
-/* s390dbf views */
-#define QDIO_DBF_SETUP_LEN 8
-#define QDIO_DBF_SETUP_PAGES 8
-#define QDIO_DBF_SETUP_NR_AREAS 1
+#define DBF_ERROR_HEX(addr, len) \
+ do { \
+ debug_event(qdio_dbf_error, DBF_ERR, (void*)(addr), len); \
+ } while (0)
-#define QDIO_DBF_TRACE_LEN 8
-#define QDIO_DBF_TRACE_NR_AREAS 2
-#ifdef CONFIG_QDIO_DEBUG
-#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 8
-#define QDIO_DBF_SETUP_LEVEL 2
-#define QDIO_DBF_TRACE_LEVEL 2
-#endif /* CONFIG_QDIO_DEBUG */
+#define DBF_DEV_EVENT(level, device, text...) \
+ do { \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ if (qdio_dbf_passes(device->debug_area, level)) { \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(device->debug_area, level, debug_buffer); \
+ } \
+ } while (0)
-extern debug_info_t *qdio_dbf_setup;
-extern debug_info_t *qdio_dbf_trace;
+#define DBF_DEV_HEX(level, device, addr, len) \
+ do { \
+ debug_event(device->debug_area, level, (void*)(addr), len); \
+ } while (0)
-void qdio_allocate_do_dbf(struct qdio_initialize *init_data);
-void debug_print_bstat(struct qdio_q *q);
+void qdio_allocate_dbf(struct qdio_initialize *init_data,
+ struct qdio_irq *irq_ptr);
void qdio_setup_debug_entries(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
int qdio_debug_init(void);
void qdio_debug_exit(void);
+
#endif
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 7c8659151993..744f928a59ea 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -74,7 +74,7 @@ static inline int do_siga_input(struct subchannel_id schid, unsigned int mask)
* Note: For IQDC unicast queues only the highest priority queue is processed.
*/
static inline int do_siga_output(unsigned long schid, unsigned long mask,
- u32 *bb, unsigned int fc)
+ unsigned int *bb, unsigned int fc)
{
register unsigned long __fc asm("0") = fc;
register unsigned long __schid asm("1") = schid;
@@ -95,8 +95,6 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask,
static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
{
- char dbf_text[15];
-
/* all done or next buffer state different */
if (ccq == 0 || ccq == 32)
return 0;
@@ -104,8 +102,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
if (ccq == 96 || ccq == 97)
return 1;
/* notify devices immediately */
- sprintf(dbf_text, "%d", ccq);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
+ DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
return -EIO;
}
@@ -115,41 +112,45 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
* @state: state of the extracted buffers
* @start: buffer number to start at
* @count: count of buffers to examine
+ * @auto_ack: automatically acknowledge buffers
*
* Returns the number of successfull extracted equal buffer states.
* Stops processing if a state is different from the last buffers state.
*/
static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
- int start, int count)
+ int start, int count, int auto_ack)
{
unsigned int ccq = 0;
int tmp_count = count, tmp_start = start;
int nr = q->nr;
int rc;
- char dbf_text[15];
BUG_ON(!q->irq_ptr->sch_token);
+ qdio_perf_stat_inc(&perf_stats.debug_eqbs_all);
if (!q->is_input_q)
nr += q->irq_ptr->nr_input_qs;
again:
- ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
+ ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count,
+ auto_ack);
rc = qdio_check_ccq(q, ccq);
/* At least one buffer was processed, return and extract the remaining
* buffers later.
*/
- if ((ccq == 96) && (count != tmp_count))
+ if ((ccq == 96) && (count != tmp_count)) {
+ qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete);
return (count - tmp_count);
+ }
+
if (rc == 1) {
- QDIO_DBF_TEXT5(1, trace, "eqAGAIN");
+ DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
goto again;
}
if (rc < 0) {
- QDIO_DBF_TEXT2(1, trace, "eqberr");
- sprintf(dbf_text, "%2x,%2x,%d,%d", count, tmp_count, ccq, nr);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
+ DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
q->handler(q->irq_ptr->cdev,
QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
0, -1, -1, q->irq_ptr->int_parm);
@@ -176,9 +177,12 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
int tmp_count = count, tmp_start = start;
int nr = q->nr;
int rc;
- char dbf_text[15];
+
+ if (!count)
+ return 0;
BUG_ON(!q->irq_ptr->sch_token);
+ qdio_perf_stat_inc(&perf_stats.debug_sqbs_all);
if (!q->is_input_q)
nr += q->irq_ptr->nr_input_qs;
@@ -186,16 +190,13 @@ again:
ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
rc = qdio_check_ccq(q, ccq);
if (rc == 1) {
- QDIO_DBF_TEXT5(1, trace, "sqAGAIN");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
+ qdio_perf_stat_inc(&perf_stats.debug_sqbs_incomplete);
goto again;
}
if (rc < 0) {
- QDIO_DBF_TEXT3(1, trace, "sqberr");
- sprintf(dbf_text, "%2x,%2x", count, tmp_count);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
- sprintf(dbf_text, "%d,%d", ccq, nr);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
-
+ DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
q->handler(q->irq_ptr->cdev,
QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
0, -1, -1, q->irq_ptr->int_parm);
@@ -207,7 +208,8 @@ again:
/* returns number of examined buffers and their common state in *state */
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
- unsigned char *state, unsigned int count)
+ unsigned char *state, unsigned int count,
+ int auto_ack)
{
unsigned char __state = 0;
int i;
@@ -216,7 +218,7 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q);
if (is_qebsm(q))
- return qdio_do_eqbs(q, state, bufnr, count);
+ return qdio_do_eqbs(q, state, bufnr, count, auto_ack);
for (i = 0; i < count; i++) {
if (!__state)
@@ -230,9 +232,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
}
inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
- unsigned char *state)
+ unsigned char *state, int auto_ack)
{
- return get_buf_states(q, bufnr, state, 1);
+ return get_buf_states(q, bufnr, state, 1, auto_ack);
}
/* wrap-around safe setting of slsb states, returns number of changed buffers */
@@ -282,14 +284,12 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output,
if (!need_siga_sync(q))
return 0;
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_sync);
cc = do_siga_sync(q->irq_ptr->schid, output, input);
- if (cc) {
- QDIO_DBF_TEXT4(0, trace, "sigasync");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
- }
+ if (cc)
+ DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc);
return cc;
}
@@ -311,50 +311,37 @@ static inline int qdio_siga_sync_all(struct qdio_q *q)
return qdio_siga_sync(q, ~0U, ~0U);
}
-static inline int qdio_do_siga_output(struct qdio_q *q, unsigned int *busy_bit)
+static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit)
{
- unsigned int fc = 0;
unsigned long schid;
+ unsigned int fc = 0;
+ u64 start_time = 0;
+ int cc;
- if (q->u.out.use_enh_siga) {
+ if (q->u.out.use_enh_siga)
fc = 3;
- }
- if (!is_qebsm(q))
- schid = *((u32 *)&q->irq_ptr->schid);
- else {
+
+ if (is_qebsm(q)) {
schid = q->irq_ptr->sch_token;
fc |= 0x80;
}
- return do_siga_output(schid, q->mask, busy_bit, fc);
-}
-
-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 *));
+ else
+ schid = *((u32 *)&q->irq_ptr->schid);
- qdio_perf_stat_inc(&perf_stats.siga_out);
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);
+ cc = do_siga_output(schid, q->mask, busy_bit, fc);
- if (!start_time)
+ /* hipersocket busy condition */
+ if (*busy_bit) {
+ WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2);
+
+ if (!start_time) {
start_time = get_usecs();
- else if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
+ goto again;
+ }
+ if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
goto again;
}
-
- if (cc == 2 && busy_bit)
- cc |= QDIO_ERROR_SIGA_BUSY;
- if (cc)
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
return cc;
}
@@ -362,14 +349,12 @@ static inline int qdio_siga_input(struct qdio_q *q)
{
int cc;
- QDIO_DBF_TEXT4(0, trace, "sigain");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
-
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_in);
cc = do_siga_input(q->irq_ptr->schid, q->mask);
if (cc)
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
+ DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc);
return cc;
}
@@ -387,35 +372,91 @@ void qdio_sync_after_thinint(struct qdio_q *q)
inline void qdio_stop_polling(struct qdio_q *q)
{
- spin_lock_bh(&q->u.in.lock);
- if (!q->u.in.polling) {
- spin_unlock_bh(&q->u.in.lock);
+ if (!q->u.in.polling)
return;
- }
+
q->u.in.polling = 0;
qdio_perf_stat_inc(&perf_stats.debug_stop_polling);
/* show the card that we are not polling anymore */
- set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
- spin_unlock_bh(&q->u.in.lock);
+ if (is_qebsm(q)) {
+ set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT,
+ q->u.in.ack_count);
+ q->u.in.ack_count = 0;
+ } else
+ set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
}
-static void announce_buffer_error(struct qdio_q *q)
+static void announce_buffer_error(struct qdio_q *q, int count)
{
- char dbf_text[15];
+ q->qdio_error |= QDIO_ERROR_SLSB_STATE;
+
+ /* special handling for no target buffer empty */
+ if ((!q->is_input_q &&
+ (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) {
+ qdio_perf_stat_inc(&perf_stats.outbound_target_full);
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%3d",
+ q->first_to_check);
+ return;
+ }
- if (q->is_input_q)
- QDIO_DBF_TEXT3(1, trace, "inperr");
- else
- QDIO_DBF_TEXT3(0, trace, "outperr");
+ DBF_ERROR("%4x BUF ERROR", SCH_NO(q));
+ DBF_ERROR((q->is_input_q) ? "IN:%2d" : "OUT:%2d", q->nr);
+ DBF_ERROR("FTC:%3d C:%3d", q->first_to_check, count);
+ DBF_ERROR("F14:%2x F15:%2x",
+ q->sbal[q->first_to_check]->element[14].flags & 0xff,
+ q->sbal[q->first_to_check]->element[15].flags & 0xff);
+}
+
+static inline void inbound_primed(struct qdio_q *q, int count)
+{
+ int new;
+
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count);
+
+ /* for QEBSM the ACK was already set by EQBS */
+ if (is_qebsm(q)) {
+ if (!q->u.in.polling) {
+ q->u.in.polling = 1;
+ q->u.in.ack_count = count;
+ q->last_move_ftc = q->first_to_check;
+ return;
+ }
+
+ /* delete the previous ACK's */
+ set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT,
+ q->u.in.ack_count);
+ q->u.in.ack_count = count;
+ q->last_move_ftc = q->first_to_check;
+ return;
+ }
+
+ /*
+ * ACK the newest buffer. The ACK will be removed in qdio_stop_polling
+ * or by the next inbound run.
+ */
+ new = add_buf(q->first_to_check, count - 1);
+ if (q->u.in.polling) {
+ /* reset the previous ACK but first set the new one */
+ set_buf_state(q, new, SLSB_P_INPUT_ACK);
+ set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
+ }
+ else {
+ q->u.in.polling = 1;
+ set_buf_state(q, q->first_to_check, SLSB_P_INPUT_ACK);
+ }
- sprintf(dbf_text, "%x-%x-%x", q->first_to_check,
- q->sbal[q->first_to_check]->element[14].flags,
- q->sbal[q->first_to_check]->element[15].flags);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
- QDIO_DBF_HEX2(1, trace, q->sbal[q->first_to_check], 256);
+ q->last_move_ftc = new;
+ count--;
+ if (!count)
+ return;
- q->qdio_error = QDIO_ERROR_SLSB_STATE;
+ /*
+ * Need to change all PRIMED buffers to NOT_INIT, otherwise
+ * we're loosing initiative in the thinint code.
+ */
+ set_buf_states(q, next_buf(q->first_to_check), SLSB_P_INPUT_NOT_INIT,
+ count);
}
static int get_inbound_buffer_frontier(struct qdio_q *q)
@@ -424,13 +465,6 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
unsigned char state;
/*
- * If we still poll don't update last_move_ftc, keep the
- * previously ACK buffer there.
- */
- if (!q->u.in.polling)
- q->last_move_ftc = q->first_to_check;
-
- /*
* Don't check 128 buffers, as otherwise qdio_inbound_q_moved
* would return 0.
*/
@@ -450,34 +484,13 @@ check_next:
if (q->first_to_check == stop)
goto out;
- count = get_buf_states(q, q->first_to_check, &state, count);
+ count = get_buf_states(q, q->first_to_check, &state, count, 1);
if (!count)
goto out;
switch (state) {
case SLSB_P_INPUT_PRIMED:
- QDIO_DBF_TEXT5(0, trace, "inptprim");
-
- /*
- * Only ACK the first buffer. The ACK will be removed in
- * qdio_stop_polling.
- */
- if (q->u.in.polling)
- state = SLSB_P_INPUT_NOT_INIT;
- else {
- q->u.in.polling = 1;
- state = SLSB_P_INPUT_ACK;
- }
- set_buf_state(q, q->first_to_check, state);
-
- /*
- * Need to change all PRIMED buffers to NOT_INIT, otherwise
- * we're loosing initiative in the thinint code.
- */
- if (count > 1)
- set_buf_states(q, next_buf(q->first_to_check),
- SLSB_P_INPUT_NOT_INIT, count - 1);
-
+ inbound_primed(q, count);
/*
* No siga-sync needed for non-qebsm here, as the inbound queue
* will be synced on the next siga-r, resp.
@@ -487,7 +500,7 @@ check_next:
atomic_sub(count, &q->nr_buf_used);
goto check_next;
case SLSB_P_INPUT_ERROR:
- announce_buffer_error(q);
+ announce_buffer_error(q, count);
/* process the buffer, the upper layer will take care of it */
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
@@ -495,13 +508,12 @@ check_next:
case SLSB_CU_INPUT_EMPTY:
case SLSB_P_INPUT_NOT_INIT:
case SLSB_P_INPUT_ACK:
- QDIO_DBF_TEXT5(0, trace, "inpnipro");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop");
break;
default:
BUG();
}
out:
- QDIO_DBF_HEX4(0, trace, &q->first_to_check, sizeof(int));
return q->first_to_check;
}
@@ -515,8 +527,7 @@ int qdio_inbound_q_moved(struct qdio_q *q)
if (!need_siga_sync(q) && !pci_out_supported(q))
q->u.in.timestamp = get_usecs();
- QDIO_DBF_TEXT4(0, trace, "inhasmvd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in moved");
return 1;
} else
return 0;
@@ -524,10 +535,7 @@ int qdio_inbound_q_moved(struct qdio_q *q)
static int qdio_inbound_q_done(struct qdio_q *q)
{
- unsigned char state;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
+ unsigned char state = 0;
if (!atomic_read(&q->nr_buf_used))
return 1;
@@ -538,7 +546,7 @@ static int qdio_inbound_q_done(struct qdio_q *q)
*/
qdio_siga_sync_q(q);
- get_buf_state(q, q->first_to_check, &state);
+ get_buf_state(q, q->first_to_check, &state, 0);
if (state == SLSB_P_INPUT_PRIMED)
/* we got something to do */
return 0;
@@ -552,20 +560,12 @@ static int qdio_inbound_q_done(struct qdio_q *q)
* has (probably) not moved (see qdio_inbound_processing).
*/
if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "inqisdon");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- sprintf(dbf_text, "pf%02x", q->first_to_check);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%3d",
+ q->first_to_check);
return 1;
} else {
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "inqisntd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- sprintf(dbf_text, "pf%02x", q->first_to_check);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in notd:%3d",
+ q->first_to_check);
return 0;
}
}
@@ -573,9 +573,6 @@ static int qdio_inbound_q_done(struct qdio_q *q)
void qdio_kick_inbound_handler(struct qdio_q *q)
{
int count, start, end;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
qdio_perf_stat_inc(&perf_stats.inbound_handler);
@@ -586,10 +583,7 @@ void qdio_kick_inbound_handler(struct qdio_q *q)
else
count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text, "s=%2xc=%2x", start, count);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count);
if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
return;
@@ -655,14 +649,14 @@ check_next:
if (q->first_to_check == stop)
return q->first_to_check;
- count = get_buf_states(q, q->first_to_check, &state, count);
+ count = get_buf_states(q, q->first_to_check, &state, count, 0);
if (!count)
return q->first_to_check;
switch (state) {
case SLSB_P_OUTPUT_EMPTY:
/* the adapter got it */
- QDIO_DBF_TEXT5(0, trace, "outpempt");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %3d", q->nr, count);
atomic_sub(count, &q->nr_buf_used);
q->first_to_check = add_buf(q->first_to_check, count);
@@ -674,14 +668,14 @@ check_next:
break;
goto check_next;
case SLSB_P_OUTPUT_ERROR:
- announce_buffer_error(q);
+ announce_buffer_error(q, count);
/* process the buffer, the upper layer will take care of it */
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
break;
case SLSB_CU_OUTPUT_PRIMED:
/* the adapter has not fetched the output yet */
- QDIO_DBF_TEXT5(0, trace, "outpprim");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr);
break;
case SLSB_P_OUTPUT_NOT_INIT:
case SLSB_P_OUTPUT_HALTED:
@@ -706,99 +700,48 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
if ((bufnr != q->last_move_ftc) || q->qdio_error) {
q->last_move_ftc = bufnr;
- QDIO_DBF_TEXT4(0, trace, "oqhasmvd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
return 1;
} else
return 0;
}
-/*
- * VM could present us cc=2 and busy bit set on SIGA-write
- * during reconfiguration of their Guest LAN (only in iqdio mode,
- * otherwise qdio is asynchronous and cc=2 and busy bit there will take
- * the queues down immediately).
- *
- * Therefore qdio_siga_output will try for a short time constantly,
- * if such a condition occurs. If it doesn't change, it will
- * increase the busy_siga_counter and save the timestamp, and
- * schedule the queue for later processing. qdio_outbound_processing
- * will check out the counter. If non-zero, it will call qdio_kick_outbound_q
- * as often as the value of the counter. This will attempt further SIGA
- * instructions. For each successful SIGA, the counter is
- * decreased, for failing SIGAs the counter remains the same, after
- * all. After some time of no movement, qdio_kick_outbound_q will
- * finally fail and reflect corresponding error codes to call
- * the upper layer module and have it take the queues down.
- *
- * Note that this is a change from the original HiperSockets design
- * (saying cc=2 and busy bit means take the queues down), but in
- * these days Guest LAN didn't exist... excessive cc=2 with busy bit
- * conditions will still take the queues down, but the threshold is
- * higher due to the Guest LAN environment.
- *
- * Called from outbound tasklet and do_QDIO handler.
- */
static void qdio_kick_outbound_q(struct qdio_q *q)
{
- int rc;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0, trace, "kickoutq");
- QDIO_DBF_HEX5(0, trace, &q, sizeof(void *));
-#endif /* CONFIG_QDIO_DEBUG */
+ unsigned int busy_bit;
+ int cc;
if (!need_siga_out(q))
return;
- rc = qdio_siga_output(q);
- switch (rc) {
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
+ qdio_perf_stat_inc(&perf_stats.siga_out);
+
+ cc = qdio_siga_output(q, &busy_bit);
+ switch (cc) {
case 0:
- /* TODO: improve error handling for CC=0 case */
-#ifdef CONFIG_QDIO_DEBUG
- 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):
- atomic_inc(&q->u.out.busy_siga_counter);
-
- /* if the last siga was successful, save timestamp here */
- if (!q->u.out.timestamp)
- q->u.out.timestamp = get_usecs();
-
- /* if we're in time, don't touch qdio_error */
- if (get_usecs() - q->u.out.timestamp < QDIO_BUSY_BIT_GIVE_UP) {
- tasklet_schedule(&q->tasklet);
- break;
+ case 2:
+ if (busy_bit) {
+ DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
+ q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY;
+ } else {
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d",
+ q->nr);
+ q->qdio_error = cc;
}
- QDIO_DBF_TEXT2(0, trace, "cc2REPRT");
-#ifdef CONFIG_QDIO_DEBUG
- 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 */
- default:
- /* for plain cc=1, 2 or 3 */
- q->qdio_error = rc;
+ break;
+ case 1:
+ case 3:
+ DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
+ q->qdio_error = cc;
+ break;
}
}
static void qdio_kick_outbound_handler(struct qdio_q *q)
{
int start, end, count;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
start = q->first_to_kick;
end = q->last_move_ftc;
@@ -807,13 +750,8 @@ static void qdio_kick_outbound_handler(struct qdio_q *q)
else
count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "kickouth");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
-
- sprintf(dbf_text, "s=%2xc=%2x", start, count);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickouth: %1d", q->nr);
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "s:%3d c:%3d", start, count);
if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
return;
@@ -828,22 +766,18 @@ static void qdio_kick_outbound_handler(struct qdio_q *q)
static void __qdio_outbound_processing(struct qdio_q *q)
{
- int siga_attempts;
+ unsigned long flags;
qdio_perf_stat_inc(&perf_stats.tasklet_outbound);
-
- /* see comment in qdio_kick_outbound_q */
- siga_attempts = atomic_read(&q->u.out.busy_siga_counter);
- while (siga_attempts--) {
- atomic_dec(&q->u.out.busy_siga_counter);
- qdio_kick_outbound_q(q);
- }
+ spin_lock_irqsave(&q->lock, flags);
BUG_ON(atomic_read(&q->nr_buf_used) < 0);
if (qdio_outbound_q_moved(q))
qdio_kick_outbound_handler(q);
+ spin_unlock_irqrestore(&q->lock, flags);
+
if (queue_type(q) == QDIO_ZFCP_QFMT) {
if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
tasklet_schedule(&q->tasklet);
@@ -908,27 +842,18 @@ void qdio_check_outbound_after_thinint(struct qdio_q *q)
static inline void qdio_set_state(struct qdio_irq *irq_ptr,
enum qdio_irq_states state)
{
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0, trace, "newstate");
- sprintf(dbf_text, "%4x%4x", irq_ptr->schid.sch_no, state);
- QDIO_DBF_TEXT5(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "newstate: %1d", state);
irq_ptr->state = state;
mb();
}
-static void qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb)
+static void qdio_irq_check_sense(struct qdio_irq *irq_ptr, struct irb *irb)
{
- char dbf_text[15];
-
if (irb->esw.esw0.erw.cons) {
- sprintf(dbf_text, "sens%4x", schid.sch_no);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
- QDIO_DBF_HEX0(0, trace, irb, 64);
- QDIO_DBF_HEX0(0, trace, irb->ecw, 64);
+ DBF_ERROR("%4x sense:", irq_ptr->schid.sch_no);
+ DBF_ERROR_HEX(irb, 64);
+ DBF_ERROR_HEX(irb->ecw, 64);
}
}
@@ -962,14 +887,10 @@ static void qdio_handle_activate_check(struct ccw_device *cdev,
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
struct qdio_q *q;
- char dbf_text[15];
- QDIO_DBF_TEXT2(1, trace, "ick2");
- 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));
- QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int));
+ DBF_ERROR("%4x ACT CHECK", irq_ptr->schid.sch_no);
+ DBF_ERROR("intp :%lx", intparm);
+ DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
if (irq_ptr->nr_input_qs) {
q = irq_ptr->input_qs[0];
@@ -1022,28 +943,29 @@ static void qdio_int_error(struct ccw_device *cdev)
}
static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat,
- int dstat)
+ int dstat)
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
if (cstat || (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
- QDIO_DBF_TEXT2(1, setup, "eq:ckcon");
+ DBF_ERROR("EQ:ck con");
goto error;
}
if (!(dstat & DEV_STAT_DEV_END)) {
- QDIO_DBF_TEXT2(1, setup, "eq:no de");
+ DBF_ERROR("EQ:no dev");
goto error;
}
if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
- QDIO_DBF_TEXT2(1, setup, "eq:badio");
+ DBF_ERROR("EQ: bad io");
goto error;
}
return 0;
error:
- QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int));
- QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int));
+ DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no);
+ DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
+
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
return 1;
}
@@ -1052,12 +974,8 @@ static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
int dstat)
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
- char dbf_text[15];
-
- sprintf(dbf_text, "qehi%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_TEXT0(0, trace, dbf_text);
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
if (!qdio_establish_check_errors(cdev, cstat, dstat))
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
}
@@ -1068,25 +986,21 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int cstat, dstat;
- char dbf_text[15];
qdio_perf_stat_inc(&perf_stats.qdio_int);
if (!intparm || !irq_ptr) {
- sprintf(dbf_text, "qihd%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("qint:%4x", cdev->private->schid.sch_no);
return;
}
if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) {
case -EIO:
- sprintf(dbf_text, "ierr%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no);
return;
case -ETIMEDOUT:
- sprintf(dbf_text, "qtoh%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no);
qdio_int_error(cdev);
return;
default:
@@ -1094,7 +1008,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
return;
}
}
- qdio_irq_check_sense(irq_ptr->schid, irb);
+ qdio_irq_check_sense(irq_ptr, irb);
cstat = irb->scsw.cmd.cstat;
dstat = irb->scsw.cmd.dstat;
@@ -1129,23 +1043,20 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
/**
* qdio_get_ssqd_desc - get qdio subchannel description
* @cdev: ccw device to get description for
+ * @data: where to store the ssqd
*
- * Returns a pointer to the saved qdio subchannel description,
- * or NULL for not setup qdio devices.
+ * Returns 0 or an error code. The results of the chsc are stored in the
+ * specified structure.
*/
-struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev)
+int qdio_get_ssqd_desc(struct ccw_device *cdev,
+ struct qdio_ssqd_desc *data)
{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- 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)
- return NULL;
+ if (!cdev || !cdev->private)
+ return -EINVAL;
- return &irq_ptr->ssqd_desc;
+ DBF_EVENT("get ssqd:%4x", cdev->private->schid.sch_no);
+ return qdio_setup_get_ssqd(NULL, &cdev->private->schid, data);
}
EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc);
@@ -1159,14 +1070,9 @@ EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc);
*/
int qdio_cleanup(struct ccw_device *cdev, int how)
{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
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;
@@ -1199,18 +1105,15 @@ static void qdio_shutdown_queues(struct ccw_device *cdev)
*/
int qdio_shutdown(struct ccw_device *cdev, int how)
{
- struct qdio_irq *irq_ptr;
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int rc;
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;
+ DBF_EVENT("qshutdown:%4x", cdev->private->schid.sch_no);
+
mutex_lock(&irq_ptr->setup_mutex);
/*
* Subchannel was already shot down. We cannot prevent being called
@@ -1234,10 +1137,8 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
/* default behaviour is halt */
rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
if (rc) {
- sprintf(dbf_text, "sher%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- sprintf(dbf_text, "rc=%d", rc);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_ERROR("%4x SHUTD ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4d", rc);
goto no_cleanup;
}
@@ -1271,17 +1172,18 @@ EXPORT_SYMBOL_GPL(qdio_shutdown);
*/
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);
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
- irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
+ DBF_EVENT("qfree:%4x", cdev->private->schid.sch_no);
mutex_lock(&irq_ptr->setup_mutex);
+
+ if (irq_ptr->debug_area != NULL) {
+ debug_unregister(irq_ptr->debug_area);
+ irq_ptr->debug_area = NULL;
+ }
cdev->private->qdio_data = NULL;
mutex_unlock(&irq_ptr->setup_mutex);
@@ -1300,10 +1202,6 @@ EXPORT_SYMBOL_GPL(qdio_free);
int qdio_initialize(struct qdio_initialize *init_data)
{
int rc;
- char dbf_text[15];
-
- sprintf(dbf_text, "qini%4x", init_data->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
rc = qdio_allocate(init_data);
if (rc)
@@ -1323,10 +1221,8 @@ EXPORT_SYMBOL_GPL(qdio_initialize);
int qdio_allocate(struct qdio_initialize *init_data)
{
struct qdio_irq *irq_ptr;
- char dbf_text[15];
- sprintf(dbf_text, "qalc%4x", init_data->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qallocate:%4x", init_data->cdev->private->schid.sch_no);
if ((init_data->no_input_qs && !init_data->input_handler) ||
(init_data->no_output_qs && !init_data->output_handler))
@@ -1340,16 +1236,13 @@ int qdio_allocate(struct qdio_initialize *init_data)
(!init_data->output_sbal_addr_array))
return -EINVAL;
- qdio_allocate_do_dbf(init_data);
-
/* irq_ptr must be in GFP_DMA since it contains ccw1.cda */
irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!irq_ptr)
goto out_err;
- QDIO_DBF_TEXT0(0, setup, "irq_ptr:");
- QDIO_DBF_HEX0(0, setup, &irq_ptr, sizeof(void *));
mutex_init(&irq_ptr->setup_mutex);
+ qdio_allocate_dbf(init_data, irq_ptr);
/*
* Allocate a page for the chsc calls in qdio_establish.
@@ -1367,9 +1260,6 @@ int qdio_allocate(struct qdio_initialize *init_data)
goto out_rel;
WARN_ON((unsigned long)irq_ptr->qdr & 0xfff);
- QDIO_DBF_TEXT0(0, setup, "qdr:");
- QDIO_DBF_HEX0(0, setup, &irq_ptr->qdr, sizeof(void *));
-
if (qdio_allocate_qs(irq_ptr, init_data->no_input_qs,
init_data->no_output_qs))
goto out_rel;
@@ -1390,14 +1280,12 @@ EXPORT_SYMBOL_GPL(qdio_allocate);
*/
int qdio_establish(struct qdio_initialize *init_data)
{
- char dbf_text[20];
struct qdio_irq *irq_ptr;
struct ccw_device *cdev = init_data->cdev;
unsigned long saveflags;
int rc;
- sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qestablish:%4x", cdev->private->schid.sch_no);
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
@@ -1427,10 +1315,8 @@ int qdio_establish(struct qdio_initialize *init_data)
rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ESTABLISH, 0, 0);
if (rc) {
- sprintf(dbf_text, "eq:io%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
- sprintf(dbf_text, "eq:rc%4x", rc);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x est IO ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4x", rc);
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags);
@@ -1451,10 +1337,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);
+ DBF_EVENT("qDmmwc:%2x", irq_ptr->ssqd_desc.mmwc);
+ DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac);
/* qebsm is now setup if available, initialize buffer states */
qdio_init_buf_states(irq_ptr);
@@ -1475,10 +1359,8 @@ int qdio_activate(struct ccw_device *cdev)
struct qdio_irq *irq_ptr;
int rc;
unsigned long saveflags;
- char dbf_text[20];
- sprintf(dbf_text, "qact%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qactivate:%4x", cdev->private->schid.sch_no);
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
@@ -1504,10 +1386,8 @@ int qdio_activate(struct ccw_device *cdev)
rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ACTIVATE,
0, DOIO_DENY_PREFETCH);
if (rc) {
- sprintf(dbf_text, "aq:io%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
- sprintf(dbf_text, "aq:rc%4x", rc);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x act IO ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4x", rc);
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags);
@@ -1565,23 +1445,38 @@ static inline int buf_in_between(int bufnr, int start, int count)
static void handle_inbound(struct qdio_q *q, unsigned int callflags,
int bufnr, int count)
{
- unsigned long flags;
- int used, rc;
+ int used, cc, diff;
- /*
- * do_QDIO could run in parallel with the queue tasklet so the
- * upper-layer programm could empty the ACK'ed buffer here.
- * If that happens we must clear the polling flag, otherwise
- * qdio_stop_polling() could set the buffer to NOT_INIT after
- * it was set to EMPTY which would kill us.
- */
- spin_lock_irqsave(&q->u.in.lock, flags);
- if (q->u.in.polling)
- if (buf_in_between(q->last_move_ftc, bufnr, count))
+ if (!q->u.in.polling)
+ goto set;
+
+ /* protect against stop polling setting an ACK for an emptied slsb */
+ if (count == QDIO_MAX_BUFFERS_PER_Q) {
+ /* overwriting everything, just delete polling status */
+ q->u.in.polling = 0;
+ q->u.in.ack_count = 0;
+ goto set;
+ } else if (buf_in_between(q->last_move_ftc, bufnr, count)) {
+ if (is_qebsm(q)) {
+ /* partial overwrite, just update last_move_ftc */
+ diff = add_buf(bufnr, count);
+ diff = sub_buf(diff, q->last_move_ftc);
+ q->u.in.ack_count -= diff;
+ if (q->u.in.ack_count <= 0) {
+ q->u.in.polling = 0;
+ q->u.in.ack_count = 0;
+ /* TODO: must we set last_move_ftc to something meaningful? */
+ goto set;
+ }
+ q->last_move_ftc = add_buf(q->last_move_ftc, diff);
+ }
+ else
+ /* the only ACK will be deleted, so stop polling */
q->u.in.polling = 0;
+ }
+set:
count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count);
- spin_unlock_irqrestore(&q->u.in.lock, flags);
used = atomic_add_return(count, &q->nr_buf_used) - count;
BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q);
@@ -1591,9 +1486,9 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags,
return;
if (need_siga_in(q)) {
- rc = qdio_siga_input(q);
- if (rc)
- q->qdio_error = rc;
+ cc = qdio_siga_input(q);
+ if (cc)
+ q->qdio_error = cc;
}
}
@@ -1640,6 +1535,10 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
while (count--)
qdio_kick_outbound_q(q);
}
+
+ /* report CC=2 conditions synchronously */
+ if (q->qdio_error)
+ __qdio_outbound_processing(q);
goto out;
}
@@ -1649,11 +1548,11 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
}
/* try to fast requeue buffers */
- get_buf_state(q, prev_buf(bufnr), &state);
+ get_buf_state(q, prev_buf(bufnr), &state, 0);
if (state != SLSB_CU_OUTPUT_PRIMED)
qdio_kick_outbound_q(q);
else {
- QDIO_DBF_TEXT5(0, trace, "fast-req");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req");
qdio_perf_stat_inc(&perf_stats.fast_requeue);
}
out:
@@ -1673,12 +1572,6 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
int q_nr, int bufnr, int count)
{
struct qdio_irq *irq_ptr;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[20];
-
- sprintf(dbf_text, "doQD%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) ||
(count > QDIO_MAX_BUFFERS_PER_Q) ||
@@ -1692,33 +1585,24 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
if (!irq_ptr)
return -ENODEV;
-#ifdef CONFIG_QDIO_DEBUG
if (callflags & QDIO_FLAG_SYNC_INPUT)
- QDIO_DBF_HEX3(0, trace, &irq_ptr->input_qs[q_nr],
- sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO input");
else
- QDIO_DBF_HEX3(0, trace, &irq_ptr->output_qs[q_nr],
- sizeof(void *));
-
- sprintf(dbf_text, "flag%04x", callflags);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
- sprintf(dbf_text, "qi%02xct%02x", bufnr, count);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO output");
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "q:%1d flag:%4x", q_nr, callflags);
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "buf:%2d cnt:%3d", bufnr, count);
if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)
return -EBUSY;
if (callflags & QDIO_FLAG_SYNC_INPUT)
- handle_inbound(irq_ptr->input_qs[q_nr],
- callflags, bufnr, count);
+ handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr,
+ count);
else if (callflags & QDIO_FLAG_SYNC_OUTPUT)
- handle_outbound(irq_ptr->output_qs[q_nr],
- callflags, bufnr, count);
- else {
- QDIO_DBF_TEXT3(1, trace, "doQD:inv");
+ handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr,
+ count);
+ else
return -EINVAL;
- }
return 0;
}
EXPORT_SYMBOL_GPL(do_QDIO);
diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c
index ec5c4a414235..136d0f0b1e93 100644
--- a/drivers/s390/cio/qdio_perf.c
+++ b/drivers/s390/cio/qdio_perf.c
@@ -74,12 +74,20 @@ static int qdio_perf_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\n");
seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n",
(long)atomic_long_read(&perf_stats.fast_requeue));
+ seq_printf(m, "Number of outbound target full condition\t: %li\n",
+ (long)atomic_long_read(&perf_stats.outbound_target_full));
seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n",
(long)atomic_long_read(&perf_stats.debug_tl_out_timer));
seq_printf(m, "Number of stop polling calls\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.debug_stop_polling));
seq_printf(m, "AI inbound tasklet loops after stop polling\t: %li\n",
(long)atomic_long_read(&perf_stats.thinint_inbound_loop2));
+ seq_printf(m, "QEBSM EQBS total/incomplete\t\t\t: %li/%li\n",
+ (long)atomic_long_read(&perf_stats.debug_eqbs_all),
+ (long)atomic_long_read(&perf_stats.debug_eqbs_incomplete));
+ seq_printf(m, "QEBSM SQBS total/incomplete\t\t\t: %li/%li\n",
+ (long)atomic_long_read(&perf_stats.debug_sqbs_all),
+ (long)atomic_long_read(&perf_stats.debug_sqbs_incomplete));
seq_printf(m, "\n");
return 0;
}
diff --git a/drivers/s390/cio/qdio_perf.h b/drivers/s390/cio/qdio_perf.h
index 5c406a8b7387..7821ac4fa517 100644
--- a/drivers/s390/cio/qdio_perf.h
+++ b/drivers/s390/cio/qdio_perf.h
@@ -36,10 +36,15 @@ struct qdio_perf_stats {
atomic_long_t inbound_handler;
atomic_long_t outbound_handler;
atomic_long_t fast_requeue;
+ atomic_long_t outbound_target_full;
/* for debugging */
atomic_long_t debug_tl_out_timer;
atomic_long_t debug_stop_polling;
+ atomic_long_t debug_eqbs_all;
+ atomic_long_t debug_eqbs_incomplete;
+ atomic_long_t debug_sqbs_all;
+ atomic_long_t debug_sqbs_incomplete;
};
extern struct qdio_perf_stats perf_stats;
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index a0b6b46e7466..c08356b95bf5 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -117,17 +117,16 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
q->mask = 1 << (31 - i);
q->nr = i;
q->handler = handler;
+ spin_lock_init(&q->lock);
}
static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
- void **sbals_array, char *dbf_text, int i)
+ void **sbals_array, int i)
{
struct qdio_q *prev;
int j;
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &q, sizeof(void *));
-
+ DBF_HEX(&q, sizeof(void *));
q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2);
/* fill in sbal */
@@ -150,31 +149,26 @@ static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
q->sl->element[j].sbal = (unsigned long)q->sbal[j];
- QDIO_DBF_TEXT2(0, setup, "sl-sb-b0");
- QDIO_DBF_HEX2(0, setup, q->sl, sizeof(void *));
- QDIO_DBF_HEX2(0, setup, &q->slsb, sizeof(void *));
- QDIO_DBF_HEX2(0, setup, q->sbal, sizeof(void *));
+ DBF_EVENT("sl-slsb-sbal");
+ DBF_HEX(q->sl, sizeof(void *));
+ DBF_HEX(&q->slsb, sizeof(void *));
+ DBF_HEX(q->sbal, sizeof(void *));
}
static void setup_queues(struct qdio_irq *irq_ptr,
struct qdio_initialize *qdio_init)
{
- char dbf_text[20];
struct qdio_q *q;
void **input_sbal_array = qdio_init->input_sbal_addr_array;
void **output_sbal_array = qdio_init->output_sbal_addr_array;
int i;
- 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) {
- sprintf(dbf_text, "in-q%4x", i);
+ DBF_EVENT("in-q:%1d", i);
setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i);
q->is_input_q = 1;
- spin_lock_init(&q->u.in.lock);
- setup_storage_lists(q, irq_ptr, input_sbal_array, dbf_text, i);
+ setup_storage_lists(q, irq_ptr, input_sbal_array, i);
input_sbal_array += QDIO_MAX_BUFFERS_PER_Q;
if (is_thinint_irq(irq_ptr))
@@ -186,12 +180,11 @@ static void setup_queues(struct qdio_irq *irq_ptr,
}
for_each_output_queue(irq_ptr, q, i) {
- sprintf(dbf_text, "outq%4x", i);
+ DBF_EVENT("outq:%1d", i);
setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i);
q->is_input_q = 0;
- setup_storage_lists(q, irq_ptr, output_sbal_array,
- dbf_text, i);
+ setup_storage_lists(q, irq_ptr, output_sbal_array, i);
output_sbal_array += QDIO_MAX_BUFFERS_PER_Q;
tasklet_init(&q->tasklet, qdio_outbound_processing,
@@ -222,8 +215,6 @@ static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac)
static void check_and_setup_qebsm(struct qdio_irq *irq_ptr,
unsigned char qdioac, unsigned long token)
{
- char dbf_text[15];
-
if (!(irq_ptr->qib.rflags & QIB_RFLAGS_ENABLE_QEBSM))
goto no_qebsm;
if (!(qdioac & AC1_SC_QEBSM_AVAILABLE) ||
@@ -232,33 +223,41 @@ static void check_and_setup_qebsm(struct qdio_irq *irq_ptr,
irq_ptr->sch_token = token;
- QDIO_DBF_TEXT0(0, setup, "V=V:1");
- sprintf(dbf_text, "%8lx", irq_ptr->sch_token);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("V=V:1");
+ DBF_EVENT("%8lx", irq_ptr->sch_token);
return;
no_qebsm:
irq_ptr->sch_token = 0;
irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
- QDIO_DBF_TEXT0(0, setup, "noV=V");
+ DBF_EVENT("noV=V");
}
-static int __get_ssqd_info(struct qdio_irq *irq_ptr)
+/*
+ * If there is a qdio_irq we use the chsc_page and store the information
+ * in the qdio_irq, otherwise we copy it to the specified structure.
+ */
+int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
+ struct subchannel_id *schid,
+ struct qdio_ssqd_desc *data)
{
struct chsc_ssqd_area *ssqd;
int rc;
- QDIO_DBF_TEXT0(0, setup, "getssqd");
- ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ DBF_EVENT("getssqd:%4x", schid->sch_no);
+ if (irq_ptr != NULL)
+ ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ else
+ ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
memset(ssqd, 0, PAGE_SIZE);
ssqd->request = (struct chsc_header) {
.length = 0x0010,
.code = 0x0024,
};
- ssqd->first_sch = irq_ptr->schid.sch_no;
- ssqd->last_sch = irq_ptr->schid.sch_no;
- ssqd->ssid = irq_ptr->schid.ssid;
+ ssqd->first_sch = schid->sch_no;
+ ssqd->last_sch = schid->sch_no;
+ ssqd->ssid = schid->ssid;
if (chsc(ssqd))
return -EIO;
@@ -268,27 +267,29 @@ static int __get_ssqd_info(struct qdio_irq *irq_ptr)
if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) ||
!(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) ||
- (ssqd->qdio_ssqd.sch != irq_ptr->schid.sch_no))
+ (ssqd->qdio_ssqd.sch != schid->sch_no))
return -EINVAL;
- memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd,
- sizeof(struct qdio_ssqd_desc));
+ if (irq_ptr != NULL)
+ memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd,
+ sizeof(struct qdio_ssqd_desc));
+ else {
+ memcpy(data, &ssqd->qdio_ssqd,
+ sizeof(struct qdio_ssqd_desc));
+ free_page((unsigned long)ssqd);
+ }
return 0;
}
void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
{
unsigned char qdioac;
- char dbf_text[15];
int rc;
- rc = __get_ssqd_info(irq_ptr);
+ rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, NULL);
if (rc) {
- QDIO_DBF_TEXT2(0, setup, "ssqdasig");
- 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);
+ DBF_ERROR("%4x ssqd ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%x", rc);
/* all flags set, worst case */
qdioac = AC1_SIGA_INPUT_NEEDED | AC1_SIGA_OUTPUT_NEEDED |
AC1_SIGA_SYNC_NEEDED;
@@ -297,9 +298,7 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
check_and_setup_qebsm(irq_ptr, qdioac, irq_ptr->ssqd_desc.sch_token);
process_ac_flags(irq_ptr, qdioac);
-
- sprintf(dbf_text, "qdioac%2x", qdioac);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
+ DBF_EVENT("qdioac:%4x", qdioac);
}
void qdio_release_memory(struct qdio_irq *irq_ptr)
@@ -419,7 +418,7 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
/* get qdio commands */
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE);
if (!ciw) {
- QDIO_DBF_TEXT2(1, setup, "no eq");
+ DBF_ERROR("%4x NO EQ", irq_ptr->schid.sch_no);
rc = -EINVAL;
goto out_err;
}
@@ -427,7 +426,7 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE);
if (!ciw) {
- QDIO_DBF_TEXT2(1, setup, "no aq");
+ DBF_ERROR("%4x NO AQ", irq_ptr->schid.sch_no);
rc = -EINVAL;
goto out_err;
}
@@ -447,56 +446,38 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
{
char s[80];
- sprintf(s, "qdio: %s ", dev_name(&cdev->dev));
- switch (irq_ptr->qib.qfmt) {
- case QDIO_QETH_QFMT:
- sprintf(s + strlen(s), "OSA ");
- break;
- case QDIO_ZFCP_QFMT:
- sprintf(s + strlen(s), "ZFCP ");
- break;
- case QDIO_IQDIO_QFMT:
- sprintf(s + strlen(s), "HS ");
- break;
- }
- 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");
+ snprintf(s, 80, "qdio: %s %s on SC %x using "
+ "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s%s\n",
+ dev_name(&cdev->dev),
+ (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" :
+ ((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"),
+ irq_ptr->schid.sch_no,
+ is_thinint_irq(irq_ptr),
+ (irq_ptr->sch_token) ? 1 : 0,
+ (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0,
+ css_general_characteristics.aif_tdd,
+ (irq_ptr->siga_flag.input) ? "R" : " ",
+ (irq_ptr->siga_flag.output) ? "W" : " ",
+ (irq_ptr->siga_flag.sync) ? "S" : " ",
+ (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ",
+ (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ",
+ (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " ");
printk(KERN_INFO "%s", s);
}
int __init qdio_setup_init(void)
{
- char dbf_text[15];
-
qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q),
256, 0, NULL);
if (!qdio_q_cache)
return -ENOMEM;
/* Check for OSA/FCP thin interrupts (bit 67). */
- sprintf(dbf_text, "thini%1x",
- (css_general_characteristics.aif_osa) ? 1 : 0);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("thinint:%1d",
+ (css_general_characteristics.aif_osa) ? 1 : 0);
/* Check for QEBSM support in general (bit 58). */
- sprintf(dbf_text, "cssQBS:%1x",
- (qebsm_possible()) ? 1 : 0);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("cssQEBSM:%1d", (qebsm_possible()) ? 1 : 0);
return 0;
}
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index ea7f61400267..8e90e147b746 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -125,13 +125,13 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
static inline int tiqdio_inbound_q_done(struct qdio_q *q)
{
- unsigned char state;
+ unsigned char state = 0;
if (!atomic_read(&q->nr_buf_used))
return 1;
qdio_siga_sync_q(q);
- get_buf_state(q, q->first_to_check, &state);
+ get_buf_state(q, q->first_to_check, &state, 0);
if (state == SLSB_P_INPUT_PRIMED)
/* more work coming */
@@ -258,8 +258,6 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data)
static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
{
struct scssc_area *scssc_area;
- char dbf_text[15];
- void *ptr;
int rc;
scssc_area = (struct scssc_area *)irq_ptr->chsc_page;
@@ -294,19 +292,15 @@ static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
rc = chsc_error_from_response(scssc_area->response.code);
if (rc) {
- sprintf(dbf_text, "sidR%4x", scssc_area->response.code);
- QDIO_DBF_TEXT1(0, trace, dbf_text);
- QDIO_DBF_TEXT1(0, setup, dbf_text);
- ptr = &scssc_area->response;
- QDIO_DBF_HEX2(1, setup, &ptr, QDIO_DBF_SETUP_LEN);
+ DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
+ scssc_area->response.code);
+ DBF_ERROR_HEX(&scssc_area->response, sizeof(void *));
return rc;
}
- QDIO_DBF_TEXT2(0, setup, "setscind");
- QDIO_DBF_HEX2(0, setup, &scssc_area->summary_indicator_addr,
- sizeof(unsigned long));
- QDIO_DBF_HEX2(0, setup, &scssc_area->subchannel_indicator_addr,
- sizeof(unsigned long));
+ DBF_EVENT("setscind");
+ DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long));
+ DBF_HEX(&scssc_area->subchannel_indicator_addr, sizeof(unsigned long));
return 0;
}
@@ -327,14 +321,11 @@ void tiqdio_free_memory(void)
int __init tiqdio_register_thinints(void)
{
- char dbf_text[20];
-
isc_register(QDIO_AIRQ_ISC);
tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler,
NULL, QDIO_AIRQ_ISC);
if (IS_ERR(tiqdio_alsi)) {
- sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_alsi));
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi));
tiqdio_alsi = NULL;
isc_unregister(QDIO_AIRQ_ISC);
return -ENOMEM;
@@ -360,7 +351,7 @@ void qdio_setup_thinint(struct qdio_irq *irq_ptr)
if (!is_thinint_irq(irq_ptr))
return;
irq_ptr->dsci = get_indicator();
- QDIO_DBF_HEX1(0, setup, &irq_ptr->dsci, sizeof(void *));
+ DBF_HEX(&irq_ptr->dsci, sizeof(void *));
}
void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)