From 46b05d2617c8efd8ec6b19acd2c95541a0118c13 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Wed, 21 Feb 2007 10:55:21 +0100 Subject: [S390] New header file ipl.h Setup.h has been misused for ipl related stuff in the past. We now move everything, which has to do with ipl and reipl to a new header file named "ipl.h". Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cio.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index b3a56dc5f68..e4471e6ee5d 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "airq.h" #include "cio.h" #include "css.h" -- cgit From 615b04b301fd4e1b7680a498b873c4ae9baad92f Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 21 Feb 2007 10:55:37 +0100 Subject: [S390] nss: Free unused memory in kernel image. With CONFIG_SHARED_KERNEL the kernel text segment that might be in a read only memory sections starts at 1MB. Memory between 0x12000 and 0x100000 is unused then. Free this, so we have appr. an extra MB of memory available. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index e4471e6ee5d..9cb129ab5be 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -1048,7 +1048,7 @@ void reipl_ccw_dev(struct ccw_dev_id *devid) do_reipl_asm(*((__u32*)&schid)); } -extern struct schib ipl_schib; +static struct schib __initdata ipl_schib; /* * ipl_save_parameters gets called very early. It is not allowed to access -- cgit From ee04bbccdeb11bdbc54015be8dca30a0deeca5e4 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 5 Mar 2007 23:35:56 +0100 Subject: [S390] cio: Fix locking when calling notify function. Make sure we hold the device lock when we modify the ccw device structure but always call the notify function without the lock held. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_fsm.c | 67 ++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 20 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 51238e7555b..8baa9cd3794 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -334,20 +334,29 @@ ccw_device_oper_notify(struct work_struct *work) struct ccw_device *cdev; struct subchannel *sch; int ret; + unsigned long flags; priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; + spin_lock_irqsave(cdev->ccwlock, flags); sch = to_subchannel(cdev->dev.parent); - ret = (sch->driver && sch->driver->notify) ? - sch->driver->notify(&sch->dev, CIO_OPER) : 0; - if (!ret) - /* Driver doesn't want device back. */ - ccw_device_do_unreg_rereg(work); - else { + if (sch->driver && sch->driver->notify) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + ret = sch->driver->notify(&sch->dev, CIO_OPER); + spin_lock_irqsave(cdev->ccwlock, flags); + } else + ret = 0; + if (ret) { /* Reenable channel measurements, if needed. */ + spin_unlock_irqrestore(cdev->ccwlock, flags); cmf_reenable(cdev); + spin_lock_irqsave(cdev->ccwlock, flags); wake_up(&cdev->private->wait_q); } + spin_unlock_irqrestore(cdev->ccwlock, flags); + if (!ret) + /* Driver doesn't want device back. */ + ccw_device_do_unreg_rereg(work); } /* @@ -534,15 +543,21 @@ ccw_device_nopath_notify(struct work_struct *work) struct ccw_device *cdev; struct subchannel *sch; int ret; + unsigned long flags; priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; + spin_lock_irqsave(cdev->ccwlock, flags); sch = to_subchannel(cdev->dev.parent); /* Extra sanity. */ if (sch->lpm) - return; - ret = (sch->driver && sch->driver->notify) ? - sch->driver->notify(&sch->dev, CIO_NO_PATH) : 0; + goto out_unlock; + if (sch->driver && sch->driver->notify) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + ret = sch->driver->notify(&sch->dev, CIO_NO_PATH); + spin_lock_irqsave(cdev->ccwlock, flags); + } else + ret = 0; if (!ret) { if (get_device(&sch->dev)) { /* Driver doesn't want to keep device. */ @@ -562,6 +577,8 @@ ccw_device_nopath_notify(struct work_struct *work) cdev->private->state = DEV_STATE_DISCONNECTED; wake_up(&cdev->private->wait_q); } +out_unlock: + spin_unlock_irqrestore(cdev->ccwlock, flags); } void @@ -607,10 +624,13 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) default: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, &cdev->private->kick_work); - ccw_device_done(cdev, DEV_STATE_NOT_OPER); + if (cdev->online) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify); + queue_work(ccw_device_notify_work, + &cdev->private->kick_work); + } else + ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } } @@ -756,15 +776,22 @@ static void ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event) { struct subchannel *sch; + int ret; sch = to_subchannel(cdev->dev.parent); - if (sch->driver->notify && - sch->driver->notify(&sch->dev, sch->lpm ? CIO_GONE : CIO_NO_PATH)) { - ccw_device_set_timeout(cdev, 0); - cdev->private->flags.fake_irb = 0; - cdev->private->state = DEV_STATE_DISCONNECTED; - wake_up(&cdev->private->wait_q); - return; + if (sch->driver->notify) { + spin_unlock_irq(cdev->ccwlock); + ret = sch->driver->notify(&sch->dev, + sch->lpm ? CIO_GONE : CIO_NO_PATH); + spin_lock_irq(cdev->ccwlock); + } else + ret = 0; + if (ret) { + ccw_device_set_timeout(cdev, 0); + cdev->private->flags.fake_irb = 0; + cdev->private->state = DEV_STATE_DISCONNECTED; + wake_up(&cdev->private->wait_q); + return; } cdev->private->state = DEV_STATE_NOT_OPER; cio_disable_subchannel(sch); -- cgit From 7c8427c3fa1b2e77c5bd8cf219c1d55dccd0f167 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 5 Mar 2007 23:35:59 +0100 Subject: [S390] cio: Use path verification to check for path state. After I/O has been killed by the common I/O layer, trigger path verification which will queue cio_device_nopath_notify itself if it finds a device to be without paths. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_fsm.c | 46 ++++++------------------------------------- 1 file changed, 6 insertions(+), 40 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 8baa9cd3794..5172060acdb 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -996,18 +996,12 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); ccw_device_set_timeout(cdev, 0); + /* Start delayed path verification. */ + ccw_device_online_verify(cdev, 0); /* OK, i/o is dead now. Call interrupt handler. */ - cdev->private->state = DEV_STATE_ONLINE; if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); - if (!sch->lpm) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, &cdev->private->kick_work); - } else if (cdev->private->flags.doverify) - /* Start delayed path verification. */ - ccw_device_online_verify(cdev, 0); } static void @@ -1020,21 +1014,8 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_set_timeout(cdev, 3*HZ); return; } - if (ret == -ENODEV) { - struct subchannel *sch; - - sch = to_subchannel(cdev->dev.parent); - if (!sch->lpm) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, - &cdev->private->kick_work); - } else - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - return; - } - //FIXME: Can we get here? - cdev->private->state = DEV_STATE_ONLINE; + /* Start delayed path verification. */ + ccw_device_online_verify(cdev, 0); if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); @@ -1052,26 +1033,11 @@ void device_kill_io(struct subchannel *sch) cdev->private->state = DEV_STATE_TIMEOUT_KILL; return; } - if (ret == -ENODEV) { - if (!sch->lpm) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, - &cdev->private->kick_work); - } else - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - return; - } + /* Start delayed path verification. */ + ccw_device_online_verify(cdev, 0); if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); - if (!sch->lpm) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, &cdev->private->kick_work); - } else - /* Start delayed path verification. */ - ccw_device_online_verify(cdev, 0); } static void -- cgit From 2470b648e17e0216922bb78c7f05b4668402459a Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 5 Mar 2007 23:36:02 +0100 Subject: [S390] cio: Call cancel_halt_clear even when actl == 0. The subchannel may just be status pending, even with actl == 0. We must go through the cancel_halt_clear procedure to put the subchannel into a defined state. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_fsm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 5172060acdb..089a3ddd626 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -144,8 +144,8 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) ret = stsch(sch->schid, &sch->schib); if (ret || !sch->schib.pmcw.dnv) return -ENODEV; - if (!sch->schib.pmcw.ena || sch->schib.scsw.actl == 0) - /* Not operational or no activity -> done. */ + if (!sch->schib.pmcw.ena) + /* Not operational -> done. */ return 0; /* Stage 1: cancel io. */ if (!(sch->schib.scsw.actl & SCSW_ACTL_HALT_PEND) && -- cgit From d9a9cdfb078d755e648d53ec25b7370f84ee5729 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 15 Mar 2007 15:50:34 -0400 Subject: [PATCH] sysfs and driver core: add callback helper, used by SCSI and S390 This patch (as868) adds a helper routine for device drivers that need to set up a callback to perform some action in a different process's context. This is intended for use by attribute methods that want to unregister themselves or their parent device. Attribute method calls are mutually exclusive with unregistration, so such actions cannot be taken directly. Two attribute methods are converted to use the new helper routine: one for SCSI device deletion and one for System/390 ccwgroup devices. Signed-off-by: Alan Stern Cc: Hugh Dickins Cc: Cornelia Huck Cc: Oliver Neukum Signed-off-by: Linus Torvalds --- drivers/s390/cio/ccwgroup.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index d48e3ca4752..5aeb68e732b 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -71,19 +71,31 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) * Provide an 'ungroup' attribute so the user can remove group devices no * longer needed or accidentially created. Saves memory :) */ +static void ccwgroup_ungroup_callback(struct device *dev) +{ + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + + __ccwgroup_remove_symlinks(gdev); + device_unregister(dev); +} + static ssize_t ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ccwgroup_device *gdev; + int rc; gdev = to_ccwgroupdev(dev); if (gdev->state != CCWGROUP_OFFLINE) return -EINVAL; - __ccwgroup_remove_symlinks(gdev); - device_unregister(dev); - + /* 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; return count; } -- cgit From dcc49d460c4052d2fa7ba44e81d3b814b799f802 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 19 Mar 2007 13:18:12 +0100 Subject: [S390] cio: qdio slsb setup Make sure set_slsb problems are handled correctly in qdio_do_qdio_fill_input() and qdio_do_qdio_fill_output. Signed-off-by: Ursula Braun Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 5b1e3ff26c0..05fac0733f3 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -210,9 +210,11 @@ again: goto again; } if (rc < 0) { - QDIO_DBF_TEXT3(1,trace,"sqberr"); - sprintf(dbf_text,"%2x,%2x,%d,%d",tmp_cnt,*cnt,ccq,q_no); - QDIO_DBF_TEXT3(1,trace,dbf_text); + QDIO_DBF_TEXT3(1,trace,"sqberr"); + sprintf(dbf_text,"%2x,%2x",tmp_cnt,*cnt); + QDIO_DBF_TEXT3(1,trace,dbf_text); + sprintf(dbf_text,"%d,%d",ccq,q_no); + QDIO_DBF_TEXT3(1,trace,dbf_text); q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION| QDIO_STATUS_LOOK_FOR_ERROR, 0, 0, 0, -1, -1, q->int_parm); @@ -1250,7 +1252,6 @@ qdio_is_inbound_q_done(struct qdio_q *q) if (!no_used) { QDIO_DBF_TEXT4(0,trace,"inqisdnA"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); - QDIO_DBF_TEXT4(0,trace,dbf_text); return 1; } if (irq->is_qebsm) { @@ -3371,10 +3372,15 @@ qdio_do_qdio_fill_input(struct qdio_q *q, unsigned int qidx, unsigned int count, struct qdio_buffer *buffers) { struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; + int tmp = 0; + qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1); if (irq->is_qebsm) { - while (count) - set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count); + while (count) { + tmp = set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count); + if (!tmp) + return; + } return; } for (;;) { @@ -3390,11 +3396,15 @@ qdio_do_qdio_fill_output(struct qdio_q *q, unsigned int qidx, unsigned int count, struct qdio_buffer *buffers) { struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; + int tmp = 0; qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1); if (irq->is_qebsm) { - while (count) - set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count); + while (count) { + tmp = set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count); + if (!tmp) + return; + } return; } -- cgit From 25c61a1fe8c97d1352a2dc0eda25128b3be0db27 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 26 Mar 2007 20:42:41 +0200 Subject: [S390] cio: Device status validity. Only accumulate device status field in irb if it is valid. Cc: Martin Schwidefsky Signed-off-by: Cornelia Huck Signed-off-by: Heiko Carstens --- drivers/s390/cio/device_status.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index 6b1caea622e..25d99bd2808 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -263,7 +263,11 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) cdev_irb->scsw.cpa = irb->scsw.cpa; /* Accumulate device status, but not the device busy flag. */ cdev_irb->scsw.dstat &= ~DEV_STAT_BUSY; - cdev_irb->scsw.dstat |= irb->scsw.dstat; + /* dstat is not always valid. */ + if (irb->scsw.stctl & + (SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_SEC_STATUS + | SCSW_STCTL_INTER_STATUS | SCSW_STCTL_ALERT_STATUS)) + cdev_irb->scsw.dstat |= irb->scsw.dstat; /* Accumulate subchannel status. */ cdev_irb->scsw.cstat |= irb->scsw.cstat; /* Copy residual count if it is valid. */ -- cgit From 8c3ce5bece2e6e233c1d05b460883046d020fbf9 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Wed, 4 Apr 2007 14:37:11 +0200 Subject: [S390] cio: Fix handling of interrupt for csch(). Wipe internal irb if the clear function bit is set before accumulating bits from the irb in order to follow hardware behaviour. Signed-off-by: Cornelia Huck Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_status.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index 25d99bd2808..aa96e675259 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -221,6 +221,14 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) cdev_irb = &cdev->private->irb; + /* + * If the clear function had been performed, all formerly pending + * status at the subchannel has been cleared and we must not pass + * intermediate accumulated status to the device driver. + */ + if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) + memset(&cdev->private->irb, 0, sizeof(struct irb)); + /* Copy bits which are valid only for the start function. */ if (irb->scsw.fctl & SCSW_FCTL_START_FUNC) { /* Copy key. */ -- cgit From 6fc321fd7dd91f0592f37503219196835314fbb7 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 27 Apr 2007 16:01:25 +0200 Subject: [S390] cio/ipl: Clean interface between cio and ipl code. Clean interface between cio and ipl code, so Peter stops complaining. Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/cio/cio.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 9cb129ab5be..21af446c1f2 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -1048,37 +1048,19 @@ void reipl_ccw_dev(struct ccw_dev_id *devid) do_reipl_asm(*((__u32*)&schid)); } -static struct schib __initdata ipl_schib; - -/* - * ipl_save_parameters gets called very early. It is not allowed to access - * anything in the bss section at all. The bss section is not cleared yet, - * but may contain some ipl parameters written by the firmware. - * These parameters (if present) are copied to 0x2000. - * To avoid corruption of the ipl parameters, all variables used by this - * function must reside on the stack or in the data section. - */ -void ipl_save_parameters(void) +int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo) { struct subchannel_id schid; - unsigned int *ipl_ptr; - void *src, *dst; + struct schib schib; schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID; if (!schid.one) - return; - if (stsch(schid, &ipl_schib)) - return; - if (!ipl_schib.pmcw.dnv) - return; - ipl_devno = ipl_schib.pmcw.dev; - ipl_flags |= IPL_DEVNO_VALID; - if (!ipl_schib.pmcw.qf) - return; - ipl_flags |= IPL_PARMBLOCK_VALID; - ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR; - src = (void *)(unsigned long)*ipl_ptr; - dst = (void *)IPL_PARMBLOCK_ORIGIN; - memmove(dst, src, PAGE_SIZE); - *ipl_ptr = IPL_PARMBLOCK_ORIGIN; + return -ENODEV; + if (stsch(schid, &schib)) + return -ENODEV; + if (!schib.pmcw.dnv) + return -ENODEV; + iplinfo->devno = schib.pmcw.dev; + iplinfo->is_qdio = schib.pmcw.qf; + return 0; } -- cgit From f86635fad14c4a6810cf0e08488fc9129a3b3b32 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:26 +0200 Subject: [S390] cio: Introduce struct chp_id. Introduce data type for channel-path IDs. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/cio/chpid.h | 51 ++++++++++++ drivers/s390/cio/chsc.c | 178 +++++++++++++++++++++++------------------- drivers/s390/cio/chsc.h | 8 +- drivers/s390/cio/cio.c | 5 +- drivers/s390/cio/device_fsm.c | 6 +- drivers/s390/cio/ioasm.h | 5 +- 6 files changed, 167 insertions(+), 86 deletions(-) create mode 100644 drivers/s390/cio/chpid.h (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/chpid.h b/drivers/s390/cio/chpid.h new file mode 100644 index 00000000000..44cc00ed9b5 --- /dev/null +++ b/drivers/s390/cio/chpid.h @@ -0,0 +1,51 @@ +/* + * drivers/s390/cio/chpid.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef S390_CHP_ID_H +#define S390_CHP_ID_H S390_CHP_ID_H + +#include +#include +#include "css.h" + +struct chp_id { + u8 reserved1; + u8 cssid; + u8 reserved2; + u8 id; +} __attribute__((packed)); + +static inline void chp_id_init(struct chp_id *chpid) +{ + memset(chpid, 0, sizeof(struct chp_id)); +} + +static inline int chp_id_is_equal(struct chp_id *a, struct chp_id *b) +{ + return (a->id == b->id) && (a->cssid == b->cssid); +} + +static inline void chp_id_next(struct chp_id *chpid) +{ + if (chpid->id < __MAX_CHPID) + chpid->id++; + else { + chpid->id = 0; + chpid->cssid++; + } +} + +static inline int chp_id_is_valid(struct chp_id *chpid) +{ + return (chpid->cssid <= __MAX_CSSID); +} + + +#define chp_id_for_each(c) \ + for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c)) + +#endif /* S390_CHP_ID_H */ diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 6f05a44e381..bb6f876e53c 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -20,43 +20,48 @@ #include "cio.h" #include "cio_debug.h" #include "ioasm.h" +#include "chpid.h" #include "chsc.h" static void *sei_page; -static int new_channel_path(int chpid); +static int new_channel_path(struct chp_id chpid); -static inline void -set_chp_logically_online(int chp, int onoff) +static inline struct channel_path *chpid_to_chp(struct chp_id chpid) { - css[0]->chps[chp]->state = onoff; + return css[chpid.cssid]->chps[chpid.id]; } -static int -get_chp_status(int chp) +static void set_chp_logically_online(struct chp_id chpid, int onoff) { - return (css[0]->chps[chp] ? css[0]->chps[chp]->state : -ENODEV); + chpid_to_chp(chpid)->state = onoff; } -void -chsc_validate_chpids(struct subchannel *sch) +static int get_chp_status(struct chp_id chpid) +{ + return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV); +} + +void chsc_validate_chpids(struct subchannel *sch) { int mask, chp; + struct chp_id chpid; + chp_id_init(&chpid); for (chp = 0; chp <= 7; chp++) { mask = 0x80 >> chp; - if (!get_chp_status(sch->schib.pmcw.chpid[chp])) + chpid.id = sch->schib.pmcw.chpid[chp]; + if (!get_chp_status(chpid)) /* disable using this path */ sch->opm &= ~mask; } } -void -chpid_is_actually_online(int chp) +void chpid_is_actually_online(struct chp_id chpid) { int state; - state = get_chp_status(chp); + state = get_chp_status(chpid); if (state < 0) { need_rescan = 1; queue_work(slow_path_wq, &slow_path_work); @@ -200,11 +205,14 @@ css_get_ssd_info(struct subchannel *sch) spin_unlock_irq(sch->lock); free_page((unsigned long)page); if (!ret) { - int j, chpid, mask; + int j, mask; + struct chp_id chpid; + + chp_id_init(&chpid); /* Allocate channel path structures, if needed. */ for (j = 0; j < 8; j++) { mask = 0x80 >> j; - chpid = sch->ssd_info.chpid[j]; + chpid.id = sch->ssd_info.chpid[j]; if ((sch->schib.pmcw.pim & mask) && (get_chp_status(chpid) < 0)) new_channel_path(chpid); @@ -227,7 +235,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) for (j = 0; j < 8; j++) { mask = 0x80 >> j; if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[j] == chpid->id)) + (sch->schib.pmcw.chpid[j] == chpid->chpid.id)) break; } if (j >= 8) @@ -277,18 +285,17 @@ out_unreg: return 0; } -static void -s390_set_chpid_offline( __u8 chpid) +static void s390_set_chpid_offline(struct chp_id chpid) { char dbf_txt[15]; struct device *dev; - sprintf(dbf_txt, "chpr%x", chpid); + sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); if (get_chp_status(chpid) <= 0) return; - dev = get_device(&css[0]->chps[chpid]->dev); + dev = get_device(&(chpid_to_chp(chpid)->dev)); bus_for_each_dev(&css_bus_type, NULL, to_channelpath(dev), s390_subchannel_remove_chpid); @@ -316,7 +323,7 @@ s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch) * check if chpid is in information updated by ssd */ if (sch->ssd_info.valid && - sch->ssd_info.chpid[chp] == res_data->chp->id && + sch->ssd_info.chpid[chp] == res_data->chp->chpid.id && (sch->ssd_info.fla[chp] & res_data->fla_mask) == res_data->fla) { found = 1; @@ -409,7 +416,8 @@ s390_process_res_acc (struct res_acc_data *res_data) int rc; char dbf_txt[15]; - sprintf(dbf_txt, "accpr%x", res_data->chp->id); + sprintf(dbf_txt, "accpr%x.%02x", res_data->chp->chpid.cssid, + res_data->chp->chpid.id); CIO_TRACE_EVENT( 2, dbf_txt); if (res_data->fla != 0) { sprintf(dbf_txt, "fla%x", res_data->fla); @@ -482,17 +490,21 @@ struct chsc_sei_area { static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) { - int chpid; + struct chp_id chpid; + int id; CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) return 0; - chpid = __get_chpid_from_lir(sei_area->ccdf); - if (chpid < 0) + id = __get_chpid_from_lir(sei_area->ccdf); + if (id < 0) CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); - else + else { + chp_id_init(&chpid); + chpid.id = id; s390_set_chpid_offline(chpid); + } return 0; } @@ -501,6 +513,7 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) { struct res_acc_data res_data; struct device *dev; + struct chp_id chpid; int status; int rc; @@ -508,13 +521,15 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) return 0; + chp_id_init(&chpid); + chpid.id = sei_area->rsid; /* allocate a new channel path structure, if needed */ - status = get_chp_status(sei_area->rsid); + status = get_chp_status(chpid); if (status < 0) - new_channel_path(sei_area->rsid); + new_channel_path(chpid); else if (!status) return 0; - dev = get_device(&css[0]->chps[sei_area->rsid]->dev); + dev = get_device(&(chpid_to_chp(chpid)->dev)); memset(&res_data, 0, sizeof(struct res_acc_data)); res_data.chp = to_channelpath(dev); if ((sei_area->vf & 0xc0) != 0) { @@ -631,7 +646,7 @@ __chp_add(struct subchannel_id schid, void *data) for (i=0; i<8; i++) { mask = 0x80 >> i; if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[i] == chp->id)) { + (sch->schib.pmcw.chpid[i] == chp->chpid.id)) { if (stsch(sch->schid, &sch->schib) != 0) { /* Endgame. */ spin_unlock_irq(sch->lock); @@ -657,8 +672,7 @@ __chp_add(struct subchannel_id schid, void *data) return 0; } -static int -chp_add(int chpid) +static int chp_add(struct chp_id chpid) { int rc; char dbf_txt[15]; @@ -667,10 +681,10 @@ chp_add(int chpid) if (!get_chp_status(chpid)) return 0; /* no need to do the rest */ - sprintf(dbf_txt, "cadd%x", chpid); + sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - dev = get_device(&css[0]->chps[chpid]->dev); + dev = get_device(&(chpid_to_chp(chpid)->dev)); rc = for_each_subchannel(__chp_add, to_channelpath(dev)); if (css_slow_subchannels_exist()) rc = -EAGAIN; @@ -683,9 +697,12 @@ chp_add(int chpid) /* * Handling of crw machine checks with channel path source. */ -int -chp_process_crw(int chpid, int on) +int chp_process_crw(int id, int on) { + struct chp_id chpid; + + chp_id_init(&chpid); + chpid.id = id; if (on == 0) { /* Path has gone. We use the link incident routine.*/ s390_set_chpid_offline(chpid); @@ -733,8 +750,8 @@ static void terminate_internal_io(struct subchannel *sch) sch->driver->termination(&sch->dev); } -static void -__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) +static void __s390_subchannel_vary_chpid(struct subchannel *sch, + struct chp_id chpid, int on) { int chp, old_lpm; unsigned long flags; @@ -745,7 +762,7 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) spin_lock_irqsave(sch->lock, flags); old_lpm = sch->lpm; for (chp = 0; chp < 8; chp++) { - if (sch->ssd_info.chpid[chp] != chpid) + if (sch->ssd_info.chpid[chp] != chpid.id) continue; if (on) { @@ -780,11 +797,10 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) spin_unlock_irqrestore(sch->lock, flags); } -static int -s390_subchannel_vary_chpid_off(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_off(struct device *dev, void *data) { struct subchannel *sch; - __u8 *chpid; + struct chp_id *chpid; sch = to_subchannel(dev); chpid = data; @@ -793,11 +809,10 @@ s390_subchannel_vary_chpid_off(struct device *dev, void *data) return 0; } -static int -s390_subchannel_vary_chpid_on(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_on(struct device *dev, void *data) { struct subchannel *sch; - __u8 *chpid; + struct chp_id *chpid; sch = to_subchannel(dev); chpid = data; @@ -833,23 +848,25 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) * Function: s390_vary_chpid * Varies the specified chpid online or offline */ -static int -s390_vary_chpid( __u8 chpid, int on) +static int s390_vary_chpid(struct chp_id chpid, int on) { char dbf_text[15]; int status; - sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid); + sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid, + chpid.id); CIO_TRACE_EVENT( 2, dbf_text); status = get_chp_status(chpid); if (status < 0) { - printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid); + printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n", + chpid.cssid, chpid.id); return -EINVAL; } if (!on && !status) { - printk(KERN_ERR "chpid %x is already offline\n", chpid); + printk(KERN_ERR "chpid %x.%02x is already offline\n", + chpid.cssid, chpid.id); return -EINVAL; } @@ -904,20 +921,19 @@ static struct bin_attribute chp_measurement_chars_attr = { .read = chp_measurement_chars_read, }; -static void -chp_measurement_copy_block(struct cmg_entry *buf, - struct channel_subsystem *css, int chpid) +static void chp_measurement_copy_block(struct cmg_entry *buf, + struct channel_subsystem *css, struct chp_id chpid) { void *area; struct cmg_entry *entry, reference_buf; int idx; - if (chpid < 128) { + if (chpid.id < 128) { area = css->cub_addr1; - idx = chpid; + idx = chpid.id; } else { area = css->cub_addr2; - idx = chpid - 128; + idx = chpid.id - 128; } entry = area + (idx * sizeof(struct cmg_entry)); do { @@ -941,7 +957,7 @@ chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count) /* Only allow single reads. */ if (off || count < size) return 0; - chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id); + chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid); count = size; return count; } @@ -1137,7 +1153,7 @@ chp_status_show(struct device *dev, struct device_attribute *attr, char *buf) if (!chp) return 0; - return (get_chp_status(chp->id) ? sprintf(buf, "online\n") : + return (get_chp_status(chp->chpid) ? sprintf(buf, "online\n") : sprintf(buf, "offline\n")); } @@ -1154,9 +1170,9 @@ chp_status_write(struct device *dev, struct device_attribute *attr, const char * return count; if (!strnicmp(cmd, "on", 2)) - error = s390_vary_chpid(cp->id, 1); + error = s390_vary_chpid(cp->chpid, 1); else if (!strnicmp(cmd, "off", 3)) - error = s390_vary_chpid(cp->id, 0); + error = s390_vary_chpid(cp->chpid, 0); else error = -EINVAL; @@ -1227,9 +1243,8 @@ chp_release(struct device *dev) kfree(cp); } -static int -chsc_determine_channel_path_description(int chpid, - struct channel_path_desc *desc) +static int chsc_determine_channel_path_description(struct chp_id chpid, + struct channel_path_desc *desc) { int ccode, ret; @@ -1252,8 +1267,8 @@ chsc_determine_channel_path_description(int chpid, scpd_area->request.length = 0x0010; scpd_area->request.code = 0x0002; - scpd_area->first_chpid = chpid; - scpd_area->last_chpid = chpid; + scpd_area->first_chpid = chpid.id; + scpd_area->last_chpid = chpid.id; ccode = chsc(scpd_area); if (ccode > 0) { @@ -1349,8 +1364,8 @@ chsc_get_channel_measurement_chars(struct channel_path *chp) scmc_area->request.length = 0x0010; scmc_area->request.code = 0x0022; - scmc_area->first_chpid = chp->id; - scmc_area->last_chpid = chp->id; + scmc_area->first_chpid = chp->chpid.id; + scmc_area->last_chpid = chp->chpid.id; ccode = chsc(scmc_area); if (ccode > 0) { @@ -1396,8 +1411,7 @@ out: * Entries for chpids on the system bus. * This replaces /proc/chpids. */ -static int -new_channel_path(int chpid) +static int new_channel_path(struct chp_id chpid) { struct channel_path *chp; int ret; @@ -1407,11 +1421,12 @@ new_channel_path(int chpid) return -ENOMEM; /* fill in status, etc. */ - chp->id = chpid; + chp->chpid = chpid; chp->state = 1; - chp->dev.parent = &css[0]->device; + chp->dev.parent = &css[chpid.cssid]->device; chp->dev.release = chp_release; - snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid); + snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid, + chpid.id); /* Obtain channel path description and fill it in. */ ret = chsc_determine_channel_path_description(chpid, &chp->desc); @@ -1437,8 +1452,8 @@ new_channel_path(int chpid) /* make it known to the system */ ret = device_register(&chp->dev); if (ret) { - printk(KERN_WARNING "%s: could not register %02x\n", - __func__, chpid); + printk(KERN_WARNING "%s: could not register %x.%02x\n", + __func__, chpid.cssid, chpid.id); goto out_free; } ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); @@ -1446,18 +1461,18 @@ new_channel_path(int chpid) device_unregister(&chp->dev); goto out_free; } - mutex_lock(&css[0]->mutex); - if (css[0]->cm_enabled) { + mutex_lock(&css[chpid.cssid]->mutex); + if (css[chpid.cssid]->cm_enabled) { ret = chsc_add_chp_cmg_attr(chp); if (ret) { sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); device_unregister(&chp->dev); - mutex_unlock(&css[0]->mutex); + mutex_unlock(&css[chpid.cssid]->mutex); goto out_free; } } - css[0]->chps[chpid] = chp; - mutex_unlock(&css[0]->mutex); + css[chpid.cssid]->chps[chpid.id] = chp; + mutex_unlock(&css[chpid.cssid]->mutex); return ret; out_free: kfree(chp); @@ -1469,8 +1484,11 @@ chsc_get_chp_desc(struct subchannel *sch, int chp_no) { struct channel_path *chp; struct channel_path_desc *desc; + struct chp_id chpid; - chp = css[0]->chps[sch->schib.pmcw.chpid[chp_no]]; + chp_id_init(&chpid); + chpid.id = sch->schib.pmcw.chpid[chp_no]; + chp = chpid_to_chp(chpid); if (!chp) return NULL; desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 0fb2b024208..2949c85b6d9 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -1,6 +1,10 @@ #ifndef S390_CHSC_H #define S390_CHSC_H +#include +#include +#include "chpid.h" + #define CHSC_SEI_ACC_CHPID 1 #define CHSC_SEI_ACC_LINKADDR 2 #define CHSC_SEI_ACC_FULLLINKADDR 3 @@ -34,7 +38,7 @@ struct channel_path_desc { } __attribute__ ((packed)); struct channel_path { - int id; + struct chp_id chpid; int state; struct channel_path_desc desc; /* Channel-measurement related stuff: */ @@ -46,7 +50,7 @@ struct channel_path { extern void s390_process_css( void ); extern void chsc_validate_chpids(struct subchannel *); -extern void chpid_is_actually_online(int); +extern void chpid_is_actually_online(struct chp_id); extern int css_get_ssd_info(struct subchannel *); extern int chsc_process_crw(void); extern int chp_process_crw(int, int); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 21af446c1f2..bab729202f4 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -954,6 +954,7 @@ static void css_reset(void) { int i, ret; unsigned long long timeout; + struct chp_id chpid; /* Reset subchannels. */ for_each_subchannel(__shutdown_subchannel_easy, NULL); @@ -963,8 +964,10 @@ static void css_reset(void) __ctl_set_bit(14, 28); /* Temporarily reenable machine checks. */ local_mcck_enable(); + chp_id_init(&chpid); for (i = 0; i <= __MAX_CHPID; i++) { - ret = rchp(i); + chpid.id = i; + ret = rchp(chpid); if ((ret == 0) || (ret == 2)) /* * rchp either succeeded, or another rchp is already diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 089a3ddd626..5f5ee1eef07 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -22,6 +22,7 @@ #include "device.h" #include "chsc.h" #include "ioasm.h" +#include "chpid.h" int device_is_online(struct subchannel *sch) @@ -210,14 +211,17 @@ static void __recover_lost_chpids(struct subchannel *sch, int old_lpm) { int mask, i; + struct chp_id chpid; + chp_id_init(&chpid); for (i = 0; i<8; i++) { mask = 0x80 >> i; if (!(sch->lpm & mask)) continue; if (old_lpm & mask) continue; - chpid_is_actually_online(sch->schib.pmcw.chpid[i]); + chpid.id = sch->schib.pmcw.chpid[i]; + chpid_is_actually_online(chpid); } } diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index ad6d8294006..78c7db6daa8 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -2,6 +2,7 @@ #define S390_CIO_IOASM_H #include "schid.h" +#include "chpid.h" /* * TPI info structure @@ -189,9 +190,9 @@ static inline int chsc(void *chsc_area) return cc; } -static inline int rchp(int chpid) +static inline int rchp(struct chp_id chpid) { - register unsigned int reg1 asm ("1") = chpid; + register struct chp_id reg1 asm ("1") = chpid; int ccode; asm volatile( -- cgit From d120b2a4e60cc9e62e7cc5dcf049100af3745cc4 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:27 +0200 Subject: [S390] cio: Allow 0 and 1 as input for channel path status attribute. Channel path status can now be modified by writing '0' and '1' to the sysfs status attribute in addition to 'offline' and 'online' respectively. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/cio/chsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index bb6f876e53c..b329851f7b5 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1169,9 +1169,9 @@ chp_status_write(struct device *dev, struct device_attribute *attr, const char * if (!num_args) return count; - if (!strnicmp(cmd, "on", 2)) + if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) error = s390_vary_chpid(cp->chpid, 1); - else if (!strnicmp(cmd, "off", 3)) + else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) error = s390_vary_chpid(cp->chpid, 0); else error = -EINVAL; -- cgit From e6b6e10ac1de116fc6d2288f185393014851cccf Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:28 +0200 Subject: [S390] cio: Introduce separate files for channel-path related code. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/cio/Makefile | 2 +- drivers/s390/cio/chp.c | 433 ++++++++++++++++++++++++++++++++++++++ drivers/s390/cio/chp.h | 37 ++++ drivers/s390/cio/chsc.c | 473 ++++-------------------------------------- drivers/s390/cio/chsc.h | 28 +-- drivers/s390/cio/cio.c | 8 +- drivers/s390/cio/css.h | 1 + drivers/s390/cio/device_fsm.c | 6 +- drivers/s390/cio/device_ops.c | 7 +- 9 files changed, 533 insertions(+), 462 deletions(-) create mode 100644 drivers/s390/cio/chp.c create mode 100644 drivers/s390/cio/chp.h (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index c490c2a1c2f..fe7b3ffa1ea 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -2,7 +2,7 @@ # Makefile for the S/390 common i/o drivers # -obj-y += airq.o blacklist.o chsc.o cio.o css.o +obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c new file mode 100644 index 00000000000..6e04521accd --- /dev/null +++ b/drivers/s390/cio/chp.c @@ -0,0 +1,433 @@ +/* + * drivers/s390/cio/chp.c + * + * Copyright IBM Corp. 1999,2007 + * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) + * Arnd Bergmann (arndb@de.ibm.com) + * Peter Oberparleiter + */ + +#include +#include +#include +#include + +#include "chpid.h" +#include "cio.h" +#include "css.h" +#include "ioasm.h" +#include "cio_debug.h" +#include "chp.h" + +#define to_channelpath(device) container_of(device, struct channel_path, dev) + +/* Return channel_path struct for given chpid. */ +static inline struct channel_path *chpid_to_chp(struct chp_id chpid) +{ + return css[chpid.cssid]->chps[chpid.id]; +} + +/* Set vary state for given chpid. */ +static void set_chp_logically_online(struct chp_id chpid, int onoff) +{ + chpid_to_chp(chpid)->state = onoff; +} + +/* On succes return 0 if channel-path is varied offline, 1 if it is varied + * online. Return -ENODEV if channel-path is not registered. */ +int chp_get_status(struct chp_id chpid) +{ + return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV); +} + +/** + * chp_get_sch_opm - return opm for subchannel + * @sch: subchannel + * + * Calculate and return the operational path mask (opm) based on the chpids + * used by the subchannel and the status of the associated channel-paths. + */ +u8 chp_get_sch_opm(struct subchannel *sch) +{ + struct chp_id chpid; + int opm; + int i; + + opm = 0; + chp_id_init(&chpid); + for (i=0; i < 8; i++) { + opm <<= 1; + chpid.id = sch->schib.pmcw.chpid[i]; + if (chp_get_status(chpid) != 0) + opm |= 1; + } + return opm; +} + +/** + * chp_is_registered - check if a channel-path is registered + * @chpid: channel-path ID + * + * Return non-zero if a channel-path with the given chpid is registered, + * zero otherwise. + */ +int chp_is_registered(struct chp_id chpid) +{ + return chpid_to_chp(chpid) != NULL; +} + +/* + * Function: s390_vary_chpid + * Varies the specified chpid online or offline + */ +static int s390_vary_chpid(struct chp_id chpid, int on) +{ + char dbf_text[15]; + int status; + + sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid, + chpid.id); + CIO_TRACE_EVENT( 2, dbf_text); + + status = chp_get_status(chpid); + if (status < 0) { + printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n", + chpid.cssid, chpid.id); + return -EINVAL; + } + + if (!on && !status) { + printk(KERN_ERR "chpid %x.%02x is already offline\n", + chpid.cssid, chpid.id); + return -EINVAL; + } + + set_chp_logically_online(chpid, on); + chsc_chp_vary(chpid, on); + return 0; +} + +/* + * Channel measurement related functions + */ +static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct channel_path *chp; + unsigned int size; + + chp = to_channelpath(container_of(kobj, struct device, kobj)); + if (!chp->cmg_chars) + return 0; + + size = sizeof(struct cmg_chars); + + if (off > size) + return 0; + if (off + count > size) + count = size - off; + memcpy(buf, chp->cmg_chars + off, count); + return count; +} + +static struct bin_attribute chp_measurement_chars_attr = { + .attr = { + .name = "measurement_chars", + .mode = S_IRUSR, + .owner = THIS_MODULE, + }, + .size = sizeof(struct cmg_chars), + .read = chp_measurement_chars_read, +}; + +static void chp_measurement_copy_block(struct cmg_entry *buf, + struct channel_subsystem *css, + struct chp_id chpid) +{ + void *area; + struct cmg_entry *entry, reference_buf; + int idx; + + if (chpid.id < 128) { + area = css->cub_addr1; + idx = chpid.id; + } else { + area = css->cub_addr2; + idx = chpid.id - 128; + } + entry = area + (idx * sizeof(struct cmg_entry)); + do { + memcpy(buf, entry, sizeof(*entry)); + memcpy(&reference_buf, entry, sizeof(*entry)); + } while (reference_buf.values[0] != buf->values[0]); +} + +static ssize_t chp_measurement_read(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct channel_path *chp; + struct channel_subsystem *css; + unsigned int size; + + chp = to_channelpath(container_of(kobj, struct device, kobj)); + css = to_css(chp->dev.parent); + + size = sizeof(struct cmg_entry); + + /* Only allow single reads. */ + if (off || count < size) + return 0; + chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid); + count = size; + return count; +} + +static struct bin_attribute chp_measurement_attr = { + .attr = { + .name = "measurement", + .mode = S_IRUSR, + .owner = THIS_MODULE, + }, + .size = sizeof(struct cmg_entry), + .read = chp_measurement_read, +}; + +void chp_remove_cmg_attr(struct channel_path *chp) +{ + device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); + device_remove_bin_file(&chp->dev, &chp_measurement_attr); +} + +int chp_add_cmg_attr(struct channel_path *chp) +{ + int ret; + + ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); + if (ret) + return ret; + ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); + if (ret) + device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); + return ret; +} + +/* + * Files for the channel path entries. + */ +static ssize_t chp_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *chp = container_of(dev, struct channel_path, dev); + + if (!chp) + return 0; + return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") : + sprintf(buf, "offline\n")); +} + +static ssize_t chp_status_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct channel_path *cp = container_of(dev, struct channel_path, dev); + char cmd[10]; + int num_args; + int error; + + num_args = sscanf(buf, "%5s", cmd); + if (!num_args) + return count; + + if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) + error = s390_vary_chpid(cp->chpid, 1); + else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) + error = s390_vary_chpid(cp->chpid, 0); + else + error = -EINVAL; + + return error < 0 ? error : count; + +} + +static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); + +static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct channel_path *chp = container_of(dev, struct channel_path, dev); + + if (!chp) + return 0; + return sprintf(buf, "%x\n", chp->desc.desc); +} + +static DEVICE_ATTR(type, 0444, chp_type_show, NULL); + +static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct channel_path *chp = to_channelpath(dev); + + if (!chp) + return 0; + if (chp->cmg == -1) /* channel measurements not available */ + return sprintf(buf, "unknown\n"); + return sprintf(buf, "%x\n", chp->cmg); +} + +static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); + +static ssize_t chp_shared_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *chp = to_channelpath(dev); + + if (!chp) + return 0; + if (chp->shared == -1) /* channel measurements not available */ + return sprintf(buf, "unknown\n"); + return sprintf(buf, "%x\n", chp->shared); +} + +static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); + +static struct attribute * chp_attrs[] = { + &dev_attr_status.attr, + &dev_attr_type.attr, + &dev_attr_cmg.attr, + &dev_attr_shared.attr, + NULL, +}; + +static struct attribute_group chp_attr_group = { + .attrs = chp_attrs, +}; + +static void chp_release(struct device *dev) +{ + struct channel_path *cp; + + cp = container_of(dev, struct channel_path, dev); + kfree(cp); +} + +/** + * chp_new - register a new channel-path + * @chpid - channel-path ID + * + * Create and register data structure representing new channel-path. Return + * zero on success, non-zero otherwise. + */ +int chp_new(struct chp_id chpid) +{ + struct channel_path *chp; + int ret; + + chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); + if (!chp) + return -ENOMEM; + + /* fill in status, etc. */ + chp->chpid = chpid; + chp->state = 1; + chp->dev.parent = &css[chpid.cssid]->device; + chp->dev.release = chp_release; + snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid, + chpid.id); + + /* Obtain channel path description and fill it in. */ + ret = chsc_determine_channel_path_description(chpid, &chp->desc); + if (ret) + goto out_free; + /* Get channel-measurement characteristics. */ + if (css_characteristics_avail && css_chsc_characteristics.scmc + && css_chsc_characteristics.secm) { + ret = chsc_get_channel_measurement_chars(chp); + if (ret) + goto out_free; + } else { + static int msg_done; + + if (!msg_done) { + printk(KERN_WARNING "cio: Channel measurements not " + "available, continuing.\n"); + msg_done = 1; + } + chp->cmg = -1; + } + + /* make it known to the system */ + ret = device_register(&chp->dev); + if (ret) { + printk(KERN_WARNING "%s: could not register %x.%02x\n", + __func__, chpid.cssid, chpid.id); + goto out_free; + } + ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); + if (ret) { + device_unregister(&chp->dev); + goto out_free; + } + mutex_lock(&css[chpid.cssid]->mutex); + if (css[chpid.cssid]->cm_enabled) { + ret = chp_add_cmg_attr(chp); + if (ret) { + sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); + device_unregister(&chp->dev); + mutex_unlock(&css[chpid.cssid]->mutex); + goto out_free; + } + } + css[chpid.cssid]->chps[chpid.id] = chp; + mutex_unlock(&css[chpid.cssid]->mutex); + return ret; +out_free: + kfree(chp); + return ret; +} + +/** + * chp_get_chp_desc - return newly allocated channel-path description + * @chpid: channel-path ID + * + * On success return a newly allocated copy of the channel-path description + * data associated with the given channel-path ID. Return %NULL on error. + */ +void *chp_get_chp_desc(struct chp_id chpid) +{ + struct channel_path *chp; + struct channel_path_desc *desc; + + chp = chpid_to_chp(chpid); + if (!chp) + return NULL; + desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); + if (!desc) + return NULL; + memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); + return desc; +} + +/** + * chp_process_crw - process channel-path status change + * @id: channel-path ID number + * @status: non-zero if channel-path has become available, zero otherwise + * + * Handle channel-report-words indicating that the status of a channel-path + * has changed. + */ +int chp_process_crw(int id, int status) +{ + struct chp_id chpid; + + chp_id_init(&chpid); + chpid.id = id; + if (status) { + if (!chp_is_registered(chpid)) + chp_new(chpid); + return chsc_chp_online(chpid); + } else { + chsc_chp_offline(chpid); + return 0; + } +} diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h new file mode 100644 index 00000000000..ac2b1a9c3bc --- /dev/null +++ b/drivers/s390/cio/chp.h @@ -0,0 +1,37 @@ +/* + * drivers/s390/cio/chp.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef S390_CHP_H +#define S390_CHP_H S390_CHP_H + +#include +#include + +#include "chpid.h" +#include "chsc.h" + +struct channel_path { + struct chp_id chpid; + int state; + struct channel_path_desc desc; + /* Channel-measurement related stuff: */ + int cmg; + int shared; + void *cmg_chars; + struct device dev; +}; + +int chp_get_status(struct chp_id chpid); +u8 chp_get_sch_opm(struct subchannel *sch); +int chp_is_registered(struct chp_id chpid); +void *chp_get_chp_desc(struct chp_id chpid); +int chp_process_crw(int id, int available); +void chp_remove_cmg_attr(struct channel_path *chp); +int chp_add_cmg_attr(struct channel_path *chp); +int chp_new(struct chp_id chpid); + +#endif /* S390_CHP_H */ diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index b329851f7b5..d99f525eac0 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -21,54 +21,11 @@ #include "cio_debug.h" #include "ioasm.h" #include "chpid.h" +#include "chp.h" #include "chsc.h" static void *sei_page; -static int new_channel_path(struct chp_id chpid); - -static inline struct channel_path *chpid_to_chp(struct chp_id chpid) -{ - return css[chpid.cssid]->chps[chpid.id]; -} - -static void set_chp_logically_online(struct chp_id chpid, int onoff) -{ - chpid_to_chp(chpid)->state = onoff; -} - -static int get_chp_status(struct chp_id chpid) -{ - return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV); -} - -void chsc_validate_chpids(struct subchannel *sch) -{ - int mask, chp; - struct chp_id chpid; - - chp_id_init(&chpid); - for (chp = 0; chp <= 7; chp++) { - mask = 0x80 >> chp; - chpid.id = sch->schib.pmcw.chpid[chp]; - if (!get_chp_status(chpid)) - /* disable using this path */ - sch->opm &= ~mask; - } -} - -void chpid_is_actually_online(struct chp_id chpid) -{ - int state; - - state = get_chp_status(chpid); - if (state < 0) { - need_rescan = 1; - queue_work(slow_path_wq, &slow_path_work); - } else - WARN_ON(!state); -} - /* FIXME: this is _always_ called for every subchannel. shouldn't we * process more than one at a time? */ static int @@ -214,8 +171,8 @@ css_get_ssd_info(struct subchannel *sch) mask = 0x80 >> j; chpid.id = sch->ssd_info.chpid[j]; if ((sch->schib.pmcw.pim & mask) && - (get_chp_status(chpid) < 0)) - new_channel_path(chpid); + !chp_is_registered(chpid)) + chp_new(chpid); } } return ret; @@ -227,7 +184,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) int j; int mask; struct subchannel *sch; - struct channel_path *chpid; + struct chp_id *chpid; struct schib schib; sch = to_subchannel(dev); @@ -235,7 +192,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) for (j = 0; j < 8; j++) { mask = 0x80 >> j; if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[j] == chpid->chpid.id)) + (sch->schib.pmcw.chpid[j] == chpid->id)) break; } if (j >= 8) @@ -285,51 +242,48 @@ out_unreg: return 0; } -static void s390_set_chpid_offline(struct chp_id chpid) +void chsc_chp_offline(struct chp_id chpid) { char dbf_txt[15]; - struct device *dev; sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - if (get_chp_status(chpid) <= 0) + if (chp_get_status(chpid) <= 0) return; - dev = get_device(&(chpid_to_chp(chpid)->dev)); - bus_for_each_dev(&css_bus_type, NULL, to_channelpath(dev), + bus_for_each_dev(&css_bus_type, NULL, &chpid, s390_subchannel_remove_chpid); if (need_rescan || css_slow_subchannels_exist()) queue_work(slow_path_wq, &slow_path_work); - put_device(dev); } struct res_acc_data { - struct channel_path *chp; + struct chp_id chpid; u32 fla_mask; u16 fla; }; -static int -s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch) +static int s390_process_res_acc_sch(struct res_acc_data *res_data, + struct subchannel *sch) { int found; int chp; int ccode; - + found = 0; for (chp = 0; chp <= 7; chp++) /* * check if chpid is in information updated by ssd */ if (sch->ssd_info.valid && - sch->ssd_info.chpid[chp] == res_data->chp->chpid.id && + sch->ssd_info.chpid[chp] == res_data->chpid.id && (sch->ssd_info.fla[chp] & res_data->fla_mask) == res_data->fla) { found = 1; break; } - + if (found == 0) return 0; @@ -416,8 +370,8 @@ s390_process_res_acc (struct res_acc_data *res_data) int rc; char dbf_txt[15]; - sprintf(dbf_txt, "accpr%x.%02x", res_data->chp->chpid.cssid, - res_data->chp->chpid.id); + sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, + res_data->chpid.id); CIO_TRACE_EVENT( 2, dbf_txt); if (res_data->fla != 0) { sprintf(dbf_txt, "fla%x", res_data->fla); @@ -503,7 +457,7 @@ static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) else { chp_id_init(&chpid); chpid.id = id; - s390_set_chpid_offline(chpid); + chsc_chp_offline(chpid); } return 0; @@ -512,7 +466,6 @@ static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) { struct res_acc_data res_data; - struct device *dev; struct chp_id chpid; int status; int rc; @@ -524,14 +477,13 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) chp_id_init(&chpid); chpid.id = sei_area->rsid; /* allocate a new channel path structure, if needed */ - status = get_chp_status(chpid); + status = chp_get_status(chpid); if (status < 0) - new_channel_path(chpid); + chp_new(chpid); else if (!status) return 0; - dev = get_device(&(chpid_to_chp(chpid)->dev)); memset(&res_data, 0, sizeof(struct res_acc_data)); - res_data.chp = to_channelpath(dev); + res_data.chpid = chpid; if ((sei_area->vf & 0xc0) != 0) { res_data.fla = sei_area->fla; if ((sei_area->vf & 0xc0) == 0xc0) @@ -542,7 +494,6 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) res_data.fla_mask = 0xff00; } rc = s390_process_res_acc(&res_data); - put_device(dev); return rc; } @@ -634,10 +585,10 @@ static int __chp_add(struct subchannel_id schid, void *data) { int i, mask; - struct channel_path *chp; + struct chp_id *chpid; struct subchannel *sch; - chp = data; + chpid = data; sch = get_subchannel_by_schid(schid); if (!sch) /* Check if the subchannel is now available. */ @@ -646,7 +597,7 @@ __chp_add(struct subchannel_id schid, void *data) for (i=0; i<8; i++) { mask = 0x80 >> i; if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[i] == chp->chpid.id)) { + (sch->schib.pmcw.chpid[i] == chpid->id)) { if (stsch(sch->schid, &sch->schib) != 0) { /* Endgame. */ spin_unlock_irq(sch->lock); @@ -672,52 +623,24 @@ __chp_add(struct subchannel_id schid, void *data) return 0; } -static int chp_add(struct chp_id chpid) +int chsc_chp_online(struct chp_id chpid) { int rc; char dbf_txt[15]; - struct device *dev; - if (!get_chp_status(chpid)) - return 0; /* no need to do the rest */ - sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - dev = get_device(&(chpid_to_chp(chpid)->dev)); - rc = for_each_subchannel(__chp_add, to_channelpath(dev)); + if (chp_get_status(chpid) == 0) + return 0; + rc = for_each_subchannel(__chp_add, &chpid); if (css_slow_subchannels_exist()) rc = -EAGAIN; if (rc != -EAGAIN) rc = 0; - put_device(dev); return rc; } -/* - * Handling of crw machine checks with channel path source. - */ -int chp_process_crw(int id, int on) -{ - struct chp_id chpid; - - chp_id_init(&chpid); - chpid.id = id; - if (on == 0) { - /* Path has gone. We use the link incident routine.*/ - s390_set_chpid_offline(chpid); - return 0; /* De-register is async anyway. */ - } - /* - * Path has come. Allocate a new channel path structure, - * if needed. - */ - if (get_chp_status(chpid) < 0) - new_channel_path(chpid); - /* Avoid the extra overhead in process_rec_acc. */ - return chp_add(chpid); -} - static int check_for_io_on_path(struct subchannel *sch, int index) { int cc; @@ -844,34 +767,13 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) return 0; } -/* - * Function: s390_vary_chpid - * Varies the specified chpid online or offline +/** + * chsc_chp_vary - propagate channel-path vary operation to subchannels + * @chpid: channl-path ID + * @on: non-zero for vary online, zero for vary offline */ -static int s390_vary_chpid(struct chp_id chpid, int on) +int chsc_chp_vary(struct chp_id chpid, int on) { - char dbf_text[15]; - int status; - - sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid, - chpid.id); - CIO_TRACE_EVENT( 2, dbf_text); - - status = get_chp_status(chpid); - if (status < 0) { - printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n", - chpid.cssid, chpid.id); - return -EINVAL; - } - - if (!on && !status) { - printk(KERN_ERR "chpid %x.%02x is already offline\n", - chpid.cssid, chpid.id); - return -EINVAL; - } - - set_chp_logically_online(chpid, on); - /* * Redo PathVerification on the devices the chpid connects to */ @@ -887,112 +789,6 @@ static int s390_vary_chpid(struct chp_id chpid, int on) return 0; } -/* - * Channel measurement related functions - */ -static ssize_t -chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off, - size_t count) -{ - struct channel_path *chp; - unsigned int size; - - chp = to_channelpath(container_of(kobj, struct device, kobj)); - if (!chp->cmg_chars) - return 0; - - size = sizeof(struct cmg_chars); - - if (off > size) - return 0; - if (off + count > size) - count = size - off; - memcpy(buf, chp->cmg_chars + off, count); - return count; -} - -static struct bin_attribute chp_measurement_chars_attr = { - .attr = { - .name = "measurement_chars", - .mode = S_IRUSR, - .owner = THIS_MODULE, - }, - .size = sizeof(struct cmg_chars), - .read = chp_measurement_chars_read, -}; - -static void chp_measurement_copy_block(struct cmg_entry *buf, - struct channel_subsystem *css, struct chp_id chpid) -{ - void *area; - struct cmg_entry *entry, reference_buf; - int idx; - - if (chpid.id < 128) { - area = css->cub_addr1; - idx = chpid.id; - } else { - area = css->cub_addr2; - idx = chpid.id - 128; - } - entry = area + (idx * sizeof(struct cmg_entry)); - do { - memcpy(buf, entry, sizeof(*entry)); - memcpy(&reference_buf, entry, sizeof(*entry)); - } while (reference_buf.values[0] != buf->values[0]); -} - -static ssize_t -chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count) -{ - struct channel_path *chp; - struct channel_subsystem *css; - unsigned int size; - - chp = to_channelpath(container_of(kobj, struct device, kobj)); - css = to_css(chp->dev.parent); - - size = sizeof(struct cmg_entry); - - /* Only allow single reads. */ - if (off || count < size) - return 0; - chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid); - count = size; - return count; -} - -static struct bin_attribute chp_measurement_attr = { - .attr = { - .name = "measurement", - .mode = S_IRUSR, - .owner = THIS_MODULE, - }, - .size = sizeof(struct cmg_entry), - .read = chp_measurement_read, -}; - -static void -chsc_remove_chp_cmg_attr(struct channel_path *chp) -{ - device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); - device_remove_bin_file(&chp->dev, &chp_measurement_attr); -} - -static int -chsc_add_chp_cmg_attr(struct channel_path *chp) -{ - int ret; - - ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); - if (ret) - return ret; - ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); - if (ret) - device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); - return ret; -} - static void chsc_remove_cmg_attr(struct channel_subsystem *css) { @@ -1001,7 +797,7 @@ chsc_remove_cmg_attr(struct channel_subsystem *css) for (i = 0; i <= __MAX_CHPID; i++) { if (!css->chps[i]) continue; - chsc_remove_chp_cmg_attr(css->chps[i]); + chp_remove_cmg_attr(css->chps[i]); } } @@ -1014,7 +810,7 @@ chsc_add_cmg_attr(struct channel_subsystem *css) for (i = 0; i <= __MAX_CHPID; i++) { if (!css->chps[i]) continue; - ret = chsc_add_chp_cmg_attr(css->chps[i]); + ret = chp_add_cmg_attr(css->chps[i]); if (ret) goto cleanup; } @@ -1023,12 +819,11 @@ cleanup: for (--i; i >= 0; i--) { if (!css->chps[i]) continue; - chsc_remove_chp_cmg_attr(css->chps[i]); + chp_remove_cmg_attr(css->chps[i]); } return ret; } - static int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) { @@ -1143,108 +938,8 @@ chsc_secm(struct channel_subsystem *css, int enable) return ret; } -/* - * Files for the channel path entries. - */ -static ssize_t -chp_status_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = container_of(dev, struct channel_path, dev); - - if (!chp) - return 0; - return (get_chp_status(chp->chpid) ? sprintf(buf, "online\n") : - sprintf(buf, "offline\n")); -} - -static ssize_t -chp_status_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct channel_path *cp = container_of(dev, struct channel_path, dev); - char cmd[10]; - int num_args; - int error; - - num_args = sscanf(buf, "%5s", cmd); - if (!num_args) - return count; - - if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) - error = s390_vary_chpid(cp->chpid, 1); - else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) - error = s390_vary_chpid(cp->chpid, 0); - else - error = -EINVAL; - - return error < 0 ? error : count; - -} - -static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); - -static ssize_t -chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = container_of(dev, struct channel_path, dev); - - if (!chp) - return 0; - return sprintf(buf, "%x\n", chp->desc.desc); -} - -static DEVICE_ATTR(type, 0444, chp_type_show, NULL); - -static ssize_t -chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = to_channelpath(dev); - - if (!chp) - return 0; - if (chp->cmg == -1) /* channel measurements not available */ - return sprintf(buf, "unknown\n"); - return sprintf(buf, "%x\n", chp->cmg); -} - -static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); - -static ssize_t -chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = to_channelpath(dev); - - if (!chp) - return 0; - if (chp->shared == -1) /* channel measurements not available */ - return sprintf(buf, "unknown\n"); - return sprintf(buf, "%x\n", chp->shared); -} - -static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); - -static struct attribute * chp_attrs[] = { - &dev_attr_status.attr, - &dev_attr_type.attr, - &dev_attr_cmg.attr, - &dev_attr_shared.attr, - NULL, -}; - -static struct attribute_group chp_attr_group = { - .attrs = chp_attrs, -}; - -static void -chp_release(struct device *dev) -{ - struct channel_path *cp; - - cp = container_of(dev, struct channel_path, dev); - kfree(cp); -} - -static int chsc_determine_channel_path_description(struct chp_id chpid, - struct channel_path_desc *desc) +int chsc_determine_channel_path_description(struct chp_id chpid, + struct channel_path_desc *desc) { int ccode, ret; @@ -1331,8 +1026,7 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, } } -static int -chsc_get_channel_measurement_chars(struct channel_path *chp) +int chsc_get_channel_measurement_chars(struct channel_path *chp) { int ccode, ret; @@ -1407,97 +1101,6 @@ out: return ret; } -/* - * Entries for chpids on the system bus. - * This replaces /proc/chpids. - */ -static int new_channel_path(struct chp_id chpid) -{ - struct channel_path *chp; - int ret; - - chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); - if (!chp) - return -ENOMEM; - - /* fill in status, etc. */ - chp->chpid = chpid; - chp->state = 1; - chp->dev.parent = &css[chpid.cssid]->device; - chp->dev.release = chp_release; - snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid, - chpid.id); - - /* Obtain channel path description and fill it in. */ - ret = chsc_determine_channel_path_description(chpid, &chp->desc); - if (ret) - goto out_free; - /* Get channel-measurement characteristics. */ - if (css_characteristics_avail && css_chsc_characteristics.scmc - && css_chsc_characteristics.secm) { - ret = chsc_get_channel_measurement_chars(chp); - if (ret) - goto out_free; - } else { - static int msg_done; - - if (!msg_done) { - printk(KERN_WARNING "cio: Channel measurements not " - "available, continuing.\n"); - msg_done = 1; - } - chp->cmg = -1; - } - - /* make it known to the system */ - ret = device_register(&chp->dev); - if (ret) { - printk(KERN_WARNING "%s: could not register %x.%02x\n", - __func__, chpid.cssid, chpid.id); - goto out_free; - } - ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); - if (ret) { - device_unregister(&chp->dev); - goto out_free; - } - mutex_lock(&css[chpid.cssid]->mutex); - if (css[chpid.cssid]->cm_enabled) { - ret = chsc_add_chp_cmg_attr(chp); - if (ret) { - sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); - device_unregister(&chp->dev); - mutex_unlock(&css[chpid.cssid]->mutex); - goto out_free; - } - } - css[chpid.cssid]->chps[chpid.id] = chp; - mutex_unlock(&css[chpid.cssid]->mutex); - return ret; -out_free: - kfree(chp); - return ret; -} - -void * -chsc_get_chp_desc(struct subchannel *sch, int chp_no) -{ - struct channel_path *chp; - struct channel_path_desc *desc; - struct chp_id chpid; - - chp_id_init(&chpid); - chpid.id = sch->schib.pmcw.chpid[chp_no]; - chp = chpid_to_chp(chpid); - if (!chp) - return NULL; - desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); - if (!desc) - return NULL; - memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); - return desc; -} - static int __init chsc_alloc_sei_area(void) { diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 2949c85b6d9..0e40defc608 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -5,10 +5,6 @@ #include #include "chpid.h" -#define CHSC_SEI_ACC_CHPID 1 -#define CHSC_SEI_ACC_LINKADDR 2 -#define CHSC_SEI_ACC_FULLLINKADDR 3 - #define CHSC_SDA_OC_MSS 0x2 struct chsc_header { @@ -37,23 +33,10 @@ struct channel_path_desc { u8 chpp; } __attribute__ ((packed)); -struct channel_path { - struct chp_id chpid; - int state; - struct channel_path_desc desc; - /* Channel-measurement related stuff: */ - int cmg; - int shared; - void *cmg_chars; - struct device dev; -}; +struct channel_path; -extern void s390_process_css( void ); -extern void chsc_validate_chpids(struct subchannel *); -extern void chpid_is_actually_online(struct chp_id); extern int css_get_ssd_info(struct subchannel *); extern int chsc_process_crw(void); -extern int chp_process_crw(int, int); struct css_general_char { u64 : 41; @@ -89,12 +72,15 @@ extern struct css_chsc_char css_chsc_characteristics; extern int chsc_determine_css_characteristics(void); extern int css_characteristics_avail; -extern void *chsc_get_chp_desc(struct subchannel*, int); - extern int chsc_enable_facility(int); struct channel_subsystem; extern int chsc_secm(struct channel_subsystem *, int); -#define to_channelpath(device) container_of(device, struct channel_path, dev) +int chsc_chp_vary(struct chp_id chpid, int on); +int chsc_determine_channel_path_description(struct chp_id chpid, + struct channel_path_desc *desc); +int chsc_chp_online(struct chp_id chpid); +void chsc_chp_offline(struct chp_id chpid); +int chsc_get_channel_measurement_chars(struct channel_path *chp); #endif diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index bab729202f4..7dd0649c95d 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -29,6 +29,7 @@ #include "ioasm.h" #include "blacklist.h" #include "cio_debug.h" +#include "chp.h" #include "../s390mach.h" debug_info_t *cio_debug_msg_id; @@ -592,9 +593,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) err = -ENODEV; goto out; } - sch->opm = 0xff; - if (!cio_is_console(sch->schid)) - chsc_validate_chpids(sch); + if (cio_is_console(sch->schid)) + sch->opm = 0xff; + else + sch->opm = chp_get_sch_opm(sch); sch->lpm = sch->schib.pmcw.pam & sch->opm; CIO_DEBUG(KERN_INFO, 0, diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index ca2bab932a8..4319e1ced79 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 5f5ee1eef07..db3d1b990f5 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -23,6 +23,7 @@ #include "chsc.h" #include "ioasm.h" #include "chpid.h" +#include "chp.h" int device_is_online(struct subchannel *sch) @@ -221,7 +222,10 @@ __recover_lost_chpids(struct subchannel *sch, int old_lpm) if (old_lpm & mask) continue; chpid.id = sch->schib.pmcw.chpid[i]; - chpid_is_actually_online(chpid); + if (!chp_is_registered(chpid)) { + need_rescan = 1; + queue_work(slow_path_wq, &slow_path_work); + } } } diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 7c7775aae38..d819ae2ee9a 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -22,6 +22,8 @@ #include "css.h" #include "chsc.h" #include "device.h" +#include "chpid.h" +#include "chp.h" int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) { @@ -606,9 +608,12 @@ void * ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) { struct subchannel *sch; + struct chp_id chpid; sch = to_subchannel(cdev->dev.parent); - return chsc_get_chp_desc(sch, chp_no); + chp_id_init(&chpid); + chpid.id = sch->schib.pmcw.chpid[chp_no]; + return chp_get_chp_desc(chpid); } // FIXME: these have to go: -- cgit From c9182e0f42c5646e670c2166b6d6638052d574af Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:29 +0200 Subject: [S390] cio: observe chpid valid flag Check validity flag of CHPID description data before continuing with channel-path initialization. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chp.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 6e04521accd..8a5839147f8 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -339,6 +339,10 @@ int chp_new(struct chp_id chpid) ret = chsc_determine_channel_path_description(chpid, &chp->desc); if (ret) goto out_free; + if ((chp->desc.flags & 0x80) == 0) { + ret = -ENODEV; + goto out_free; + } /* Get channel-measurement characteristics. */ if (css_characteristics_avail && css_chsc_characteristics.scmc && css_chsc_characteristics.secm) { -- cgit From f5ba6c863617c15d22cce5f8666ff4c832773025 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 27 Apr 2007 16:01:30 +0200 Subject: [S390] cio: Clean up online_store. Detangle the online_store code and make it more readable. Signed-off-by: Cornelia Huck Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 109 ++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 47 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e322111fb36..c3fc205e3bc 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -413,11 +413,60 @@ ccw_device_set_online(struct ccw_device *cdev) return (ret == 0) ? -ENODEV : ret; } -static ssize_t -online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static void online_store_handle_offline(struct ccw_device *cdev) +{ + if (cdev->private->state == DEV_STATE_DISCONNECTED) + ccw_device_remove_disconnected(cdev); + else if (cdev->drv && cdev->drv->set_offline) + ccw_device_set_offline(cdev); +} + +static int online_store_recog_and_online(struct ccw_device *cdev) +{ + int ret; + + /* Do device recognition, if needed. */ + if (cdev->id.cu_type == 0) { + ret = ccw_device_recognition(cdev); + if (ret) { + printk(KERN_WARNING"Couldn't start recognition " + "for device %s (ret=%d)\n", + cdev->dev.bus_id, ret); + return ret; + } + wait_event(cdev->private->wait_q, + cdev->private->flags.recog_done); + } + if (cdev->drv && cdev->drv->set_online) + ccw_device_set_online(cdev); + return 0; +} +static void online_store_handle_online(struct ccw_device *cdev, int force) +{ + int ret; + + ret = online_store_recog_and_online(cdev); + if (ret) + return; + if (force && cdev->private->state == DEV_STATE_BOXED) { + ret = ccw_device_stlck(cdev); + if (ret) { + printk(KERN_WARNING"ccw_device_stlck for device %s " + "returned %d!\n", cdev->dev.bus_id, ret); + return; + } + if (cdev->id.cu_type == 0) + cdev->private->state = DEV_STATE_NOT_OPER; + online_store_recog_and_online(cdev); + } + +} + +static ssize_t online_store (struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct ccw_device *cdev = to_ccwdev(dev); - int i, force, ret; + int i, force; char *tmp; if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) @@ -434,51 +483,17 @@ online_store (struct device *dev, struct device_attribute *attr, const char *buf force = 0; i = simple_strtoul(buf, &tmp, 16); } - if (i == 1) { - /* Do device recognition, if needed. */ - if (cdev->id.cu_type == 0) { - ret = ccw_device_recognition(cdev); - if (ret) { - printk(KERN_WARNING"Couldn't start recognition " - "for device %s (ret=%d)\n", - cdev->dev.bus_id, ret); - goto out; - } - wait_event(cdev->private->wait_q, - cdev->private->flags.recog_done); - } - if (cdev->drv && cdev->drv->set_online) - ccw_device_set_online(cdev); - } else if (i == 0) { - if (cdev->private->state == DEV_STATE_DISCONNECTED) - ccw_device_remove_disconnected(cdev); - else if (cdev->drv && cdev->drv->set_offline) - ccw_device_set_offline(cdev); - } - if (force && cdev->private->state == DEV_STATE_BOXED) { - ret = ccw_device_stlck(cdev); - if (ret) { - printk(KERN_WARNING"ccw_device_stlck for device %s " - "returned %d!\n", cdev->dev.bus_id, ret); - goto out; - } - /* Do device recognition, if needed. */ - if (cdev->id.cu_type == 0) { - cdev->private->state = DEV_STATE_NOT_OPER; - ret = ccw_device_recognition(cdev); - if (ret) { - printk(KERN_WARNING"Couldn't start recognition " - "for device %s (ret=%d)\n", - cdev->dev.bus_id, ret); - goto out; - } - wait_event(cdev->private->wait_q, - cdev->private->flags.recog_done); - } - if (cdev->drv && cdev->drv->set_online) - ccw_device_set_online(cdev); + + switch (i) { + case 0: + online_store_handle_offline(cdev); + break; + case 1: + online_store_handle_online(cdev, force); + break; + default: + count = -EINVAL; } - out: if (cdev->drv) module_put(cdev->drv->owner); atomic_set(&cdev->private->onoff, 0); -- cgit From e5854a5839fa426a7873f038080f63587de5f1f1 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:31 +0200 Subject: [S390] cio: Channel-path configure function. Add a new attribute to the channel-path sysfs directory through which channel-path configure operations can be triggered. Also listen for hardware events requesting channel-path configure operations and process them accordingly. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/cio/chp.c | 250 +++++++++++++++++++++++++++++++++++++++++- drivers/s390/cio/chp.h | 20 +++- drivers/s390/cio/chpid.h | 51 --------- drivers/s390/cio/chsc.c | 44 +++++++- drivers/s390/cio/chsc.h | 2 +- drivers/s390/cio/cio.c | 1 + drivers/s390/cio/cio.h | 1 + drivers/s390/cio/css.h | 4 +- drivers/s390/cio/device_fsm.c | 2 +- drivers/s390/cio/device_ops.c | 2 +- drivers/s390/cio/ioasm.h | 2 +- 11 files changed, 318 insertions(+), 61 deletions(-) delete mode 100644 drivers/s390/cio/chpid.h (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 8a5839147f8..0e92c8c8986 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -10,9 +10,14 @@ #include #include #include +#include +#include +#include +#include #include +#include +#include -#include "chpid.h" #include "cio.h" #include "css.h" #include "ioasm.h" @@ -20,6 +25,32 @@ #include "chp.h" #define to_channelpath(device) container_of(device, struct channel_path, dev) +#define CHP_INFO_UPDATE_INTERVAL 1*HZ + +enum cfg_task_t { + cfg_none, + cfg_configure, + cfg_deconfigure +}; + +/* Map for pending configure tasks. */ +static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1]; +static DEFINE_MUTEX(cfg_lock); +static int cfg_busy; + +/* Map for channel-path status. */ +static struct sclp_chp_info chp_info; +static DEFINE_MUTEX(info_lock); + +/* Time after which channel-path status may be outdated. */ +static unsigned long chp_info_expires; + +/* Workqueue to perform pending configure tasks. */ +static struct workqueue_struct *chp_wq; +static struct work_struct cfg_work; + +/* Wait queue for configure completion events. */ +static wait_queue_head_t cfg_wait_queue; /* Return channel_path struct for given chpid. */ static inline struct channel_path *chpid_to_chp(struct chp_id chpid) @@ -251,6 +282,43 @@ static ssize_t chp_status_write(struct device *dev, static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); +static ssize_t chp_configure_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *cp; + int status; + + cp = container_of(dev, struct channel_path, dev); + status = chp_info_get_status(cp->chpid); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} + +static int cfg_wait_idle(void); + +static ssize_t chp_configure_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct channel_path *cp; + int val; + char delim; + + if (sscanf(buf, "%d %c", &val, &delim) != 1) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + cp = container_of(dev, struct channel_path, dev); + chp_cfg_schedule(cp->chpid, val); + cfg_wait_idle(); + + return count; +} + +static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write); + static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -293,6 +361,7 @@ static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); static struct attribute * chp_attrs[] = { &dev_attr_status.attr, + &dev_attr_configure.attr, &dev_attr_type.attr, &dev_attr_cmg.attr, &dev_attr_shared.attr, @@ -323,6 +392,8 @@ int chp_new(struct chp_id chpid) struct channel_path *chp; int ret; + if (chp_is_registered(chpid)) + return 0; chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); if (!chp) return -ENOMEM; @@ -435,3 +506,180 @@ int chp_process_crw(int id, int status) return 0; } } + +static inline int info_bit_num(struct chp_id id) +{ + return id.id + id.cssid * (__MAX_CHPID + 1); +} + +/* Force chp_info refresh on next call to info_validate(). */ +static void info_expire(void) +{ + mutex_lock(&info_lock); + chp_info_expires = jiffies - 1; + mutex_unlock(&info_lock); +} + +/* Ensure that chp_info is up-to-date. */ +static int info_update(void) +{ + int rc; + + mutex_lock(&info_lock); + rc = 0; + if (time_after(jiffies, chp_info_expires)) { + /* Data is too old, update. */ + rc = sclp_chp_read_info(&chp_info); + chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ; + } + mutex_unlock(&info_lock); + + return rc; +} + +/** + * chp_info_get_status - retrieve configure status of a channel-path + * @chpid: channel-path ID + * + * On success, return 0 for standby, 1 for configured, 2 for reserved, + * 3 for not recognized. Return negative error code on error. + */ +int chp_info_get_status(struct chp_id chpid) +{ + int rc; + int bit; + + rc = info_update(); + if (rc) + return rc; + + bit = info_bit_num(chpid); + mutex_lock(&info_lock); + if (!chp_test_bit(chp_info.recognized, bit)) + rc = CHP_STATUS_NOT_RECOGNIZED; + else if (chp_test_bit(chp_info.configured, bit)) + rc = CHP_STATUS_CONFIGURED; + else if (chp_test_bit(chp_info.standby, bit)) + rc = CHP_STATUS_STANDBY; + else + rc = CHP_STATUS_RESERVED; + mutex_unlock(&info_lock); + + return rc; +} + +/* Return configure task for chpid. */ +static enum cfg_task_t cfg_get_task(struct chp_id chpid) +{ + return chp_cfg_task[chpid.cssid][chpid.id]; +} + +/* Set configure task for chpid. */ +static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg) +{ + chp_cfg_task[chpid.cssid][chpid.id] = cfg; +} + +/* Perform one configure/deconfigure request. Reschedule work function until + * last request. */ +static void cfg_func(struct work_struct *work) +{ + struct chp_id chpid; + enum cfg_task_t t; + + mutex_lock(&cfg_lock); + t = cfg_none; + chp_id_for_each(&chpid) { + t = cfg_get_task(chpid); + if (t != cfg_none) { + cfg_set_task(chpid, cfg_none); + break; + } + } + mutex_unlock(&cfg_lock); + + switch (t) { + case cfg_configure: + sclp_chp_configure(chpid); + info_expire(); + chsc_chp_online(chpid); + break; + case cfg_deconfigure: + sclp_chp_deconfigure(chpid); + info_expire(); + chsc_chp_offline(chpid); + break; + case cfg_none: + /* Get updated information after last change. */ + info_update(); + mutex_lock(&cfg_lock); + cfg_busy = 0; + mutex_unlock(&cfg_lock); + wake_up_interruptible(&cfg_wait_queue); + return; + } + queue_work(chp_wq, &cfg_work); +} + +/** + * chp_cfg_schedule - schedule chpid configuration request + * @chpid - channel-path ID + * @configure - Non-zero for configure, zero for deconfigure + * + * Schedule a channel-path configuration/deconfiguration request. + */ +void chp_cfg_schedule(struct chp_id chpid, int configure) +{ + CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id, + configure); + mutex_lock(&cfg_lock); + cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure); + cfg_busy = 1; + mutex_unlock(&cfg_lock); + queue_work(chp_wq, &cfg_work); +} + +/** + * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request + * @chpid - channel-path ID + * + * Cancel an active channel-path deconfiguration request if it has not yet + * been performed. + */ +void chp_cfg_cancel_deconfigure(struct chp_id chpid) +{ + CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id); + mutex_lock(&cfg_lock); + if (cfg_get_task(chpid) == cfg_deconfigure) + cfg_set_task(chpid, cfg_none); + mutex_unlock(&cfg_lock); +} + +static int cfg_wait_idle(void) +{ + if (wait_event_interruptible(cfg_wait_queue, !cfg_busy)) + return -ERESTARTSYS; + return 0; +} + +static int __init chp_init(void) +{ + struct chp_id chpid; + + chp_wq = create_singlethread_workqueue("cio_chp"); + if (!chp_wq) + return -ENOMEM; + INIT_WORK(&cfg_work, cfg_func); + init_waitqueue_head(&cfg_wait_queue); + if (info_update()) + return 0; + /* Register available channel-paths. */ + chp_id_for_each(&chpid) { + if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED) + chp_new(chpid); + } + + return 0; +} + +subsys_initcall(chp_init); diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index ac2b1a9c3bc..862af69d970 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -10,10 +10,23 @@ #include #include - -#include "chpid.h" +#include #include "chsc.h" +#define CHP_STATUS_STANDBY 0 +#define CHP_STATUS_CONFIGURED 1 +#define CHP_STATUS_RESERVED 2 +#define CHP_STATUS_NOT_RECOGNIZED 3 + +static inline int chp_test_bit(u8 *bitmap, int num) +{ + int byte = num >> 3; + int mask = 128 >> (num & 7); + + return (bitmap[byte] & mask) ? 1 : 0; +} + + struct channel_path { struct chp_id chpid; int state; @@ -33,5 +46,8 @@ int chp_process_crw(int id, int available); void chp_remove_cmg_attr(struct channel_path *chp); int chp_add_cmg_attr(struct channel_path *chp); int chp_new(struct chp_id chpid); +void chp_cfg_schedule(struct chp_id chpid, int configure); +void chp_cfg_cancel_deconfigure(struct chp_id chpid); +int chp_info_get_status(struct chp_id chpid); #endif /* S390_CHP_H */ diff --git a/drivers/s390/cio/chpid.h b/drivers/s390/cio/chpid.h deleted file mode 100644 index 44cc00ed9b5..00000000000 --- a/drivers/s390/cio/chpid.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * drivers/s390/cio/chpid.h - * - * Copyright IBM Corp. 2007 - * Author(s): Peter Oberparleiter - */ - -#ifndef S390_CHP_ID_H -#define S390_CHP_ID_H S390_CHP_ID_H - -#include -#include -#include "css.h" - -struct chp_id { - u8 reserved1; - u8 cssid; - u8 reserved2; - u8 id; -} __attribute__((packed)); - -static inline void chp_id_init(struct chp_id *chpid) -{ - memset(chpid, 0, sizeof(struct chp_id)); -} - -static inline int chp_id_is_equal(struct chp_id *a, struct chp_id *b) -{ - return (a->id == b->id) && (a->cssid == b->cssid); -} - -static inline void chp_id_next(struct chp_id *chpid) -{ - if (chpid->id < __MAX_CHPID) - chpid->id++; - else { - chpid->id = 0; - chpid->cssid++; - } -} - -static inline int chp_id_is_valid(struct chp_id *chpid) -{ - return (chpid->cssid <= __MAX_CSSID); -} - - -#define chp_id_for_each(c) \ - for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c)) - -#endif /* S390_CHP_ID_H */ diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index d99f525eac0..3dec460bba2 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -15,12 +15,12 @@ #include #include +#include #include "css.h" #include "cio.h" #include "cio_debug.h" #include "ioasm.h" -#include "chpid.h" #include "chp.h" #include "chsc.h" @@ -498,6 +498,45 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) return rc; } +struct chp_config_data { + u8 map[32]; + u8 op; + u8 pc; +}; + +static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) +{ + struct chp_config_data *data; + struct chp_id chpid; + int num; + + CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); + if (sei_area->rs != 0) + return 0; + data = (struct chp_config_data *) &(sei_area->ccdf); + chp_id_init(&chpid); + for (num = 0; num <= __MAX_CHPID; num++) { + 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); + switch (data->op) { + case 0: + chp_cfg_schedule(chpid, 1); + break; + case 1: + chp_cfg_schedule(chpid, 0); + break; + case 2: + chp_cfg_cancel_deconfigure(chpid); + break; + } + } + + return 0; +} + static int chsc_process_sei(struct chsc_sei_area *sei_area) { int rc; @@ -514,6 +553,9 @@ static int chsc_process_sei(struct chsc_sei_area *sei_area) case 2: /* i/o resource accessibiliy */ rc = chsc_process_sei_res_acc(sei_area); break; + case 8: /* channel-path-configuration notification */ + rc = chsc_process_sei_chp_config(sei_area); + break; default: /* other stuff */ CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", sei_area->cc); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 0e40defc608..322586f27cc 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -3,7 +3,7 @@ #include #include -#include "chpid.h" +#include #define CHSC_SDA_OC_MSS 0x2 diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 7dd0649c95d..ea1defba569 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "airq.h" #include "cio.h" #include "css.h" diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 35154a21035..e62ab5c5286 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -3,6 +3,7 @@ #include "schid.h" #include +#include /* * where we put the ssd info diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 4319e1ced79..b2b1a265c60 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -5,8 +5,10 @@ #include #include #include +#include #include +#include #include "schid.h" @@ -149,8 +151,6 @@ extern void css_reiterate_subchannels(void); #define __MAX_SUBCHANNEL 65535 #define __MAX_SSID 3 -#define __MAX_CHPID 255 -#define __MAX_CSSID 0 struct channel_subsystem { u8 cssid; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index db3d1b990f5..d6226881d0d 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -15,6 +15,7 @@ #include #include +#include #include "cio.h" #include "cio_debug.h" @@ -22,7 +23,6 @@ #include "device.h" #include "chsc.h" #include "ioasm.h" -#include "chpid.h" #include "chp.h" int diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index d819ae2ee9a..16f59fcb66b 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -16,13 +16,13 @@ #include #include +#include #include "cio.h" #include "cio_debug.h" #include "css.h" #include "chsc.h" #include "device.h" -#include "chpid.h" #include "chp.h" int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index 78c7db6daa8..7153dd95908 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -1,8 +1,8 @@ #ifndef S390_CIO_IOASM_H #define S390_CIO_IOASM_H +#include #include "schid.h" -#include "chpid.h" /* * TPI info structure -- cgit From cfbe9bb2fb5de1da58d351432a9465c22d6d3ee5 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 27 Apr 2007 16:01:32 +0200 Subject: [S390] cio: Use add_uevent_var. Convert ccw_uevent to use add_uevent_var and adapt snprint_alias. Signed-off-by: Cornelia Huck Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 66 +++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 40 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index c3fc205e3bc..34e7d77b997 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -56,13 +56,12 @@ ccw_bus_match (struct device * dev, struct device_driver * drv) /* Store modalias string delimited by prefix/suffix string into buffer with * specified size. Return length of resulting string (excluding trailing '\0') * even if string doesn't fit buffer (snprintf semantics). */ -static int snprint_alias(char *buf, size_t size, const char *prefix, +static int snprint_alias(char *buf, size_t size, struct ccw_device_id *id, const char *suffix) { int len; - len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type, - id->cu_model); + len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model); if (len > size) return len; buf += len; @@ -85,53 +84,40 @@ static int ccw_uevent(struct device *dev, char **envp, int num_envp, struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); int i = 0; - int len; + int len = 0; + int ret; + char modalias_buf[30]; /* CU_TYPE= */ - len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "CU_TYPE=%04X", id->cu_type); + if (ret) + return ret; /* CU_MODEL= */ - len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "CU_MODEL=%02X", id->cu_model); + if (ret) + return ret; /* The next two can be zero, that's ok for us */ /* DEV_TYPE= */ - len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "DEV_TYPE=%04X", id->dev_type); + if (ret) + return ret; /* DEV_MODEL= */ - len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X", - (unsigned char) id->dev_model) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "DEV_MODEL=%02X", id->dev_model); + if (ret) + return ret; /* MODALIAS= */ - len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; - - envp[i] = NULL; - - return 0; + snprint_alias(modalias_buf, sizeof(modalias_buf), id, ""); + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "MODALIAS=%s", modalias_buf); + return ret; } struct bus_type ccw_bus_type; @@ -280,7 +266,7 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf) struct ccw_device_id *id = &(cdev->id); int len; - len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1; + len = snprint_alias(buf, PAGE_SIZE, id, "\n") + 1; return len > PAGE_SIZE ? PAGE_SIZE : len; } -- cgit From 387b734fc2b55f776b192c7afdfd892ba42347d4 Mon Sep 17 00:00:00 2001 From: Stefan Bader Date: Fri, 27 Apr 2007 16:01:33 +0200 Subject: [S390] cio: Re-start path verification after aborting internal I/O. Path verification triggered by changes to the available CHPIDs will be interrupted by another change but not re-started. This results in an invalid path mask. To solve this make sure to completely re-start path verification when changing the available paths. Signed-off-by: Stefan Bader Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 108 ++++++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 54 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 3dec460bba2..02615eb4398 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -178,6 +178,38 @@ css_get_ssd_info(struct subchannel *sch) return ret; } +static int check_for_io_on_path(struct subchannel *sch, int mask) +{ + int cc; + + cc = stsch(sch->schid, &sch->schib); + if (cc) + return 0; + if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask) + return 1; + return 0; +} + +static void terminate_internal_io(struct subchannel *sch) +{ + if (cio_clear(sch)) { + /* Recheck device in case clear failed. */ + sch->lpm = 0; + if (device_trigger_verify(sch) != 0) { + if(css_enqueue_subchannel_slow(sch->schid)) { + css_clear_subchannel_slow_list(); + need_rescan = 1; + } + } + return; + } + /* Request retry of internal operation. */ + device_set_intretry(sch); + /* Call handler. */ + if (sch->driver && sch->driver->termination) + sch->driver->termination(&sch->dev); +} + static int s390_subchannel_remove_chpid(struct device *dev, void *data) { @@ -208,37 +240,33 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) if (sch->schib.pmcw.pim == 0x80) goto out_unreg; - if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && - (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && - (sch->schib.pmcw.lpum == mask)) { - int cc; - - cc = cio_clear(sch); - if (cc == -ENODEV) + if (check_for_io_on_path(sch, mask)) { + if (device_is_online(sch)) + device_kill_io(sch); + else { + terminate_internal_io(sch); + /* Re-start path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); + } + } else { + /* trigger path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); + else if (sch->lpm == mask) goto out_unreg; - /* Request retry of internal operation. */ - device_set_intretry(sch); - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); - goto out_unlock; } - /* trigger path verification. */ - if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); - else if (sch->lpm == mask) - goto out_unreg; -out_unlock: spin_unlock_irq(sch->lock); return 0; + out_unreg: - spin_unlock_irq(sch->lock); sch->lpm = 0; if (css_enqueue_subchannel_slow(sch->schid)) { css_clear_subchannel_slow_list(); need_rescan = 1; } + spin_unlock_irq(sch->lock); return 0; } @@ -683,38 +711,6 @@ int chsc_chp_online(struct chp_id chpid) return rc; } -static int check_for_io_on_path(struct subchannel *sch, int index) -{ - int cc; - - cc = stsch(sch->schid, &sch->schib); - if (cc) - return 0; - if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index)) - return 1; - return 0; -} - -static void terminate_internal_io(struct subchannel *sch) -{ - if (cio_clear(sch)) { - /* Recheck device in case clear failed. */ - sch->lpm = 0; - if (device_trigger_verify(sch) != 0) { - if(css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } - return; - } - /* Request retry of internal operation. */ - device_set_intretry(sch); - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); -} - static void __s390_subchannel_vary_chpid(struct subchannel *sch, struct chp_id chpid, int on) { @@ -741,13 +737,17 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, } sch->opm &= ~(0x80 >> chp); sch->lpm &= ~(0x80 >> chp); - if (check_for_io_on_path(sch, chp)) { + if (check_for_io_on_path(sch, (0x80 >> chp))) { if (device_is_online(sch)) /* Path verification is done after killing. */ device_kill_io(sch); - else + else { /* Kill and retry internal I/O. */ terminate_internal_io(sch); + /* Re-start path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); + } } else if (!sch->lpm) { if (device_trigger_verify(sch) != 0) { if (css_enqueue_subchannel_slow(sch->schid)) { -- cgit From 83b3370c79b91b9be3f6540c3c914e689134b45f Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:34 +0200 Subject: [S390] cio: replace subchannel evaluation queue with bitmap Use a bitmap for indicating which subchannels require evaluation instead of allocating memory for each evaluation request. This approach reduces memory consumption during recovery in case of massive evaluation request occurrence and removes the need for memory allocation failure handling. Cc: Heiko Carstens Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/Makefile | 2 +- drivers/s390/cio/chp.c | 8 +-- drivers/s390/cio/chp.h | 2 +- drivers/s390/cio/chsc.c | 130 ++++++++++--------------------------- drivers/s390/cio/chsc.h | 4 +- drivers/s390/cio/css.c | 148 ++++++++++++++---------------------------- drivers/s390/cio/css.h | 10 +-- drivers/s390/cio/device_fsm.c | 6 +- drivers/s390/cio/idset.c | 112 ++++++++++++++++++++++++++++++++ drivers/s390/cio/idset.h | 25 +++++++ 10 files changed, 229 insertions(+), 218 deletions(-) create mode 100644 drivers/s390/cio/idset.c create mode 100644 drivers/s390/cio/idset.h (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index fe7b3ffa1ea..cfaf77b320f 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -2,7 +2,7 @@ # Makefile for the S/390 common i/o drivers # -obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o +obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 0e92c8c8986..ac289e6eadf 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -491,7 +491,7 @@ void *chp_get_chp_desc(struct chp_id chpid) * Handle channel-report-words indicating that the status of a channel-path * has changed. */ -int chp_process_crw(int id, int status) +void chp_process_crw(int id, int status) { struct chp_id chpid; @@ -500,11 +500,9 @@ int chp_process_crw(int id, int status) if (status) { if (!chp_is_registered(chpid)) chp_new(chpid); - return chsc_chp_online(chpid); - } else { + chsc_chp_online(chpid); + } else chsc_chp_offline(chpid); - return 0; - } } static inline int info_bit_num(struct chp_id id) diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index 862af69d970..65286563c59 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -42,7 +42,7 @@ int chp_get_status(struct chp_id chpid); u8 chp_get_sch_opm(struct subchannel *sch); int chp_is_registered(struct chp_id chpid); void *chp_get_chp_desc(struct chp_id chpid); -int chp_process_crw(int id, int available); +void chp_process_crw(int id, int available); void chp_remove_cmg_attr(struct channel_path *chp); int chp_add_cmg_attr(struct channel_path *chp); int chp_new(struct chp_id chpid); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 02615eb4398..89a130a6265 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -195,12 +195,8 @@ static void terminate_internal_io(struct subchannel *sch) if (cio_clear(sch)) { /* Recheck device in case clear failed. */ sch->lpm = 0; - if (device_trigger_verify(sch) != 0) { - if(css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } + if (device_trigger_verify(sch) != 0) + css_schedule_eval(sch->schid); return; } /* Request retry of internal operation. */ @@ -262,11 +258,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) out_unreg: sch->lpm = 0; - if (css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } spin_unlock_irq(sch->lock); + css_schedule_eval(sch->schid); return 0; } @@ -281,9 +274,6 @@ void chsc_chp_offline(struct chp_id chpid) return; bus_for_each_dev(&css_bus_type, NULL, &chpid, s390_subchannel_remove_chpid); - - if (need_rescan || css_slow_subchannels_exist()) - queue_work(slow_path_wq, &slow_path_work); } struct res_acc_data { @@ -331,7 +321,6 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid) { struct schib schib; - int ret; /* * We don't know the device yet, but since a path * may be available now to the device we'll have @@ -342,15 +331,10 @@ s390_process_res_acc_new_sch(struct subchannel_id schid) */ if (stsch_err(schid, &schib)) /* We're through */ - return need_rescan ? -EAGAIN : -ENXIO; + return -ENXIO; /* Put it on the slow path. */ - ret = css_enqueue_subchannel_slow(schid); - if (ret) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } @@ -392,10 +376,8 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) } -static int -s390_process_res_acc (struct res_acc_data *res_data) +static void s390_process_res_acc (struct res_acc_data *res_data) { - int rc; char dbf_txt[15]; sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, @@ -413,12 +395,7 @@ s390_process_res_acc (struct res_acc_data *res_data) * The more information we have (info), the less scanning * will we have to do. */ - rc = for_each_subchannel(__s390_process_res_acc, res_data); - if (css_slow_subchannels_exist()) - rc = -EAGAIN; - else if (rc != -EAGAIN) - rc = 0; - return rc; + for_each_subchannel(__s390_process_res_acc, res_data); } static int @@ -470,7 +447,7 @@ struct chsc_sei_area { /* ccdf has to be big enough for a link-incident record */ } __attribute__ ((packed)); -static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) +static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) { struct chp_id chpid; int id; @@ -478,7 +455,7 @@ static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) - return 0; + return; id = __get_chpid_from_lir(sei_area->ccdf); if (id < 0) CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); @@ -487,21 +464,18 @@ static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) chpid.id = id; chsc_chp_offline(chpid); } - - return 0; } -static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) +static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) { struct res_acc_data res_data; struct chp_id chpid; int status; - int rc; CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) - return 0; + return; chp_id_init(&chpid); chpid.id = sei_area->rsid; /* allocate a new channel path structure, if needed */ @@ -509,7 +483,7 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) if (status < 0) chp_new(chpid); else if (!status) - return 0; + return; memset(&res_data, 0, sizeof(struct res_acc_data)); res_data.chpid = chpid; if ((sei_area->vf & 0xc0) != 0) { @@ -521,9 +495,7 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) /* link address */ res_data.fla_mask = 0xff00; } - rc = s390_process_res_acc(&res_data); - - return rc; + s390_process_res_acc(&res_data); } struct chp_config_data { @@ -532,7 +504,7 @@ struct chp_config_data { u8 pc; }; -static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) +static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) { struct chp_config_data *data; struct chp_id chpid; @@ -540,7 +512,7 @@ static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); if (sei_area->rs != 0) - return 0; + return; data = (struct chp_config_data *) &(sei_area->ccdf); chp_id_init(&chpid); for (num = 0; num <= __MAX_CHPID; num++) { @@ -561,52 +533,44 @@ static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) break; } } - - return 0; } -static int chsc_process_sei(struct chsc_sei_area *sei_area) +static void chsc_process_sei(struct chsc_sei_area *sei_area) { - int rc; - /* Check if we might have lost some information. */ - if (sei_area->flags & 0x40) + if (sei_area->flags & 0x40) { CIO_CRW_EVENT(2, "chsc: event overflow\n"); + css_schedule_eval_all(); + } /* which kind of information was stored? */ - rc = 0; switch (sei_area->cc) { case 1: /* link incident*/ - rc = chsc_process_sei_link_incident(sei_area); + chsc_process_sei_link_incident(sei_area); break; case 2: /* i/o resource accessibiliy */ - rc = chsc_process_sei_res_acc(sei_area); + chsc_process_sei_res_acc(sei_area); break; case 8: /* channel-path-configuration notification */ - rc = chsc_process_sei_chp_config(sei_area); + chsc_process_sei_chp_config(sei_area); break; default: /* other stuff */ CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", sei_area->cc); break; } - - return rc; } -int chsc_process_crw(void) +void chsc_process_crw(void) { struct chsc_sei_area *sei_area; - int ret; - int rc; if (!sei_page) - return 0; + return; /* Access to sei_page is serialized through machine check handler * thread, so no need for locking. */ sei_area = sei_page; CIO_TRACE_EVENT( 2, "prcss"); - ret = 0; do { memset(sei_area, 0, sizeof(*sei_area)); sei_area->request.length = 0x0010; @@ -616,37 +580,26 @@ int chsc_process_crw(void) if (sei_area->response.code == 0x0001) { CIO_CRW_EVENT(4, "chsc: sei successful\n"); - rc = chsc_process_sei(sei_area); - if (rc) - ret = rc; + chsc_process_sei(sei_area); } else { CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", sei_area->response.code); - ret = 0; break; } } while (sei_area->flags & 0x80); - - return ret; } static int __chp_add_new_sch(struct subchannel_id schid) { struct schib schib; - int ret; if (stsch_err(schid, &schib)) /* We're through */ - return need_rescan ? -EAGAIN : -ENXIO; + return -ENXIO; /* Put it on the slow path. */ - ret = css_enqueue_subchannel_slow(schid); - if (ret) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } @@ -693,22 +646,15 @@ __chp_add(struct subchannel_id schid, void *data) return 0; } -int chsc_chp_online(struct chp_id chpid) +void chsc_chp_online(struct chp_id chpid) { - int rc; char dbf_txt[15]; sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - if (chp_get_status(chpid) == 0) - return 0; - rc = for_each_subchannel(__chp_add, &chpid); - if (css_slow_subchannels_exist()) - rc = -EAGAIN; - if (rc != -EAGAIN) - rc = 0; - return rc; + if (chp_get_status(chpid) != 0) + for_each_subchannel(__chp_add, &chpid); } static void __s390_subchannel_vary_chpid(struct subchannel *sch, @@ -749,12 +695,8 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, sch->driver->verify(&sch->dev); } } else if (!sch->lpm) { - if (device_trigger_verify(sch) != 0) { - if (css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } + if (device_trigger_verify(sch) != 0) + css_schedule_eval(sch->schid); } else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); break; @@ -801,11 +743,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) /* We're through */ return -ENXIO; /* Put it on the slow path. */ - if (css_enqueue_subchannel_slow(schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } @@ -826,8 +764,6 @@ int chsc_chp_vary(struct chp_id chpid, int on) if (on) /* Scan for new devices on varied on path. */ for_each_subchannel(__s390_vary_chpid_on, NULL); - if (need_rescan || css_slow_subchannels_exist()) - queue_work(slow_path_wq, &slow_path_work); return 0; } diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 322586f27cc..742ef57d2c5 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -36,7 +36,7 @@ struct channel_path_desc { struct channel_path; extern int css_get_ssd_info(struct subchannel *); -extern int chsc_process_crw(void); +extern void chsc_process_crw(void); struct css_general_char { u64 : 41; @@ -79,7 +79,7 @@ extern int chsc_secm(struct channel_subsystem *, int); int chsc_chp_vary(struct chp_id chpid, int on); int chsc_determine_channel_path_description(struct chp_id chpid, struct channel_path_desc *desc); -int chsc_chp_online(struct chp_id chpid); +void chsc_chp_online(struct chp_id chpid); void chsc_chp_offline(struct chp_id chpid); int chsc_get_channel_measurement_chars(struct channel_path *chp); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index fe0ace7aece..fcc641e578f 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -20,8 +20,8 @@ #include "ioasm.h" #include "chsc.h" #include "device.h" +#include "idset.h" -int need_rescan = 0; int css_init_done = 0; static int need_reprobe = 0; static int max_ssid = 0; @@ -306,7 +306,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) return css_probe_device(schid); } -static int css_evaluate_subchannel(struct subchannel_id schid, int slow) +static void css_evaluate_subchannel(struct subchannel_id schid, int slow) { struct subchannel *sch; int ret; @@ -317,53 +317,66 @@ static int css_evaluate_subchannel(struct subchannel_id schid, int slow) put_device(&sch->dev); } else ret = css_evaluate_new_subchannel(schid, slow); - - return ret; + if (ret == -EAGAIN) + css_schedule_eval(schid); } -static int -css_rescan_devices(struct subchannel_id schid, void *data) +static struct idset *slow_subchannel_set; +static spinlock_t slow_subchannel_lock; + +static int __init slow_subchannel_init(void) { - return css_evaluate_subchannel(schid, 1); + spin_lock_init(&slow_subchannel_lock); + slow_subchannel_set = idset_sch_new(); + if (!slow_subchannel_set) { + printk(KERN_WARNING "cio: could not allocate slow subchannel " + "set\n"); + return -ENOMEM; + } + return 0; } -struct slow_subchannel { - struct list_head slow_list; - struct subchannel_id schid; -}; - -static LIST_HEAD(slow_subchannels_head); -static DEFINE_SPINLOCK(slow_subchannel_lock); +subsys_initcall(slow_subchannel_init); -static void -css_trigger_slow_path(struct work_struct *unused) +static void css_slow_path_func(struct work_struct *unused) { - CIO_TRACE_EVENT(4, "slowpath"); - - if (need_rescan) { - need_rescan = 0; - for_each_subchannel(css_rescan_devices, NULL); - return; - } + struct subchannel_id schid; + CIO_TRACE_EVENT(4, "slowpath"); spin_lock_irq(&slow_subchannel_lock); - while (!list_empty(&slow_subchannels_head)) { - struct slow_subchannel *slow_sch = - list_entry(slow_subchannels_head.next, - struct slow_subchannel, slow_list); - - list_del_init(slow_subchannels_head.next); + init_subchannel_id(&schid); + while (idset_sch_get_first(slow_subchannel_set, &schid)) { + idset_sch_del(slow_subchannel_set, schid); spin_unlock_irq(&slow_subchannel_lock); - css_evaluate_subchannel(slow_sch->schid, 1); + css_evaluate_subchannel(schid, 1); spin_lock_irq(&slow_subchannel_lock); - kfree(slow_sch); } spin_unlock_irq(&slow_subchannel_lock); } -DECLARE_WORK(slow_path_work, css_trigger_slow_path); +static DECLARE_WORK(slow_path_work, css_slow_path_func); struct workqueue_struct *slow_path_wq; +void css_schedule_eval(struct subchannel_id schid) +{ + unsigned long flags; + + spin_lock_irqsave(&slow_subchannel_lock, flags); + idset_sch_add(slow_subchannel_set, schid); + queue_work(slow_path_wq, &slow_path_work); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); +} + +void css_schedule_eval_all(void) +{ + unsigned long flags; + + spin_lock_irqsave(&slow_subchannel_lock, flags); + idset_fill(slow_subchannel_set); + queue_work(slow_path_wq, &slow_path_work); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); +} + /* Reprobe subchannel if unregistered. */ static int reprobe_subchannel(struct subchannel_id schid, void *data) { @@ -425,34 +438,15 @@ void css_schedule_reprobe(void) EXPORT_SYMBOL_GPL(css_schedule_reprobe); -/* - * Rescan for new devices. FIXME: This is slow. - * This function is called when we have lost CRWs due to overflows and we have - * to do subchannel housekeeping. - */ -void -css_reiterate_subchannels(void) -{ - css_clear_subchannel_slow_list(); - need_rescan = 1; -} - /* * Called from the machine check handler for subchannel report words. */ -int -css_process_crw(int rsid1, int rsid2) +void css_process_crw(int rsid1, int rsid2) { - int ret; struct subchannel_id mchk_schid; CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n", rsid1, rsid2); - - if (need_rescan) - /* We need to iterate all subchannels anyway. */ - return -EAGAIN; - init_subchannel_id(&mchk_schid); mchk_schid.sch_no = rsid1; if (rsid2 != 0) @@ -463,14 +457,7 @@ css_process_crw(int rsid1, int rsid2) * use stsch() to find out if the subchannel in question has come * or gone. */ - ret = css_evaluate_subchannel(mchk_schid, 0); - if (ret == -EAGAIN) { - if (css_enqueue_subchannel_slow(mchk_schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } - return ret; + css_evaluate_subchannel(mchk_schid, 0); } static int __init @@ -745,47 +732,6 @@ struct bus_type css_bus_type = { subsys_initcall(init_channel_subsystem); -int -css_enqueue_subchannel_slow(struct subchannel_id schid) -{ - struct slow_subchannel *new_slow_sch; - unsigned long flags; - - new_slow_sch = kzalloc(sizeof(struct slow_subchannel), GFP_ATOMIC); - if (!new_slow_sch) - return -ENOMEM; - new_slow_sch->schid = schid; - spin_lock_irqsave(&slow_subchannel_lock, flags); - list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head); - spin_unlock_irqrestore(&slow_subchannel_lock, flags); - return 0; -} - -void -css_clear_subchannel_slow_list(void) -{ - unsigned long flags; - - spin_lock_irqsave(&slow_subchannel_lock, flags); - while (!list_empty(&slow_subchannels_head)) { - struct slow_subchannel *slow_sch = - list_entry(slow_subchannels_head.next, - struct slow_subchannel, slow_list); - - list_del_init(slow_subchannels_head.next); - kfree(slow_sch); - } - spin_unlock_irqrestore(&slow_subchannel_lock, flags); -} - - - -int -css_slow_subchannels_exist(void) -{ - return (!list_empty(&slow_subchannels_head)); -} - MODULE_LICENSE("GPL"); EXPORT_SYMBOL(css_bus_type); EXPORT_SYMBOL_GPL(css_characteristics_avail); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index b2b1a265c60..4b3133a7bae 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -146,7 +146,7 @@ extern void css_sch_device_unregister(struct subchannel *); extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); -extern int css_process_crw(int, int); +extern void css_process_crw(int, int); extern void css_reiterate_subchannels(void); #define __MAX_SUBCHANNEL 65535 @@ -186,16 +186,12 @@ int device_trigger_verify(struct subchannel *sch); void device_kill_pending_timer(struct subchannel *); /* Helper functions to build lists for the slow path. */ -extern int css_enqueue_subchannel_slow(struct subchannel_id schid); -void css_walk_subchannel_slow_list(void (*fn)(unsigned long)); -void css_clear_subchannel_slow_list(void); -int css_slow_subchannels_exist(void); -extern int need_rescan; +void css_schedule_eval(struct subchannel_id schid); +void css_schedule_eval_all(void); int sch_is_pseudo_sch(struct subchannel *); extern struct workqueue_struct *slow_path_wq; -extern struct work_struct slow_path_work; int subchannel_add_files (struct device *); extern struct attribute_group *subch_attr_groups[]; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index d6226881d0d..898ec3b2beb 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -222,10 +222,8 @@ __recover_lost_chpids(struct subchannel *sch, int old_lpm) if (old_lpm & mask) continue; chpid.id = sch->schib.pmcw.chpid[i]; - if (!chp_is_registered(chpid)) { - need_rescan = 1; - queue_work(slow_path_wq, &slow_path_work); - } + if (!chp_is_registered(chpid)) + css_schedule_eval_all(); } } diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c new file mode 100644 index 00000000000..16ea828e99f --- /dev/null +++ b/drivers/s390/cio/idset.c @@ -0,0 +1,112 @@ +/* + * drivers/s390/cio/idset.c + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#include +#include +#include "idset.h" +#include "css.h" + +struct idset { + int num_ssid; + int num_id; + unsigned long bitmap[0]; +}; + +static inline unsigned long bitmap_size(int num_ssid, int num_id) +{ + return __BITOPS_WORDS(num_ssid * num_id) * sizeof(unsigned long); +} + +static struct idset *idset_new(int num_ssid, int num_id) +{ + struct idset *set; + + set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id), + GFP_KERNEL); + if (set) { + set->num_ssid = num_ssid; + set->num_id = num_id; + } + return set; +} + +void idset_free(struct idset *set) +{ + kfree(set); +} + +void idset_clear(struct idset *set) +{ + memset(set->bitmap, 0, bitmap_size(set->num_ssid, set->num_id)); +} + +void idset_fill(struct idset *set) +{ + memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id)); +} + +static inline void idset_add(struct idset *set, int ssid, int id) +{ + set_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline void idset_del(struct idset *set, int ssid, int id) +{ + clear_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline int idset_contains(struct idset *set, int ssid, int id) +{ + return test_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline int idset_get_first(struct idset *set, int *ssid, int *id) +{ + int bitnum; + + bitnum = find_first_bit(set->bitmap, set->num_ssid * set->num_id); + if (bitnum >= set->num_ssid * set->num_id) + return 0; + *ssid = bitnum / set->num_id; + *id = bitnum % set->num_id; + return 1; +} + +struct idset *idset_sch_new(void) +{ + return idset_new(__MAX_SSID + 1, __MAX_SUBCHANNEL + 1); +} + +void idset_sch_add(struct idset *set, struct subchannel_id schid) +{ + idset_add(set, schid.ssid, schid.sch_no); +} + +void idset_sch_del(struct idset *set, struct subchannel_id schid) +{ + idset_del(set, schid.ssid, schid.sch_no); +} + +int idset_sch_contains(struct idset *set, struct subchannel_id schid) +{ + return idset_contains(set, schid.ssid, schid.sch_no); +} + +int idset_sch_get_first(struct idset *set, struct subchannel_id *schid) +{ + int ssid = 0; + int id = 0; + int rc; + + rc = idset_get_first(set, &ssid, &id); + if (rc) { + init_subchannel_id(schid); + schid->ssid = ssid; + schid->sch_no = id; + } + return rc; +} diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h new file mode 100644 index 00000000000..144466ab8c1 --- /dev/null +++ b/drivers/s390/cio/idset.h @@ -0,0 +1,25 @@ +/* + * drivers/s390/cio/idset.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef S390_IDSET_H +#define S390_IDSET_H S390_IDSET_H + +#include "schid.h" + +struct idset; + +void idset_free(struct idset *set); +void idset_clear(struct idset *set); +void idset_fill(struct idset *set); + +struct idset *idset_sch_new(void); +void idset_sch_add(struct idset *set, struct subchannel_id id); +void idset_sch_del(struct idset *set, struct subchannel_id id); +int idset_sch_contains(struct idset *set, struct subchannel_id id); +int idset_sch_get_first(struct idset *set, struct subchannel_id *id); + +#endif /* S390_IDSET_H */ -- cgit From 7ad6a24970325294a22a08446d473384c15b928e Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:35 +0200 Subject: [S390] cio: fix subchannel channel-path data usage Ensure that channel-path related subchannel data is only retrieved and used when it is valid and that it is updated when it may have changed. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 303 +++++++++++++++--------------------------- drivers/s390/cio/chsc.h | 10 +- drivers/s390/cio/cio.h | 16 +-- drivers/s390/cio/css.c | 53 +++++++- drivers/s390/cio/css.h | 1 + drivers/s390/cio/device.c | 12 +- drivers/s390/cio/device_fsm.c | 1 + 7 files changed, 179 insertions(+), 217 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 89a130a6265..0841e16b6a8 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -26,155 +26,84 @@ static void *sei_page; -/* FIXME: this is _always_ called for every subchannel. shouldn't we - * process more than one at a time? */ -static int -chsc_get_sch_desc_irq(struct subchannel *sch, void *page) -{ - int ccode, j; +struct chsc_ssd_area { + struct chsc_header request; + u16 :10; + u16 ssid:2; + u16 :4; + u16 f_sch; /* first subchannel */ + u16 :16; + u16 l_sch; /* last subchannel */ + u32 :32; + struct chsc_header response; + u32 :32; + u8 sch_valid : 1; + u8 dev_valid : 1; + u8 st : 3; /* subchannel type */ + u8 zeroes : 3; + u8 unit_addr; /* unit address */ + u16 devno; /* device number */ + u8 path_mask; + u8 fla_valid_mask; + u16 sch; /* subchannel */ + u8 chpid[8]; /* chpids 0-7 */ + u16 fla[8]; /* full link addresses 0-7 */ +} __attribute__ ((packed)); - struct { - struct chsc_header request; - u16 reserved1a:10; - u16 ssid:2; - u16 reserved1b:4; - u16 f_sch; /* first subchannel */ - u16 reserved2; - u16 l_sch; /* last subchannel */ - u32 reserved3; - struct chsc_header response; - u32 reserved4; - u8 sch_valid : 1; - u8 dev_valid : 1; - u8 st : 3; /* subchannel type */ - u8 zeroes : 3; - u8 unit_addr; /* unit address */ - u16 devno; /* device number */ - u8 path_mask; - u8 fla_valid_mask; - u16 sch; /* subchannel */ - u8 chpid[8]; /* chpids 0-7 */ - u16 fla[8]; /* full link addresses 0-7 */ - } __attribute__ ((packed)) *ssd_area; - - ssd_area = page; +int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) +{ + unsigned long page; + struct chsc_ssd_area *ssd_area; + int ccode; + int ret; + int i; + int mask; + page = get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!page) + return -ENOMEM; + ssd_area = (struct chsc_ssd_area *) page; ssd_area->request.length = 0x0010; ssd_area->request.code = 0x0004; - - ssd_area->ssid = sch->schid.ssid; - ssd_area->f_sch = sch->schid.sch_no; - ssd_area->l_sch = sch->schid.sch_no; + ssd_area->ssid = schid.ssid; + ssd_area->f_sch = schid.sch_no; + ssd_area->l_sch = schid.sch_no; ccode = chsc(ssd_area); + /* Check response. */ if (ccode > 0) { - pr_debug("chsc returned with ccode = %d\n", ccode); - return (ccode == 3) ? -ENODEV : -EBUSY; + ret = (ccode == 3) ? -ENODEV : -EBUSY; + goto out_free; } - - switch (ssd_area->response.code) { - case 0x0001: /* everything ok */ - break; - case 0x0002: - CIO_CRW_EVENT(2, "Invalid command!\n"); - return -EINVAL; - case 0x0003: - CIO_CRW_EVENT(2, "Error in chsc request block!\n"); - return -EINVAL; - case 0x0004: - CIO_CRW_EVENT(2, "Model does not provide ssd\n"); - return -EOPNOTSUPP; - default: - CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", + if (ssd_area->response.code != 0x0001) { + CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n", + schid.ssid, schid.sch_no, ssd_area->response.code); - return -EIO; - } - - /* - * ssd_area->st stores the type of the detected - * subchannel, with the following definitions: - * - * 0: I/O subchannel: All fields have meaning - * 1: CHSC subchannel: Only sch_val, st and sch - * have meaning - * 2: Message subchannel: All fields except unit_addr - * have meaning - * 3: ADM subchannel: Only sch_val, st and sch - * have meaning - * - * Other types are currently undefined. - */ - if (ssd_area->st > 3) { /* uhm, that looks strange... */ - CIO_CRW_EVENT(0, "Strange subchannel type %d" - " for sch 0.%x.%04x\n", ssd_area->st, - sch->schid.ssid, sch->schid.sch_no); - /* - * There may have been a new subchannel type defined in the - * time since this code was written; since we don't know which - * fields have meaning and what to do with it we just jump out - */ - return 0; - } else { - const char *type[4] = {"I/O", "chsc", "message", "ADM"}; - CIO_CRW_EVENT(6, "ssd: sch 0.%x.%04x is %s subchannel\n", - sch->schid.ssid, sch->schid.sch_no, - type[ssd_area->st]); - - sch->ssd_info.valid = 1; - sch->ssd_info.type = ssd_area->st; + ret = -EIO; + goto out_free; } - - if (ssd_area->st == 0 || ssd_area->st == 2) { - for (j = 0; j < 8; j++) { - if (!((0x80 >> j) & ssd_area->path_mask & - ssd_area->fla_valid_mask)) - continue; - sch->ssd_info.chpid[j] = ssd_area->chpid[j]; - sch->ssd_info.fla[j] = ssd_area->fla[j]; - } + if (!ssd_area->sch_valid) { + ret = -ENODEV; + goto out_free; } - return 0; -} - -int -css_get_ssd_info(struct subchannel *sch) -{ - int ret; - void *page; - - page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!page) - return -ENOMEM; - spin_lock_irq(sch->lock); - ret = chsc_get_sch_desc_irq(sch, page); - if (ret) { - static int cio_chsc_err_msg; - - if (!cio_chsc_err_msg) { - printk(KERN_ERR - "chsc_get_sch_descriptions:" - " Error %d while doing chsc; " - "processing some machine checks may " - "not work\n", ret); - cio_chsc_err_msg = 1; - } - } - spin_unlock_irq(sch->lock); - free_page((unsigned long)page); - if (!ret) { - int j, mask; - struct chp_id chpid; - - chp_id_init(&chpid); - /* Allocate channel path structures, if needed. */ - for (j = 0; j < 8; j++) { - mask = 0x80 >> j; - chpid.id = sch->ssd_info.chpid[j]; - if ((sch->schib.pmcw.pim & mask) && - !chp_is_registered(chpid)) - chp_new(chpid); + /* Copy data */ + ret = 0; + memset(ssd, 0, sizeof(struct chsc_ssd_info)); + if ((ssd_area->st != 0) && (ssd_area->st != 2)) + goto out_free; + ssd->path_mask = ssd_area->path_mask; + ssd->fla_valid_mask = ssd_area->fla_valid_mask; + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (ssd_area->path_mask & mask) { + chp_id_init(&ssd->chpid[i]); + ssd->chpid[i].id = ssd_area->chpid[i]; } + if (ssd_area->fla_valid_mask & mask) + ssd->fla[i] = ssd_area->fla[i]; } +out_free: + free_page(page); return ret; } @@ -276,47 +205,6 @@ void chsc_chp_offline(struct chp_id chpid) s390_subchannel_remove_chpid); } -struct res_acc_data { - struct chp_id chpid; - u32 fla_mask; - u16 fla; -}; - -static int s390_process_res_acc_sch(struct res_acc_data *res_data, - struct subchannel *sch) -{ - int found; - int chp; - int ccode; - - found = 0; - for (chp = 0; chp <= 7; chp++) - /* - * check if chpid is in information updated by ssd - */ - if (sch->ssd_info.valid && - sch->ssd_info.chpid[chp] == res_data->chpid.id && - (sch->ssd_info.fla[chp] & res_data->fla_mask) - == res_data->fla) { - found = 1; - break; - } - - if (found == 0) - return 0; - - /* - * Do a stsch to update our subchannel structure with the - * new path information and eventually check for logically - * offline chpids. - */ - ccode = stsch(sch->schid, &sch->schib); - if (ccode > 0) - return 0; - - return 0x80 >> chp; -} - static int s390_process_res_acc_new_sch(struct subchannel_id schid) { @@ -338,6 +226,32 @@ s390_process_res_acc_new_sch(struct subchannel_id schid) return 0; } +struct res_acc_data { + struct chp_id chpid; + u32 fla_mask; + u16 fla; +}; + +static int get_res_chpid_mask(struct chsc_ssd_info *ssd, + struct res_acc_data *data) +{ + int i; + int mask; + + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (!(ssd->path_mask & mask)) + continue; + if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid)) + continue; + if ((ssd->fla_valid_mask & mask) && + ((ssd->fla[i] & data->fla_mask) != data->fla)) + continue; + return mask; + } + return 0; +} + static int __s390_process_res_acc(struct subchannel_id schid, void *data) { @@ -352,14 +266,11 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) return s390_process_res_acc_new_sch(schid); spin_lock_irq(sch->lock); - - chp_mask = s390_process_res_acc_sch(res_data, sch); - - if (chp_mask == 0) { - spin_unlock_irq(sch->lock); - put_device(&sch->dev); - return 0; - } + chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); + if (chp_mask == 0) + goto out; + if (stsch(sch->schid, &sch->schib)) + goto out; old_lpm = sch->lpm; sch->lpm = ((sch->schib.pmcw.pim & sch->schib.pmcw.pam & @@ -369,13 +280,12 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - +out: spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } - static void s390_process_res_acc (struct res_acc_data *res_data) { char dbf_txt[15]; @@ -661,29 +571,30 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, struct chp_id chpid, int on) { int chp, old_lpm; + int mask; unsigned long flags; - if (!sch->ssd_info.valid) - return; - spin_lock_irqsave(sch->lock, flags); old_lpm = sch->lpm; for (chp = 0; chp < 8; chp++) { - if (sch->ssd_info.chpid[chp] != chpid.id) + mask = 0x80 >> chp; + if (!(sch->ssd_info.path_mask & mask)) + continue; + if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid)) continue; if (on) { - sch->opm |= (0x80 >> chp); - sch->lpm |= (0x80 >> chp); + sch->opm |= mask; + sch->lpm |= mask; if (!old_lpm) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); break; } - sch->opm &= ~(0x80 >> chp); - sch->lpm &= ~(0x80 >> chp); - if (check_for_io_on_path(sch, (0x80 >> chp))) { + sch->opm &= ~mask; + sch->lpm &= ~mask; + if (check_for_io_on_path(sch, mask)) { if (device_is_online(sch)) /* Path verification is done after killing. */ device_kill_io(sch); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 742ef57d2c5..2ad81d11cf7 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -4,6 +4,7 @@ #include #include #include +#include "schid.h" #define CHSC_SDA_OC_MSS 0x2 @@ -35,7 +36,6 @@ struct channel_path_desc { struct channel_path; -extern int css_get_ssd_info(struct subchannel *); extern void chsc_process_crw(void); struct css_general_char { @@ -69,6 +69,14 @@ struct css_chsc_char { extern struct css_general_char css_general_characteristics; extern struct css_chsc_char css_chsc_characteristics; +struct chsc_ssd_info { + u8 path_mask; + u8 fla_valid_mask; + struct chp_id chpid[8]; + u16 fla[8]; +}; +extern int chsc_get_ssd_info(struct subchannel_id schid, + struct chsc_ssd_info *ssd); extern int chsc_determine_css_characteristics(void); extern int css_characteristics_avail; diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index e62ab5c5286..7446c39951a 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -1,19 +1,11 @@ #ifndef S390_CIO_H #define S390_CIO_H -#include "schid.h" #include #include - -/* - * where we put the ssd info - */ -struct ssd_info { - __u8 valid:1; - __u8 type:7; /* subchannel type */ - __u8 chpid[8]; /* chpids */ - __u16 fla[8]; /* full link addresses */ -} __attribute__ ((packed)); +#include +#include "chsc.h" +#include "schid.h" /* * path management control word @@ -109,7 +101,7 @@ struct subchannel { struct schib schib; /* subchannel information block */ struct orb orb; /* operation request block */ struct ccw1 sense_ccw; /* static ccw for sense command */ - struct ssd_info ssd_info; /* subchannel description */ + struct chsc_ssd_info ssd_info; /* subchannel description */ struct device dev; /* entry in device tree */ struct css_driver *driver; } __attribute__ ((aligned(8))); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index fcc641e578f..27c6d9e55b2 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -21,6 +21,7 @@ #include "chsc.h" #include "device.h" #include "idset.h" +#include "chp.h" int css_init_done = 0; static int need_reprobe = 0; @@ -125,8 +126,52 @@ void css_sch_device_unregister(struct subchannel *sch) mutex_unlock(&sch->reg_mutex); } -static int -css_register_subchannel(struct subchannel *sch) +static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) +{ + int i; + int mask; + + memset(ssd, 0, sizeof(struct chsc_ssd_info)); + ssd->path_mask = pmcw->pim; + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (pmcw->pim & mask) { + chp_id_init(&ssd->chpid[i]); + ssd->chpid[i].id = pmcw->chpid[i]; + } + } +} + +static void ssd_register_chpids(struct chsc_ssd_info *ssd) +{ + int i; + int mask; + + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (ssd->path_mask & mask) + if (!chp_is_registered(ssd->chpid[i])) + chp_new(ssd->chpid[i]); + } +} + +void css_update_ssd_info(struct subchannel *sch) +{ + int ret; + + if (cio_is_console(sch->schid)) { + /* Console is initialized too early for functions requiring + * memory allocation. */ + ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); + } else { + ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info); + if (ret) + ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); + ssd_register_chpids(&sch->ssd_info); + } +} + +static int css_register_subchannel(struct subchannel *sch) { int ret; @@ -135,9 +180,7 @@ css_register_subchannel(struct subchannel *sch) sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; sch->dev.groups = subch_attr_groups; - - css_get_ssd_info(sch); - + css_update_ssd_info(sch); /* make it known to the system */ ret = css_sch_device_register(sch); if (ret) { diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 4b3133a7bae..71fcfdc4280 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -148,6 +148,7 @@ extern int css_init_done; extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); extern void css_process_crw(int, int); extern void css_reiterate_subchannels(void); +void css_update_ssd_info(struct subchannel *sch); #define __MAX_SUBCHANNEL 65535 #define __MAX_SSID 3 diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 34e7d77b997..7bb44e73ea9 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -216,12 +216,18 @@ static ssize_t chpids_show (struct device * dev, struct device_attribute *attr, char * buf) { struct subchannel *sch = to_subchannel(dev); - struct ssd_info *ssd = &sch->ssd_info; + struct chsc_ssd_info *ssd = &sch->ssd_info; ssize_t ret = 0; int chp; + int mask; - for (chp = 0; chp < 8; chp++) - ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]); + for (chp = 0; chp < 8; chp++) { + mask = 0x80 >> chp; + if (ssd->path_mask & mask) + ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id); + else + ret += sprintf(buf + ret, "00 "); + } ret += sprintf (buf+ret, "\n"); return min((ssize_t)PAGE_SIZE, ret); } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 898ec3b2beb..aadd2fd4a86 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -246,6 +246,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) */ old_lpm = sch->lpm; stsch(sch->schid, &sch->schib); + css_update_ssd_info(sch); sch->lpm = sch->schib.pmcw.pam & sch->opm; /* Check since device may again have become not operational. */ if (!sch->schib.pmcw.dnv) -- cgit From 82b7ac058f60e0c92f9237fbaf440671f437ecdf Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 27 Apr 2007 16:01:36 +0200 Subject: [S390] cio: Dont call css_update_ssd_info from interrupt context. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 3 ++- drivers/s390/cio/device_fsm.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 7bb44e73ea9..3b56f373da3 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -743,6 +743,7 @@ static int io_subchannel_recog(struct ccw_device *, struct subchannel *); static void sch_attach_device(struct subchannel *sch, struct ccw_device *cdev) { + css_update_ssd_info(sch); spin_lock_irq(sch->lock); sch->dev.driver_data = cdev; cdev->private->schid = sch->schid; @@ -878,7 +879,7 @@ 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); - + css_update_ssd_info(sch); /* * io_subchannel_register() will also be called after device * recognition has been done for a boxed device (which will already diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index aadd2fd4a86..898ec3b2beb 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -246,7 +246,6 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) */ old_lpm = sch->lpm; stsch(sch->schid, &sch->schib); - css_update_ssd_info(sch); sch->lpm = sch->schib.pmcw.pam & sch->opm; /* Check since device may again have become not operational. */ if (!sch->schib.pmcw.dnv) -- cgit From d76123eb357a4baa653714183df286c1bb99f707 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 27 Apr 2007 16:01:37 +0200 Subject: [S390] cio: ccwgroup register vs. unregister. Introduce a mutex for struct ccwgroup to prevent simuntaneous register/unregister on the same ccwgroup device. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/ccwgroup.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 5aeb68e732b..e5ccda63e88 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -75,8 +75,10 @@ static void ccwgroup_ungroup_callback(struct device *dev) { struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + mutex_lock(&gdev->reg_mutex); __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + mutex_unlock(&gdev->reg_mutex); } static ssize_t @@ -173,7 +175,8 @@ ccwgroup_create(struct device *root, return -ENOMEM; atomic_set(&gdev->onoff, 0); - + mutex_init(&gdev->reg_mutex); + mutex_lock(&gdev->reg_mutex); for (i = 0; i < argc; i++) { gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); @@ -183,12 +186,12 @@ ccwgroup_create(struct device *root, || gdev->cdev[i]->id.driver_info != gdev->cdev[0]->id.driver_info) { rc = -EINVAL; - goto free_dev; + goto error; } /* Don't allow a device to belong to more than one group. */ if (gdev->cdev[i]->dev.driver_data) { rc = -EINVAL; - goto free_dev; + goto error; } gdev->cdev[i]->dev.driver_data = gdev; } @@ -203,9 +206,8 @@ ccwgroup_create(struct device *root, gdev->cdev[0]->dev.bus_id); rc = device_register(&gdev->dev); - if (rc) - goto free_dev; + goto error; get_device(&gdev->dev); rc = device_create_file(&gdev->dev, &dev_attr_ungroup); @@ -216,27 +218,21 @@ ccwgroup_create(struct device *root, rc = __ccwgroup_create_symlinks(gdev); if (!rc) { + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); return 0; } device_remove_file(&gdev->dev, &dev_attr_ungroup); device_unregister(&gdev->dev); error: - for (i = 0; i < argc; i++) - if (gdev->cdev[i]) { - put_device(&gdev->cdev[i]->dev); - gdev->cdev[i]->dev.driver_data = NULL; - } - put_device(&gdev->dev); - return rc; -free_dev: for (i = 0; i < argc; i++) if (gdev->cdev[i]) { if (gdev->cdev[i]->dev.driver_data == gdev) gdev->cdev[i]->dev.driver_data = NULL; put_device(&gdev->cdev[i]->dev); } - kfree(gdev); + mutex_unlock(&gdev->reg_mutex); + put_device(&gdev->dev); return rc; } @@ -422,8 +418,12 @@ ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver) get_driver(&cdriver->driver); while ((dev = driver_find_device(&cdriver->driver, NULL, NULL, __ccwgroup_match_all))) { - __ccwgroup_remove_symlinks(to_ccwgroupdev(dev)); + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + + mutex_lock(&gdev->reg_mutex); + __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + mutex_unlock(&gdev->reg_mutex); put_device(dev); } put_driver(&cdriver->driver); @@ -444,8 +444,10 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) if (cdev->dev.driver_data) { gdev = (struct ccwgroup_device *)cdev->dev.driver_data; if (get_device(&gdev->dev)) { + mutex_lock(&gdev->reg_mutex); if (device_is_registered(&gdev->dev)) return gdev; + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); } return NULL; @@ -465,6 +467,7 @@ ccwgroup_remove_ccwdev(struct ccw_device *cdev) if (gdev) { __ccwgroup_remove_symlinks(gdev); device_unregister(&gdev->dev); + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); } } -- cgit From 8c4941c53b14e5a08ed2f270e9f087b410a9abcc Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 27 Apr 2007 16:01:38 +0200 Subject: [S390] cio: cm_enable memory leak. We allocage two pages when channel path measurements are enabled via cm_enable. We must not forget to free them again when channel path measurements are disabled again. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 0841e16b6a8..ea92ac4d657 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -818,7 +818,7 @@ chsc_secm(struct channel_subsystem *css, int enable) } else chsc_remove_cmg_attr(css); } - if (enable && !css->cm_enabled) { + if (!css->cm_enabled) { free_page((unsigned long)css->cub_addr1); free_page((unsigned long)css->cub_addr2); } -- cgit From ef99516c9646802c3d38c3eb83de302e05b3c1b5 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 27 Apr 2007 16:01:39 +0200 Subject: [S390] cio: Unregister ccw devices directly. We used to unregister ccw devices not directly from the I/O subchannel remove function in order to avoid lifelocks on the css bus semaphore. This semaphore is gone, and there is no reason to not unregister the ccw device directly (it is even better since it is more in keeping with the goal of immediate disconnect). Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 56 ++++++++++------------------------------------- 1 file changed, 12 insertions(+), 44 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 3b56f373da3..03355902c58 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -290,16 +290,10 @@ int ccw_device_is_orphan(struct ccw_device *cdev) return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent)); } -static void ccw_device_unregister(struct work_struct *work) +static void ccw_device_unregister(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; if (test_and_clear_bit(1, &cdev->private->registered)) - device_unregister(&cdev->dev); - put_device(&cdev->dev); + device_del(&cdev->dev); } static void @@ -316,11 +310,8 @@ 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); - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } + ccw_device_unregister(cdev); + put_device(&cdev->dev); return ; } sch = to_subchannel(cdev->dev.parent); @@ -555,17 +546,10 @@ static struct attribute_group ccwdev_attr_group = { .attrs = ccwdev_attrs, }; -static int -device_add_files (struct device *dev) -{ - return sysfs_create_group(&dev->kobj, &ccwdev_attr_group); -} - -static void -device_remove_files(struct device *dev) -{ - sysfs_remove_group(&dev->kobj, &ccwdev_attr_group); -} +struct attribute_group *ccwdev_attr_groups[] = { + &ccwdev_attr_group, + NULL, +}; /* this is a simple abstraction for device_register that sets the * correct bus type and adds the bus specific files */ @@ -580,10 +564,6 @@ static int ccw_device_register(struct ccw_device *cdev) return ret; set_bit(1, &cdev->private->registered); - if ((ret = device_add_files(dev))) { - if (test_and_clear_bit(1, &cdev->private->registered)) - device_del(dev); - } return ret; } @@ -655,10 +635,6 @@ ccw_device_add_changed(struct work_struct *work) return; } set_bit(1, &cdev->private->registered); - if (device_add_files(&cdev->dev)) { - if (test_and_clear_bit(1, &cdev->private->registered)) - device_unregister(&cdev->dev); - } } void ccw_device_do_unreg_rereg(struct work_struct *work) @@ -671,9 +647,7 @@ void ccw_device_do_unreg_rereg(struct work_struct *work) cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); - device_remove_files(&cdev->dev); - if (test_and_clear_bit(1, &cdev->private->registered)) - device_del(&cdev->dev); + ccw_device_unregister(cdev); PREPARE_WORK(&cdev->private->kick_work, ccw_device_add_changed); queue_work(ccw_device_work, &cdev->private->kick_work); @@ -712,6 +686,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, cdev->dev.parent = &sch->dev; cdev->dev.release = ccw_device_release; INIT_LIST_HEAD(&cdev->private->kick_work.entry); + cdev->dev.groups = ccwdev_attr_groups; /* Do first half of device_register. */ device_initialize(&cdev->dev); if (!get_device(&sch->dev)) { @@ -1141,15 +1116,8 @@ io_subchannel_remove (struct subchannel *sch) sch->dev.driver_data = NULL; cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); - /* - * Put unregistration on workqueue to avoid livelocks on the css bus - * semaphore. - */ - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } + ccw_device_unregister(cdev); + put_device(&cdev->dev); return 0; } -- cgit From cb629a01bb5bca951287e761c590a5686c6ca416 Mon Sep 17 00:00:00 2001 From: Milind Arun Choudhary Date: Fri, 27 Apr 2007 16:02:01 +0200 Subject: [S390] SPIN_LOCK_UNLOCKED cleanup in drivers/s390 SPIN_LOCK_UNLOCKED cleanup,use __SPIN_LOCK_UNLOCKED instead. Signed-off-by: Milind Arun Choudhary Cc: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 90b22faabbf..28abd697be1 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -476,7 +476,7 @@ struct cmb_area { }; static struct cmb_area cmb_area = { - .lock = SPIN_LOCK_UNLOCKED, + .lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock), .list = LIST_HEAD_INIT(cmb_area.list), .num_channels = 1024, }; -- cgit From fa1a8c23eb7d3ded8a3c6d0e653339a2bc7fca9e Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 26 Apr 2007 00:12:03 -0700 Subject: s390: cio: Delay uevents for subchannels We often have the situation that we register a subchannel and start device recognition, only to find out that the device is not usable after all, which triggers an unregister of the subchannel. This often happens on hundreds of subchannels on a LPAR, leading to a storm of events which aren't of any use. Therefore, use uevent_suppress to delay the KOBJ_ADD uevent for a subchannel until we know that its ccw_device is to be registered. Signed-off-by: Cornelia Huck Cc: Kay Sievers Cc: Eric Rannaud Cc: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/s390/cio/device.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 03355902c58..a23ff582db9 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -871,6 +871,12 @@ io_subchannel_register(struct work_struct *work) } goto out; } + /* + * Now we know this subchannel will stay, we can throw + * our delayed uevent. + */ + sch->dev.uevent_suppress = 0; + kobject_uevent(&sch->dev.kobj, KOBJ_ADD); /* make it known to the system */ ret = ccw_device_register(cdev); if (ret) { -- cgit