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