From 2253e8d79237c69086ded391e6767afe16972527 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 27 Jan 2014 13:26:10 +0100 Subject: s390/cio: fix driver callback initialization for ccw consoles ccw consoles are in use before they can be properly registered with the driver core. For devices which are in use by a device driver we rely on the ccw_device's pointer to the driver callbacks to be valid. For ccw consoles this pointer is NULL until they are registered later during boot and we dereferenced this pointer. This worked by chance on 64 bit builds (cdev->drv was NULL but the optional callback cdev->drv->path_event was also NULL by coincidence) and was unnoticed until we received reports about boot failures on 31 bit systems. Fix it by initializing the driver pointer for ccw consoles. Cc: # 3.10+ Reported-by: Mike Frysinger Reported-by: Heiko Carstens Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e9d783563cb..4283dd3cdd4 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1609,7 +1609,7 @@ out_unlock: return rc; } -struct ccw_device *ccw_device_probe_console(void) +struct ccw_device *ccw_device_probe_console(struct ccw_driver *drv) { struct io_subchannel_private *io_priv; struct ccw_device *cdev; @@ -1631,6 +1631,7 @@ struct ccw_device *ccw_device_probe_console(void) kfree(io_priv); return cdev; } + cdev->drv = drv; set_io_private(sch, io_priv); ret = ccw_device_console_enable(cdev, sch); if (ret) { -- cgit v1.2.3-70-g09d2 From 1e5320960510d6d6f2cbdc7ed33df9791283b7ea Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 27 Jan 2014 13:28:10 +0100 Subject: s390/cio: reorder initialization of ccw consoles Drivers for ccw consoles use ccw_device_probe_console to receive an initialized ccw device which is already enabled for interrupts. After that the device driver does the initialization of its private data. This can race with unsolicited interrupts which can happen once the device is enabled for interrupts. Split ccw_device_probe_console into ccw_device_create_console and ccw_device_enable_console and reorder the initialization of the ccw console drivers. While at it mark these functions as __init. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/ccwdev.h | 4 +++- drivers/s390/char/con3215.c | 8 +++++++- drivers/s390/char/raw3270.c | 9 ++++++++- drivers/s390/cio/device.c | 29 +++++++++++++++++------------ 4 files changed, 35 insertions(+), 15 deletions(-) (limited to 'drivers/s390/cio') diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h index 31b5ca8f8c3..a9c2c068617 100644 --- a/arch/s390/include/asm/ccwdev.h +++ b/arch/s390/include/asm/ccwdev.h @@ -219,7 +219,9 @@ extern void ccw_device_get_id(struct ccw_device *, struct ccw_dev_id *); #define to_ccwdev(n) container_of(n, struct ccw_device, dev) #define to_ccwdrv(n) container_of(n, struct ccw_driver, driver) -extern struct ccw_device *ccw_device_probe_console(struct ccw_driver *); +extern struct ccw_device *ccw_device_create_console(struct ccw_driver *); +extern void ccw_device_destroy_console(struct ccw_device *); +extern int ccw_device_enable_console(struct ccw_device *); extern void ccw_device_wait_idle(struct ccw_device *); extern int ccw_device_force_console(struct ccw_device *); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index bb86494e2b7..5af7f0bd612 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -922,7 +922,7 @@ static int __init con3215_init(void) raw3215_freelist = req; } - cdev = ccw_device_probe_console(&raw3215_ccw_driver); + cdev = ccw_device_create_console(&raw3215_ccw_driver); if (IS_ERR(cdev)) return -ENODEV; @@ -932,6 +932,12 @@ static int __init con3215_init(void) cdev->handler = raw3215_irq; raw->flags |= RAW3215_FIXED; + if (ccw_device_enable_console(cdev)) { + ccw_device_destroy_console(cdev); + raw3215_free_info(raw); + raw3215[0] = NULL; + return -ENODEV; + } /* Request the console irq */ if (raw3215_startup(raw) != 0) { diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index de2c0483949..041c65bc7bb 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -790,7 +790,7 @@ struct raw3270 __init *raw3270_setup_console(void) char *ascebc; int rc; - cdev = ccw_device_probe_console(&raw3270_ccw_driver); + cdev = ccw_device_create_console(&raw3270_ccw_driver); if (IS_ERR(cdev)) return ERR_CAST(cdev); @@ -800,6 +800,13 @@ struct raw3270 __init *raw3270_setup_console(void) if (rc) return ERR_PTR(rc); set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags); + + rc = ccw_device_enable_console(cdev); + if (rc) { + ccw_device_destroy_console(cdev); + return ERR_PTR(rc); + } + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); do { __raw3270_reset_device(rp); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 4283dd3cdd4..da431992fd8 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1572,11 +1572,14 @@ out: } #ifdef CONFIG_CCW_CONSOLE -static int ccw_device_console_enable(struct ccw_device *cdev, - struct subchannel *sch) +int __init ccw_device_enable_console(struct ccw_device *cdev) { + struct subchannel *sch = to_subchannel(cdev->dev.parent); int rc; + if (!cdev->drv || !cdev->handler) + return -EINVAL; + io_subchannel_init_fields(sch); rc = cio_commit_config(sch); if (rc) @@ -1609,12 +1612,11 @@ out_unlock: return rc; } -struct ccw_device *ccw_device_probe_console(struct ccw_driver *drv) +struct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv) { struct io_subchannel_private *io_priv; struct ccw_device *cdev; struct subchannel *sch; - int ret; sch = cio_probe_console(); if (IS_ERR(sch)) @@ -1633,17 +1635,20 @@ struct ccw_device *ccw_device_probe_console(struct ccw_driver *drv) } cdev->drv = drv; set_io_private(sch, io_priv); - ret = ccw_device_console_enable(cdev, sch); - if (ret) { - set_io_private(sch, NULL); - put_device(&sch->dev); - put_device(&cdev->dev); - kfree(io_priv); - return ERR_PTR(ret); - } return cdev; } +void __init ccw_device_destroy_console(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct io_subchannel_private *io_priv = to_io_private(sch); + + set_io_private(sch, NULL); + put_device(&sch->dev); + put_device(&cdev->dev); + kfree(io_priv); +} + /** * ccw_device_wait_idle() - busy wait for device to become idle * @cdev: ccw device -- cgit v1.2.3-70-g09d2 From 137a14f434705a366cc94b2b32f2488c975863ad Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 27 Jan 2014 13:29:15 +0100 Subject: s390/cio: fix irq stats for early interrupts on ccw consoles Interrupts which happen on ccw consoles prior to their registration with the driver core are not accounted to the respective device driver. Fix this by setting the proper interrupt class during initialization of ccw consoles. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/s390/cio') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index da431992fd8..d8d9b5b5cc5 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1571,6 +1571,18 @@ out: return rc; } +static void ccw_device_set_int_class(struct ccw_device *cdev) +{ + struct ccw_driver *cdrv = cdev->drv; + + /* Note: we interpret class 0 in this context as an uninitialized + * field since it translates to a non-I/O interrupt class. */ + if (cdrv->int_class != 0) + cdev->private->int_class = cdrv->int_class; + else + cdev->private->int_class = IRQIO_CIO; +} + #ifdef CONFIG_CCW_CONSOLE int __init ccw_device_enable_console(struct ccw_device *cdev) { @@ -1635,6 +1647,7 @@ struct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv) } cdev->drv = drv; set_io_private(sch, io_priv); + ccw_device_set_int_class(cdev); return cdev; } @@ -1732,15 +1745,8 @@ ccw_device_probe (struct device *dev) int ret; cdev->drv = cdrv; /* to let the driver call _set_online */ - /* Note: we interpret class 0 in this context as an uninitialized - * field since it translates to a non-I/O interrupt class. */ - if (cdrv->int_class != 0) - cdev->private->int_class = cdrv->int_class; - else - cdev->private->int_class = IRQIO_CIO; - + ccw_device_set_int_class(cdev); ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV; - if (ret) { cdev->drv = NULL; cdev->private->int_class = IRQIO_CIO; -- cgit v1.2.3-70-g09d2 From fe7c30a420761654777d3cc15412fc7626407e93 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 13 Feb 2014 13:02:32 +0100 Subject: s390/airq: add support for irq ranges Add airq_iv_alloc and airq_iv_free to allocate and free consecutive ranges of irqs from the interrupt vector. Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/airq.h | 14 ++++++++-- drivers/s390/cio/airq.c | 66 +++++++++++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 27 deletions(-) (limited to 'drivers/s390/cio') diff --git a/arch/s390/include/asm/airq.h b/arch/s390/include/asm/airq.h index 4bbb5957ed1..bd93ff6661b 100644 --- a/arch/s390/include/asm/airq.h +++ b/arch/s390/include/asm/airq.h @@ -44,11 +44,21 @@ struct airq_iv { struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags); void airq_iv_release(struct airq_iv *iv); -unsigned long airq_iv_alloc_bit(struct airq_iv *iv); -void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit); +unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num); +void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num); unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start, unsigned long end); +static inline unsigned long airq_iv_alloc_bit(struct airq_iv *iv) +{ + return airq_iv_alloc(iv, 1); +} + +static inline void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit) +{ + airq_iv_free(iv, bit, 1); +} + static inline unsigned long airq_iv_end(struct airq_iv *iv) { return iv->end; diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index f055df0b167..445564c790f 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -186,55 +186,71 @@ void airq_iv_release(struct airq_iv *iv) EXPORT_SYMBOL(airq_iv_release); /** - * airq_iv_alloc_bit - allocate an irq bit from an interrupt vector + * airq_iv_alloc - allocate irq bits from an interrupt vector * @iv: pointer to an interrupt vector structure + * @num: number of consecutive irq bits to allocate * - * Returns the bit number of the allocated irq, or -1UL if no bit - * is available or the AIRQ_IV_ALLOC flag has not been specified + * Returns the bit number of the first irq in the allocated block of irqs, + * or -1UL if no bit is available or the AIRQ_IV_ALLOC flag has not been + * specified */ -unsigned long airq_iv_alloc_bit(struct airq_iv *iv) +unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num) { - unsigned long bit; + unsigned long bit, i; - if (!iv->avail) + if (!iv->avail || num == 0) return -1UL; spin_lock(&iv->lock); bit = find_first_bit_inv(iv->avail, iv->bits); - if (bit < iv->bits) { - clear_bit_inv(bit, iv->avail); - if (bit >= iv->end) - iv->end = bit + 1; - } else + while (bit + num <= iv->bits) { + for (i = 1; i < num; i++) + if (!test_bit_inv(bit + i, iv->avail)) + break; + if (i >= num) { + /* Found a suitable block of irqs */ + for (i = 0; i < num; i++) + clear_bit_inv(bit + i, iv->avail); + if (bit + num >= iv->end) + iv->end = bit + num + 1; + break; + } + bit = find_next_bit_inv(iv->avail, iv->bits, bit + i + 1); + } + if (bit + num > iv->bits) bit = -1UL; spin_unlock(&iv->lock); return bit; } -EXPORT_SYMBOL(airq_iv_alloc_bit); +EXPORT_SYMBOL(airq_iv_alloc); /** - * airq_iv_free_bit - free an irq bit of an interrupt vector + * airq_iv_free - free irq bits of an interrupt vector * @iv: pointer to interrupt vector structure - * @bit: number of the irq bit to free + * @bit: number of the first irq bit to free + * @num: number of consecutive irq bits to free */ -void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit) +void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num) { - if (!iv->avail) + unsigned long i; + + if (!iv->avail || num == 0) return; spin_lock(&iv->lock); - /* Clear (possibly left over) interrupt bit */ - clear_bit_inv(bit, iv->vector); - /* Make the bit position available again */ - set_bit_inv(bit, iv->avail); - if (bit == iv->end - 1) { + for (i = 0; i < num; i++) { + /* Clear (possibly left over) interrupt bit */ + clear_bit_inv(bit + i, iv->vector); + /* Make the bit positions available again */ + set_bit_inv(bit + i, iv->avail); + } + if (bit + num >= iv->end) { /* Find new end of bit-field */ - while (--iv->end > 0) - if (!test_bit_inv(iv->end - 1, iv->avail)) - break; + while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail)) + iv->end--; } spin_unlock(&iv->lock); } -EXPORT_SYMBOL(airq_iv_free_bit); +EXPORT_SYMBOL(airq_iv_free); /** * airq_iv_scan - scan interrupt vector for non-zero bits -- cgit v1.2.3-70-g09d2 From f7e1e65d29636d050cdde0770b9544572959a67d Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 17 Feb 2014 11:16:10 +0100 Subject: s390: improve debug feature usage The maximum usable buffer size of the s390 debug feature (when using the sprintf_view) is 11 * sizeof(long) (1 pointer for the format string + 10 arguments). When a larger buffer size is specified the additional memory is unused and wasted per debug entry. So reducing the buffer size to its maximum (or to the actual buffer size used) will make more precious debug feature space usable. For pci_msg, chsc_msg, and cio_crw we use the additional usable dbf space to reduce the number of allocated pages. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/pci/pci_debug.c | 2 +- drivers/s390/cio/chsc_sch.c | 3 +-- drivers/s390/cio/cio.c | 4 ++-- drivers/s390/net/qeth_core_main.c | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/s390/cio') diff --git a/arch/s390/pci/pci_debug.c b/arch/s390/pci/pci_debug.c index 75c69b402e0..c5c66840ac0 100644 --- a/arch/s390/pci/pci_debug.c +++ b/arch/s390/pci/pci_debug.c @@ -139,7 +139,7 @@ void zpci_debug_exit_device(struct zpci_dev *zdev) int __init zpci_debug_init(void) { /* event trace buffer */ - pci_debug_msg_id = debug_register("pci_msg", 16, 1, 16 * sizeof(long)); + pci_debug_msg_id = debug_register("pci_msg", 8, 1, 8 * sizeof(long)); if (!pci_debug_msg_id) return -EINVAL; debug_register_view(pci_debug_msg_id, &debug_sprintf_view); diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index 7b29d0be0ca..1d3661af7bd 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -173,8 +173,7 @@ static struct css_driver chsc_subchannel_driver = { static int __init chsc_init_dbfs(void) { - chsc_debug_msg_id = debug_register("chsc_msg", 16, 1, - 16 * sizeof(long)); + chsc_debug_msg_id = debug_register("chsc_msg", 8, 1, 4 * sizeof(long)); if (!chsc_debug_msg_id) goto out; debug_register_view(chsc_debug_msg_id, &debug_sprintf_view); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 8ee88c4ebd8..97c48b38d67 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -54,7 +54,7 @@ debug_info_t *cio_debug_crw_id; */ static int __init cio_debug_init(void) { - cio_debug_msg_id = debug_register("cio_msg", 16, 1, 16 * sizeof(long)); + cio_debug_msg_id = debug_register("cio_msg", 16, 1, 11 * sizeof(long)); if (!cio_debug_msg_id) goto out_unregister; debug_register_view(cio_debug_msg_id, &debug_sprintf_view); @@ -64,7 +64,7 @@ static int __init cio_debug_init(void) goto out_unregister; debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view); debug_set_level(cio_debug_trace_id, 2); - cio_debug_crw_id = debug_register("cio_crw", 16, 1, 16 * sizeof(long)); + cio_debug_crw_id = debug_register("cio_crw", 8, 1, 8 * sizeof(long)); if (!cio_debug_crw_id) goto out_unregister; debug_register_view(cio_debug_crw_id, &debug_sprintf_view); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index c3a83df0789..bd5d4ab4bcf 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -33,8 +33,8 @@ struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = { /* N P A M L V H */ [QETH_DBF_SETUP] = {"qeth_setup", 8, 1, 8, 5, &debug_hex_ascii_view, NULL}, - [QETH_DBF_MSG] = {"qeth_msg", - 8, 1, 128, 3, &debug_sprintf_view, NULL}, + [QETH_DBF_MSG] = {"qeth_msg", 8, 1, 11 * sizeof(long), 3, + &debug_sprintf_view, NULL}, [QETH_DBF_CTRL] = {"qeth_control", 8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL}, }; -- cgit v1.2.3-70-g09d2