From 661ca0da3e1fa60c9e56f995fd83772ffa9ffe90 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Sat, 26 Jan 2008 14:10:36 +0100 Subject: [S390] Cleanup in Documentation/kernel-parameters.txt. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 65de5ba..d29b6d9 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -370,8 +370,6 @@ and is between 256 and 4096 characters. It is defined in the file configured. Potentially dangerous and should only be used if you are entirely sure of the consequences. - chandev= [HW,NET] Generic channel device initialisation - checkreqprot [SELINUX] Set initial checkreqprot flag value. Format: { "0" | "1" } See security/selinux/Kconfig help text. @@ -382,6 +380,12 @@ and is between 256 and 4096 characters. It is defined in the file Value can be changed at runtime via /selinux/checkreqprot. + cio_ignore= [S390] + See Documentation/s390/CommonIO for details. + + cio_msg= [S390] + See Documentation/s390/CommonIO for details. + clock= [BUGS=X86-32, HW] gettimeofday clocksource override. [Deprecated] Forces specified clocksource (if available) to be used -- cgit v0.10.2 From 14ff56bbb3422cc465a12e81f5e1b5f06168aeda Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Sat, 26 Jan 2008 14:10:37 +0100 Subject: [S390] cio: Dump ccw device information in case of timeout. Information about a ccw device will be dumped in case of a ccw timeout. This can be enabled with the kernel parameter ccw_timeout_log. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index d29b6d9..880f882 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -370,6 +370,9 @@ and is between 256 and 4096 characters. It is defined in the file configured. Potentially dangerous and should only be used if you are entirely sure of the consequences. + ccw_timeout_log [S390] + See Documentation/s390/CommonIO for details. + checkreqprot [SELINUX] Set initial checkreqprot flag value. Format: { "0" | "1" } See security/selinux/Kconfig help text. diff --git a/Documentation/s390/CommonIO b/Documentation/s390/CommonIO index 86320aa..8fbc0a8 100644 --- a/Documentation/s390/CommonIO +++ b/Documentation/s390/CommonIO @@ -4,6 +4,11 @@ S/390 common I/O-Layer - command line parameters, procfs and debugfs entries Command line parameters ----------------------- +* ccw_timeout_log + + Enable logging of debug information in case of ccw device timeouts. + + * cio_msg = yes | no Determines whether information on found devices and sensed device diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index bfad421..31c0f78 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -25,6 +25,8 @@ #include "ioasm.h" #include "chp.h" +static int timeout_log_enabled; + int device_is_online(struct subchannel *sch) { @@ -82,6 +84,52 @@ int device_trigger_verify(struct subchannel *sch) return 0; } +static int __init ccw_timeout_log_setup(char *unused) +{ + timeout_log_enabled = 1; + return 1; +} + +__setup("ccw_timeout_log", ccw_timeout_log_setup); + +static void ccw_timeout_log(struct ccw_device *cdev) +{ + struct schib schib; + struct subchannel *sch; + int cc; + + sch = to_subchannel(cdev->dev.parent); + cc = stsch(sch->schid, &schib); + + printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, " + "device information:\n", get_clock()); + printk(KERN_WARNING "cio: orb:\n"); + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + &sch->orb, sizeof(sch->orb), 0); + printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id); + printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id); + printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, " + "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm); + + if ((void *)(addr_t)sch->orb.cpa == &sch->sense_ccw || + (void *)(addr_t)sch->orb.cpa == cdev->private->iccws) + printk(KERN_WARNING "cio: last channel program (intern):\n"); + else + printk(KERN_WARNING "cio: last channel program:\n"); + + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + (void *)(addr_t)sch->orb.cpa, sizeof(struct ccw1), 0); + printk(KERN_WARNING "cio: ccw device state: %d\n", + cdev->private->state); + printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc); + printk(KERN_WARNING "cio: schib:\n"); + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + &schib, sizeof(schib), 0); + printk(KERN_WARNING "cio: ccw device flags:\n"); + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + &cdev->private->flags, sizeof(cdev->private->flags), 0); +} + /* * Timeout function. It just triggers a DEV_EVENT_TIMEOUT. */ @@ -92,6 +140,8 @@ ccw_device_timeout(unsigned long data) cdev = (struct ccw_device *) data; spin_lock_irq(cdev->ccwlock); + if (timeout_log_enabled) + ccw_timeout_log(cdev); dev_fsm_event(cdev, DEV_EVENT_TIMEOUT); spin_unlock_irq(cdev->ccwlock); } -- cgit v0.10.2 From 084325d80418adf4d75b10a9ceff1348f2e09163 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:38 +0100 Subject: [S390] cio: Use helpers instead of container_of(). - Introduce to_cssdriver. - Use to_xxx instead of container_of where possible. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 5baa517..b3e2c16 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -35,8 +35,8 @@ ccwgroup_bus_match (struct device * dev, struct device_driver * drv) struct ccwgroup_device *gdev; struct ccwgroup_driver *gdrv; - gdev = container_of(dev, struct ccwgroup_device, dev); - gdrv = container_of(drv, struct ccwgroup_driver, driver); + gdev = to_ccwgroupdev(dev); + gdrv = to_ccwgroupdrv(drv); if (gdev->creator_id == gdrv->driver_id) return 1; diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index c3df2cd..bc2f8fa 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -787,8 +787,8 @@ int sch_is_pseudo_sch(struct subchannel *sch) static int css_bus_match (struct device *dev, struct device_driver *drv) { - struct subchannel *sch = container_of (dev, struct subchannel, dev); - struct css_driver *driver = container_of (drv, struct css_driver, drv); + struct subchannel *sch = to_subchannel(dev); + struct css_driver *driver = to_cssdriver(drv); if (sch->st == driver->subchannel_type) return 1; @@ -802,7 +802,7 @@ css_probe (struct device *dev) struct subchannel *sch; sch = to_subchannel(dev); - sch->driver = container_of (dev->driver, struct css_driver, drv); + sch->driver = to_cssdriver(dev->driver); return (sch->driver->probe ? sch->driver->probe(sch) : 0); } diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 81215ef..1d8306c 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -134,6 +134,8 @@ struct css_driver { void (*shutdown)(struct subchannel *); }; +#define to_cssdriver(n) container_of(n, struct css_driver, drv) + /* * all css_drivers have the css_bus_type */ -- cgit v0.10.2 From 602b20f2bf335d0d5fce11cb2ade22aa74e7ba25 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:39 +0100 Subject: [S390] cio: css_driver: Use consistent parameters. Make all callbacks in css_driver take a struct subchannel (and not a struct device). Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 597c0c7..44a2ea4 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -132,7 +132,7 @@ static void terminate_internal_io(struct subchannel *sch) device_set_intretry(sch); /* Call handler. */ if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); + sch->driver->termination(sch); } static int @@ -172,12 +172,12 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) terminate_internal_io(sch); /* Re-start path verification. */ if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); } } else { /* trigger path verification. */ if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); else if (sch->lpm == mask) goto out_unreg; } @@ -279,7 +279,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) if (!old_lpm && sch->lpm) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); out: spin_unlock_irq(sch->lock); put_device(&sch->dev); @@ -549,7 +549,7 @@ __chp_add(struct subchannel_id schid, void *data) | mask) & sch->opm; if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); spin_unlock_irq(sch->lock); put_device(&sch->dev); @@ -589,7 +589,7 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, if (!old_lpm) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); break; } sch->opm &= ~mask; @@ -603,13 +603,13 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, terminate_internal_io(sch); /* Re-start path verification. */ if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); } } else if (!sch->lpm) { if (device_trigger_verify(sch) != 0) css_schedule_eval(sch->schid); } else if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); + sch->driver->verify(sch); break; } spin_unlock_irqrestore(sch->lock, flags); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 4690534..469b007 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -147,7 +147,7 @@ cio_tpi(void) spin_lock(sch->lock); memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw)); if (sch->driver && sch->driver->irq) - sch->driver->irq(&sch->dev); + sch->driver->irq(sch); spin_unlock(sch->lock); irq_exit (); _local_bh_enable(); @@ -680,7 +680,7 @@ do_IRQ (struct pt_regs *regs) sizeof (irb->scsw)); /* Call interrupt handler if there is one. */ if (sch->driver && sch->driver->irq) - sch->driver->irq(&sch->dev); + sch->driver->irq(sch); } if (sch) spin_unlock(sch->lock); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index bc2f8fa..614cc69 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -293,7 +293,7 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) action = UNREGISTER; if (sch->driver && sch->driver->notify) { spin_unlock_irqrestore(sch->lock, flags); - ret = sch->driver->notify(&sch->dev, event); + ret = sch->driver->notify(sch, event); spin_lock_irqsave(sch->lock, flags); if (ret) action = NONE; diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 1d8306c..32acd24 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -125,10 +125,10 @@ struct subchannel; struct css_driver { unsigned int subchannel_type; struct device_driver drv; - void (*irq)(struct device *); - int (*notify)(struct device *, int); - void (*verify)(struct device *); - void (*termination)(struct device *); + void (*irq)(struct subchannel *); + int (*notify)(struct subchannel *, int); + void (*verify)(struct subchannel *); + void (*termination)(struct subchannel *); int (*probe)(struct subchannel *); int (*remove)(struct subchannel *); void (*shutdown)(struct subchannel *); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 74f6b53..000c641 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -115,11 +115,12 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env) struct bus_type ccw_bus_type; -static int io_subchannel_probe (struct subchannel *); -static int io_subchannel_remove (struct subchannel *); -static int io_subchannel_notify(struct device *, int); -static void io_subchannel_verify(struct device *); -static void io_subchannel_ioterm(struct device *); +static void io_subchannel_irq(struct subchannel *); +static int io_subchannel_probe(struct subchannel *); +static int io_subchannel_remove(struct subchannel *); +static int io_subchannel_notify(struct subchannel *, int); +static void io_subchannel_verify(struct subchannel *); +static void io_subchannel_ioterm(struct subchannel *); static void io_subchannel_shutdown(struct subchannel *); static struct css_driver io_subchannel_driver = { @@ -1096,6 +1097,18 @@ out: put_device(&cdev->dev); } +static void io_subchannel_irq(struct subchannel *sch) +{ + struct ccw_device *cdev; + + cdev = sch->dev.driver_data; + + CIO_TRACE_EVENT(3, "IRQ"); + CIO_TRACE_EVENT(3, sch->dev.bus_id); + if (cdev) + dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); +} + static int io_subchannel_probe (struct subchannel *sch) { @@ -1183,12 +1196,11 @@ io_subchannel_remove (struct subchannel *sch) return 0; } -static int -io_subchannel_notify(struct device *dev, int event) +static int io_subchannel_notify(struct subchannel *sch, int event) { struct ccw_device *cdev; - cdev = dev->driver_data; + cdev = sch->dev.driver_data; if (!cdev) return 0; if (!cdev->drv) @@ -1198,22 +1210,20 @@ io_subchannel_notify(struct device *dev, int event) return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; } -static void -io_subchannel_verify(struct device *dev) +static void io_subchannel_verify(struct subchannel *sch) { struct ccw_device *cdev; - cdev = dev->driver_data; + cdev = sch->dev.driver_data; if (cdev) dev_fsm_event(cdev, DEV_EVENT_VERIFY); } -static void -io_subchannel_ioterm(struct device *dev) +static void io_subchannel_ioterm(struct subchannel *sch) { struct ccw_device *cdev; - cdev = dev->driver_data; + cdev = sch->dev.driver_data; if (!cdev) return; /* Internal I/O will be retried by the interrupt handler. */ diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 0d40896..6183731 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -74,7 +74,6 @@ extern struct workqueue_struct *ccw_device_notify_work; extern wait_queue_head_t ccw_device_init_wq; extern atomic_t ccw_device_init_count; -void io_subchannel_irq (struct device *pdev); void io_subchannel_recog_done(struct ccw_device *cdev); int ccw_device_cancel_halt_clear(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 31c0f78..baca6c6 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -399,7 +399,7 @@ ccw_device_oper_notify(struct work_struct *work) sch = to_subchannel(cdev->dev.parent); if (sch->driver && sch->driver->notify) { spin_unlock_irqrestore(cdev->ccwlock, flags); - ret = sch->driver->notify(&sch->dev, CIO_OPER); + ret = sch->driver->notify(sch, CIO_OPER); spin_lock_irqsave(cdev->ccwlock, flags); } else ret = 0; @@ -1273,21 +1273,4 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { }, }; -/* - * io_subchannel_irq is called for "real" interrupts or for status - * pending conditions on msch. - */ -void -io_subchannel_irq (struct device *pdev) -{ - struct ccw_device *cdev; - - cdev = to_subchannel(pdev)->dev.driver_data; - - CIO_TRACE_EVENT (3, "IRQ"); - CIO_TRACE_EVENT (3, pdev->bus_id); - if (cdev) - dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); -} - EXPORT_SYMBOL_GPL(ccw_device_set_timeout); -- cgit v0.10.2 From 98c13c283a4e8b7f844f1f470abd7c756248fcc4 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:40 +0100 Subject: [S390] cio: Reset sch->driver. sch->driver needs to be reset to NULL on failed probe and after remove. We also need to check for sch->driver on shutdown. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 614cc69..f558686 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -796,32 +796,36 @@ css_bus_match (struct device *dev, struct device_driver *drv) return 0; } -static int -css_probe (struct device *dev) +static int css_probe(struct device *dev) { struct subchannel *sch; + int ret; sch = to_subchannel(dev); sch->driver = to_cssdriver(dev->driver); - return (sch->driver->probe ? sch->driver->probe(sch) : 0); + ret = sch->driver->probe ? sch->driver->probe(sch) : 0; + if (ret) + sch->driver = NULL; + return ret; } -static int -css_remove (struct device *dev) +static int css_remove(struct device *dev) { struct subchannel *sch; + int ret; sch = to_subchannel(dev); - return (sch->driver->remove ? sch->driver->remove(sch) : 0); + ret = sch->driver->remove ? sch->driver->remove(sch) : 0; + sch->driver = NULL; + return ret; } -static void -css_shutdown (struct device *dev) +static void css_shutdown(struct device *dev) { struct subchannel *sch; sch = to_subchannel(dev); - if (sch->driver->shutdown) + if (sch->driver && sch->driver->shutdown) sch->driver->shutdown(sch); } -- cgit v0.10.2 From 25b7bb5838ab81b68a9de72df577103d8b4aba3c Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:41 +0100 Subject: [S390] cio: Add css_driver_{register,unregister}. Add wrapper functions for driver_register and driver_unregister so that css drivers don't need to muck with struct device_driver directly. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index f558686..7d887f8 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -837,6 +837,33 @@ struct bus_type css_bus_type = { .shutdown = css_shutdown, }; +/** + * css_driver_register - register a css driver + * @cdrv: css driver to register + * + * This is mainly a wrapper around driver_register that sets name + * and bus_type in the embedded struct device_driver correctly. + */ +int css_driver_register(struct css_driver *cdrv) +{ + cdrv->drv.name = cdrv->name; + cdrv->drv.bus = &css_bus_type; + return driver_register(&cdrv->drv); +} +EXPORT_SYMBOL_GPL(css_driver_register); + +/** + * css_driver_unregister - unregister a css driver + * @cdrv: css driver to unregister + * + * This is a wrapper around driver_unregister. + */ +void css_driver_unregister(struct css_driver *cdrv) +{ + driver_unregister(&cdrv->drv); +} +EXPORT_SYMBOL_GPL(css_driver_unregister); + subsys_initcall(init_channel_subsystem); MODULE_LICENSE("GPL"); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 32acd24..c9fd600 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -132,6 +132,7 @@ struct css_driver { int (*probe)(struct subchannel *); int (*remove)(struct subchannel *); void (*shutdown)(struct subchannel *); + const char *name; }; #define to_cssdriver(n) container_of(n, struct css_driver, drv) @@ -141,6 +142,9 @@ struct css_driver { */ extern struct bus_type css_bus_type; +extern int css_driver_register(struct css_driver *); +extern void css_driver_unregister(struct css_driver *); + extern void css_sch_device_unregister(struct subchannel *); extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 000c641..7bccca9 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -125,10 +125,7 @@ static void io_subchannel_shutdown(struct subchannel *); static struct css_driver io_subchannel_driver = { .subchannel_type = SUBCHANNEL_TYPE_IO, - .drv = { - .name = "io_subchannel", - .bus = &css_bus_type, - }, + .name = "io_subchannel", .irq = io_subchannel_irq, .notify = io_subchannel_notify, .verify = io_subchannel_verify, @@ -167,7 +164,8 @@ init_ccw_bus_type (void) if ((ret = bus_register (&ccw_bus_type))) goto out_err; - if ((ret = driver_register(&io_subchannel_driver.drv))) + ret = css_driver_register(&io_subchannel_driver); + if (ret) goto out_err; wait_event(ccw_device_init_wq, @@ -187,7 +185,7 @@ out_err: static void __exit cleanup_ccw_bus_type (void) { - driver_unregister(&io_subchannel_driver.drv); + css_driver_unregister(&io_subchannel_driver); bus_unregister(&ccw_bus_type); destroy_workqueue(ccw_device_notify_work); destroy_workqueue(ccw_device_work); -- cgit v0.10.2 From bc698bcf8897363732226dc9ecba044771679996 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:42 +0100 Subject: [S390] cio: Cleanup debug feature usage. Cleanup cio_debug.h. Also make CIO_DEBUG add the "cio:" prefix to the printk string so that it isn't needed for the debug feature. Fix outdated comments for cio_debug_init() and clean it up. Enlarge cio_crw to the same size as cio_msg so we may actually find some relevant information there. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 469b007..cd5475b 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -56,39 +56,37 @@ __setup ("cio_msg=", cio_setup); /* * Function: cio_debug_init - * Initializes three debug logs (under /proc/s390dbf) for common I/O: - * - cio_msg logs the messages which are printk'ed when CONFIG_DEBUG_IO is on + * Initializes three debug logs for common I/O: + * - cio_msg logs generic cio messages * - cio_trace logs the calling of different functions - * - cio_crw logs the messages which are printk'ed when CONFIG_DEBUG_CRW is on - * debug levels depend on CONFIG_DEBUG_IO resp. CONFIG_DEBUG_CRW + * - cio_crw logs machine check related cio messages */ -static int __init -cio_debug_init (void) +static int __init cio_debug_init(void) { - cio_debug_msg_id = debug_register ("cio_msg", 16, 4, 16*sizeof (long)); + cio_debug_msg_id = debug_register("cio_msg", 16, 4, 16 * sizeof(long)); if (!cio_debug_msg_id) goto out_unregister; - debug_register_view (cio_debug_msg_id, &debug_sprintf_view); - debug_set_level (cio_debug_msg_id, 2); - cio_debug_trace_id = debug_register ("cio_trace", 16, 4, 16); + debug_register_view(cio_debug_msg_id, &debug_sprintf_view); + debug_set_level(cio_debug_msg_id, 2); + cio_debug_trace_id = debug_register("cio_trace", 16, 4, 16); if (!cio_debug_trace_id) 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", 4, 4, 16*sizeof (long)); + 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, 4, 16 * sizeof(long)); if (!cio_debug_crw_id) goto out_unregister; - debug_register_view (cio_debug_crw_id, &debug_sprintf_view); - debug_set_level (cio_debug_crw_id, 2); + debug_register_view(cio_debug_crw_id, &debug_sprintf_view); + debug_set_level(cio_debug_crw_id, 4); return 0; out_unregister: if (cio_debug_msg_id) - debug_unregister (cio_debug_msg_id); + debug_unregister(cio_debug_msg_id); if (cio_debug_trace_id) - debug_unregister (cio_debug_trace_id); + debug_unregister(cio_debug_trace_id); if (cio_debug_crw_id) - debug_unregister (cio_debug_crw_id); + debug_unregister(cio_debug_crw_id); printk(KERN_WARNING"cio: could not initialize debugging\n"); return -1; } @@ -567,7 +565,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) */ if (sch->st != 0) { CIO_DEBUG(KERN_INFO, 0, - "cio: Subchannel 0.%x.%04x reports " + "Subchannel 0.%x.%04x reports " "non-I/O subchannel type %04X\n", sch->schid.ssid, sch->schid.sch_no, sch->st); /* We stop here for non-io subchannels. */ @@ -600,7 +598,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) sch->lpm = sch->schib.pmcw.pam & sch->opm; CIO_DEBUG(KERN_INFO, 0, - "cio: Detected device %04x on subchannel 0.%x.%04X" + "Detected device %04x on subchannel 0.%x.%04X" " - PIM = %02X, PAM = %02X, POM = %02X\n", sch->schib.pmcw.dev, sch->schid.ssid, sch->schid.sch_no, sch->schib.pmcw.pim, diff --git a/drivers/s390/cio/cio_debug.h b/drivers/s390/cio/cio_debug.h index c9bf898..d7429ef 100644 --- a/drivers/s390/cio/cio_debug.h +++ b/drivers/s390/cio/cio_debug.h @@ -8,20 +8,19 @@ extern debug_info_t *cio_debug_msg_id; extern debug_info_t *cio_debug_trace_id; extern debug_info_t *cio_debug_crw_id; -#define CIO_TRACE_EVENT(imp, txt) do { \ +#define CIO_TRACE_EVENT(imp, txt) do { \ debug_text_event(cio_debug_trace_id, imp, txt); \ } while (0) -#define CIO_MSG_EVENT(imp, args...) do { \ - debug_sprintf_event(cio_debug_msg_id, imp , ##args); \ +#define CIO_MSG_EVENT(imp, args...) do { \ + debug_sprintf_event(cio_debug_msg_id, imp , ##args); \ } while (0) -#define CIO_CRW_EVENT(imp, args...) do { \ - debug_sprintf_event(cio_debug_crw_id, imp , ##args); \ +#define CIO_CRW_EVENT(imp, args...) do { \ + debug_sprintf_event(cio_debug_crw_id, imp , ##args); \ } while (0) -static inline void -CIO_HEX_EVENT(int level, void *data, int length) +static inline void CIO_HEX_EVENT(int level, void *data, int length) { if (unlikely(!cio_debug_trace_id)) return; @@ -32,9 +31,10 @@ CIO_HEX_EVENT(int level, void *data, int length) } } -#define CIO_DEBUG(printk_level,event_level,msg...) ({ \ - if (cio_show_msg) printk(printk_level msg); \ - CIO_MSG_EVENT (event_level, msg); \ -}) +#define CIO_DEBUG(printk_level, event_level, msg...) do { \ + if (cio_show_msg) \ + printk(printk_level "cio: " msg); \ + CIO_MSG_EVENT(event_level, msg); \ + } while (0) #endif diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index baca6c6..8b6be21 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -318,7 +318,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) switch (state) { case DEV_STATE_NOT_OPER: CIO_DEBUG(KERN_WARNING, 2, - "cio: SenseID : unknown device %04x on subchannel " + "SenseID : unknown device %04x on subchannel " "0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); break; @@ -344,7 +344,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) } /* Issue device info message. */ CIO_DEBUG(KERN_INFO, 2, - "cio: SenseID : device 0.%x.%04x reports: " + "SenseID : device 0.%x.%04x reports: " "CU Type/Mod = %04X/%02X, Dev Type/Mod = " "%04X/%02X\n", cdev->private->dev_id.ssid, @@ -354,7 +354,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) break; case DEV_STATE_BOXED: CIO_DEBUG(KERN_WARNING, 2, - "cio: SenseID : boxed device %04x on subchannel " + "SenseID : boxed device %04x on subchannel " "0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); break; @@ -439,7 +439,7 @@ ccw_device_done(struct ccw_device *cdev, int state) if (state == DEV_STATE_BOXED) CIO_DEBUG(KERN_WARNING, 2, - "cio: Boxed device %04x on subchannel %04x\n", + "Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (cdev->private->flags.donotify) { -- cgit v0.10.2 From cd6b4f27b9bb2a6a5ec82b96b87c85421257be6c Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:43 +0100 Subject: [S390] cio: Introduce subchannel->private. Introduce a private pointer in struct subchannel to store per-subchannel type data (cannot use dev->priv since this is already used for something else). Create a new header io_sch.h for I/O subchannel specific structures and instructions. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index cd5475b..d0bcebd 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -28,6 +28,7 @@ #include "css.h" #include "chsc.h" #include "ioasm.h" +#include "io_sch.h" #include "blacklist.h" #include "cio_debug.h" #include "chp.h" @@ -182,33 +183,35 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ { char dbf_txt[15]; int ccode; + struct orb *orb; - CIO_TRACE_EVENT (4, "stIO"); - CIO_TRACE_EVENT (4, sch->dev.bus_id); + CIO_TRACE_EVENT(4, "stIO"); + CIO_TRACE_EVENT(4, sch->dev.bus_id); + orb = &to_io_private(sch)->orb; /* sch is always under 2G. */ - sch->orb.intparm = (__u32)(unsigned long)sch; - sch->orb.fmt = 1; + orb->intparm = (u32)(addr_t)sch; + orb->fmt = 1; - sch->orb.pfch = sch->options.prefetch == 0; - sch->orb.spnd = sch->options.suspend; - sch->orb.ssic = sch->options.suspend && sch->options.inter; - sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm; + orb->pfch = sch->options.prefetch == 0; + orb->spnd = sch->options.suspend; + orb->ssic = sch->options.suspend && sch->options.inter; + orb->lpm = (lpm != 0) ? lpm : sch->lpm; #ifdef CONFIG_64BIT /* * for 64 bit we always support 64 bit IDAWs with 4k page size only */ - sch->orb.c64 = 1; - sch->orb.i2k = 0; + orb->c64 = 1; + orb->i2k = 0; #endif - sch->orb.key = key >> 4; + orb->key = key >> 4; /* issue "Start Subchannel" */ - sch->orb.cpa = (__u32) __pa (cpa); - ccode = ssch (sch->schid, &sch->orb); + orb->cpa = (__u32) __pa(cpa); + ccode = ssch(sch->schid, orb); /* process condition code */ - sprintf (dbf_txt, "ccode:%d", ccode); - CIO_TRACE_EVENT (4, dbf_txt); + sprintf(dbf_txt, "ccode:%d", ccode); + CIO_TRACE_EVENT(4, dbf_txt); switch (ccode) { case 0: @@ -423,7 +426,7 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc) for (retry = 5, ret = 0; retry > 0; retry--) { sch->schib.pmcw.ena = 1; sch->schib.pmcw.isc = isc; - sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; + sch->schib.pmcw.intparm = (u32)(addr_t)sch; ret = cio_modify(sch); if (ret == -ENODEV) break; @@ -696,8 +699,14 @@ do_IRQ (struct pt_regs *regs) #ifdef CONFIG_CCW_CONSOLE static struct subchannel console_subchannel; +static struct io_subchannel_private console_priv; static int console_subchannel_in_use; +void *cio_get_console_priv(void) +{ + return &console_priv; +} + /* * busy wait for the next interrupt on the console */ @@ -802,7 +811,7 @@ cio_probe_console(void) ctl_set_bit(6, 24); console_subchannel.schib.pmcw.isc = 7; console_subchannel.schib.pmcw.intparm = - (__u32)(unsigned long)&console_subchannel; + (u32)(addr_t)&console_subchannel; ret = cio_modify(&console_subchannel); if (ret) { console_subchannel_in_use = 0; diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 7446c399..a6ef218 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -11,32 +11,32 @@ * path management control word */ struct pmcw { - __u32 intparm; /* interruption parameter */ - __u32 qf : 1; /* qdio facility */ - __u32 res0 : 1; /* reserved zeros */ - __u32 isc : 3; /* interruption sublass */ - __u32 res5 : 3; /* reserved zeros */ - __u32 ena : 1; /* enabled */ - __u32 lm : 2; /* limit mode */ - __u32 mme : 2; /* measurement-mode enable */ - __u32 mp : 1; /* multipath mode */ - __u32 tf : 1; /* timing facility */ - __u32 dnv : 1; /* device number valid */ - __u32 dev : 16; /* device number */ - __u8 lpm; /* logical path mask */ - __u8 pnom; /* path not operational mask */ - __u8 lpum; /* last path used mask */ - __u8 pim; /* path installed mask */ - __u16 mbi; /* measurement-block index */ - __u8 pom; /* path operational mask */ - __u8 pam; /* path available mask */ - __u8 chpid[8]; /* CHPID 0-7 (if available) */ - __u32 unused1 : 8; /* reserved zeros */ - __u32 st : 3; /* subchannel type */ - __u32 unused2 : 18; /* reserved zeros */ - __u32 mbfc : 1; /* measurement block format control */ - __u32 xmwme : 1; /* extended measurement word mode enable */ - __u32 csense : 1; /* concurrent sense; can be enabled ...*/ + u32 intparm; /* interruption parameter */ + u32 qf : 1; /* qdio facility */ + u32 res0 : 1; /* reserved zeros */ + u32 isc : 3; /* interruption sublass */ + u32 res5 : 3; /* reserved zeros */ + u32 ena : 1; /* enabled */ + u32 lm : 2; /* limit mode */ + u32 mme : 2; /* measurement-mode enable */ + u32 mp : 1; /* multipath mode */ + u32 tf : 1; /* timing facility */ + u32 dnv : 1; /* device number valid */ + u32 dev : 16; /* device number */ + u8 lpm; /* logical path mask */ + u8 pnom; /* path not operational mask */ + u8 lpum; /* last path used mask */ + u8 pim; /* path installed mask */ + u16 mbi; /* measurement-block index */ + u8 pom; /* path operational mask */ + u8 pam; /* path available mask */ + u8 chpid[8]; /* CHPID 0-7 (if available) */ + u32 unused1 : 8; /* reserved zeros */ + u32 st : 3; /* subchannel type */ + u32 unused2 : 18; /* reserved zeros */ + u32 mbfc : 1; /* measurement block format control */ + u32 xmwme : 1; /* extended measurement word mode enable */ + u32 csense : 1; /* concurrent sense; can be enabled ...*/ /* ... per MSCH, however, if facility */ /* ... is not installed, this results */ /* ... in an operand exception. */ @@ -52,31 +52,6 @@ struct schib { __u8 mda[4]; /* model dependent area */ } __attribute__ ((packed,aligned(4))); -/* - * operation request block - */ -struct orb { - __u32 intparm; /* interruption parameter */ - __u32 key : 4; /* flags, like key, suspend control, etc. */ - __u32 spnd : 1; /* suspend control */ - __u32 res1 : 1; /* reserved */ - __u32 mod : 1; /* modification control */ - __u32 sync : 1; /* synchronize control */ - __u32 fmt : 1; /* format control */ - __u32 pfch : 1; /* prefetch control */ - __u32 isic : 1; /* initial-status-interruption control */ - __u32 alcc : 1; /* address-limit-checking control */ - __u32 ssic : 1; /* suppress-suspended-interr. control */ - __u32 res2 : 1; /* reserved */ - __u32 c64 : 1; /* IDAW/QDIO 64 bit control */ - __u32 i2k : 1; /* IDAW 2/4kB block size control */ - __u32 lpm : 8; /* logical path mask */ - __u32 ils : 1; /* incorrect length */ - __u32 zero : 6; /* reserved zeros */ - __u32 orbx : 1; /* ORB extension control */ - __u32 cpa; /* channel program address */ -} __attribute__ ((packed,aligned(4))); - /* subchannel data structure used by I/O subroutines */ struct subchannel { struct subchannel_id schid; @@ -99,11 +74,10 @@ struct subchannel { __u8 lpm; /* logical path mask */ __u8 opm; /* operational path mask */ struct schib schib; /* subchannel information block */ - struct orb orb; /* operation request block */ - struct ccw1 sense_ccw; /* static ccw for sense command */ struct chsc_ssd_info ssd_info; /* subchannel description */ struct device dev; /* entry in device tree */ struct css_driver *driver; + void *private; /* private per subchannel type data */ } __attribute__ ((aligned(8))); #define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */ @@ -133,10 +107,12 @@ extern void cio_release_console(void); extern int cio_is_console(struct subchannel_id); extern struct subchannel *cio_get_console_subchannel(void); extern spinlock_t * cio_get_console_lock(void); +extern void *cio_get_console_priv(void); #else #define cio_is_console(schid) 0 #define cio_get_console_subchannel() NULL -#define cio_get_console_lock() NULL; +#define cio_get_console_lock() NULL +#define cio_get_console_priv() NULL #endif extern int cio_show_msg; diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 7d887f8..2520a44 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -77,7 +77,7 @@ css_alloc_subchannel(struct subchannel_id schid) * This is fine even on 64bit since the subchannel is always located * under 2G. */ - sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; + sch->schib.pmcw.intparm = (u32)(addr_t)sch; ret = cio_modify(sch); if (ret) { kfree(sch->lock); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index c9fd600..b2b4a30 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -58,64 +58,6 @@ struct pgid { __u32 tod_high; /* high word TOD clock */ } __attribute__ ((packed)); -#define MAX_CIWS 8 - -/* - * sense-id response buffer layout - */ -struct senseid { - /* common part */ - __u8 reserved; /* always 0x'FF' */ - __u16 cu_type; /* control unit type */ - __u8 cu_model; /* control unit model */ - __u16 dev_type; /* device type */ - __u8 dev_model; /* device model */ - __u8 unused; /* padding byte */ - /* extended part */ - struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */ -} __attribute__ ((packed,aligned(4))); - -struct ccw_device_private { - struct ccw_device *cdev; - struct subchannel *sch; - int state; /* device state */ - atomic_t onoff; - unsigned long registered; - struct ccw_dev_id dev_id; /* device id */ - struct subchannel_id schid; /* subchannel number */ - __u8 imask; /* lpm mask for SNID/SID/SPGID */ - int iretry; /* retry counter SNID/SID/SPGID */ - struct { - unsigned int fast:1; /* post with "channel end" */ - unsigned int repall:1; /* report every interrupt status */ - unsigned int pgroup:1; /* do path grouping */ - unsigned int force:1; /* allow forced online */ - } __attribute__ ((packed)) options; - struct { - unsigned int pgid_single:1; /* use single path for Set PGID */ - unsigned int esid:1; /* Ext. SenseID supported by HW */ - unsigned int dosense:1; /* delayed SENSE required */ - unsigned int doverify:1; /* delayed path verification */ - unsigned int donotify:1; /* call notify function */ - unsigned int recog_done:1; /* dev. recog. complete */ - unsigned int fake_irb:1; /* deliver faked irb */ - unsigned int intretry:1; /* retry internal operation */ - } __attribute__((packed)) flags; - unsigned long intparm; /* user interruption parameter */ - struct qdio_irq *qdio_data; - struct irb irb; /* device status */ - struct senseid senseid; /* SenseID info */ - struct pgid pgid[8]; /* path group IDs per chpid*/ - struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */ - struct work_struct kick_work; - wait_queue_head_t wait_q; - struct timer_list timer; - void *cmb; /* measurement information */ - struct list_head cmb_list; /* list of measured devices */ - u64 cmb_start_time; /* clock value of cmb reset */ - void *cmb_wait; /* deferred cmb enable/disable */ -}; - /* * A css driver handles all subchannels of one type. * Currently, we only care about I/O subchannels (type 0), these diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 7bccca9..30fe59c 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -28,6 +28,7 @@ #include "css.h" #include "device.h" #include "ioasm.h" +#include "io_sch.h" /******************* bus type handling ***********************/ @@ -1143,6 +1144,11 @@ io_subchannel_probe (struct subchannel *sch) */ dev_id.devno = sch->schib.pmcw.dev; dev_id.ssid = sch->schid.ssid; + /* Allocate I/O subchannel private data. */ + sch->private = kzalloc(sizeof(struct io_subchannel_private), + GFP_KERNEL | GFP_DMA); + if (!sch->private) + return -ENOMEM; cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); if (!cdev) cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), @@ -1160,9 +1166,10 @@ io_subchannel_probe (struct subchannel *sch) return 0; } cdev = io_subchannel_create_ccwdev(sch); - if (IS_ERR(cdev)) + if (IS_ERR(cdev)) { + kfree(sch->private); return PTR_ERR(cdev); - + } rc = io_subchannel_recog(cdev, sch); if (rc) { spin_lock_irqsave(sch->lock, flags); @@ -1170,6 +1177,7 @@ io_subchannel_probe (struct subchannel *sch) spin_unlock_irqrestore(sch->lock, flags); if (cdev->dev.release) cdev->dev.release(&cdev->dev); + kfree(sch->private); } return rc; @@ -1191,6 +1199,7 @@ io_subchannel_remove (struct subchannel *sch) spin_unlock_irqrestore(cdev->ccwlock, flags); ccw_device_unregister(cdev); put_device(&cdev->dev); + kfree(sch->private); return 0; } @@ -1279,6 +1288,9 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch) { int rc; + /* Attach subchannel private data. */ + sch->private = cio_get_console_priv(); + memset(sch->private, 0, sizeof(struct io_subchannel_private)); /* Initialize the ccw_device structure. */ cdev->dev.parent= &sch->dev; rc = io_subchannel_recog(cdev, sch); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 6183731..87576fe 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -5,6 +5,8 @@ #include #include +#include "io_sch.h" + /* * states of the device statemachine */ diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 8b6be21..44ce7a3 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -96,29 +96,32 @@ static void ccw_timeout_log(struct ccw_device *cdev) { struct schib schib; struct subchannel *sch; + struct io_subchannel_private *private; int cc; sch = to_subchannel(cdev->dev.parent); + private = to_io_private(sch); cc = stsch(sch->schid, &schib); printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, " "device information:\n", get_clock()); printk(KERN_WARNING "cio: orb:\n"); print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, - &sch->orb, sizeof(sch->orb), 0); + &private->orb, sizeof(private->orb), 0); printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id); printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id); printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, " "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm); - if ((void *)(addr_t)sch->orb.cpa == &sch->sense_ccw || - (void *)(addr_t)sch->orb.cpa == cdev->private->iccws) + if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw || + (void *)(addr_t)private->orb.cpa == cdev->private->iccws) printk(KERN_WARNING "cio: last channel program (intern):\n"); else printk(KERN_WARNING "cio: last channel program:\n"); print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, - (void *)(addr_t)sch->orb.cpa, sizeof(struct ccw1), 0); + (void *)(addr_t)private->orb.cpa, + sizeof(struct ccw1), 0); printk(KERN_WARNING "cio: ccw device state: %d\n", cdev->private->state); printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc); @@ -1078,7 +1081,7 @@ device_trigger_reprobe(struct subchannel *sch) sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) sch->schib.pmcw.mp = 1; - sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; + sch->schib.pmcw.intparm = (u32)(addr_t)sch; /* We should also udate ssd info, but this has to wait. */ /* Check if this is another device which appeared on the same sch. */ if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 156f3f9..918b8b8 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -24,6 +24,7 @@ #include "css.h" #include "device.h" #include "ioasm.h" +#include "io_sch.h" /* * Input : @@ -219,11 +220,13 @@ ccw_device_check_sense_id(struct ccw_device *cdev) return -EAGAIN; } if (irb->scsw.cc == 3) { - if ((sch->orb.lpm & - sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) + u8 lpm; + + lpm = to_io_private(sch)->orb.lpm; + if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x " "on subchannel 0.%x.%04x is " - "'not operational'\n", sch->orb.lpm, + "'not operational'\n", lpm, cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); return -EACCES; diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index cb1879a..c52449a 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -22,6 +22,7 @@ #include "css.h" #include "device.h" #include "ioasm.h" +#include "io_sch.h" /* * Helper function called from interrupt context to decide whether an @@ -155,10 +156,13 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) return -EAGAIN; } if (irb->scsw.cc == 3) { + u8 lpm; + + lpm = to_io_private(sch)->orb.lpm; CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x," " lpm %02X, became 'not operational'\n", cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, sch->orb.lpm); + sch->schid.sch_no, lpm); return -EACCES; } i = 8 - ffs(cdev->private->imask); diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index aa96e67..ebe0848 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -20,6 +20,7 @@ #include "css.h" #include "device.h" #include "ioasm.h" +#include "io_sch.h" /* * Check for any kind of channel or interface control check but don't @@ -310,6 +311,7 @@ int ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) { struct subchannel *sch; + struct ccw1 *sense_ccw; sch = to_subchannel(cdev->dev.parent); @@ -326,15 +328,16 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) /* * We have ending status but no sense information. Do a basic sense. */ - sch->sense_ccw.cmd_code = CCW_CMD_BASIC_SENSE; - sch->sense_ccw.cda = (__u32) __pa(cdev->private->irb.ecw); - sch->sense_ccw.count = SENSE_MAX_COUNT; - sch->sense_ccw.flags = CCW_FLAG_SLI; + sense_ccw = &to_io_private(sch)->sense_ccw; + sense_ccw->cmd_code = CCW_CMD_BASIC_SENSE; + sense_ccw->cda = (__u32) __pa(cdev->private->irb.ecw); + sense_ccw->count = SENSE_MAX_COUNT; + sense_ccw->flags = CCW_FLAG_SLI; /* Reset internal retry indication. */ cdev->private->flags.intretry = 0; - return cio_start (sch, &sch->sense_ccw, 0xff); + return cio_start(sch, sense_ccw, 0xff); } /* diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h new file mode 100644 index 0000000..d49e5a7 --- /dev/null +++ b/drivers/s390/cio/io_sch.h @@ -0,0 +1,161 @@ +#ifndef S390_IO_SCH_H +#define S390_IO_SCH_H + +#include "schid.h" + +/* + * operation request block + */ +struct orb { + u32 intparm; /* interruption parameter */ + u32 key : 4; /* flags, like key, suspend control, etc. */ + u32 spnd : 1; /* suspend control */ + u32 res1 : 1; /* reserved */ + u32 mod : 1; /* modification control */ + u32 sync : 1; /* synchronize control */ + u32 fmt : 1; /* format control */ + u32 pfch : 1; /* prefetch control */ + u32 isic : 1; /* initial-status-interruption control */ + u32 alcc : 1; /* address-limit-checking control */ + u32 ssic : 1; /* suppress-suspended-interr. control */ + u32 res2 : 1; /* reserved */ + u32 c64 : 1; /* IDAW/QDIO 64 bit control */ + u32 i2k : 1; /* IDAW 2/4kB block size control */ + u32 lpm : 8; /* logical path mask */ + u32 ils : 1; /* incorrect length */ + u32 zero : 6; /* reserved zeros */ + u32 orbx : 1; /* ORB extension control */ + u32 cpa; /* channel program address */ +} __attribute__ ((packed, aligned(4))); + +struct io_subchannel_private { + struct orb orb; /* operation request block */ + struct ccw1 sense_ccw; /* static ccw for sense command */ +} __attribute__ ((aligned(8))); + +#define to_io_private(n) ((struct io_subchannel_private *)n->private) + +#define MAX_CIWS 8 + +/* + * sense-id response buffer layout + */ +struct senseid { + /* common part */ + u8 reserved; /* always 0x'FF' */ + u16 cu_type; /* control unit type */ + u8 cu_model; /* control unit model */ + u16 dev_type; /* device type */ + u8 dev_model; /* device model */ + u8 unused; /* padding byte */ + /* extended part */ + struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */ +} __attribute__ ((packed, aligned(4))); + +struct ccw_device_private { + struct ccw_device *cdev; + struct subchannel *sch; + int state; /* device state */ + atomic_t onoff; + unsigned long registered; + struct ccw_dev_id dev_id; /* device id */ + struct subchannel_id schid; /* subchannel number */ + u8 imask; /* lpm mask for SNID/SID/SPGID */ + int iretry; /* retry counter SNID/SID/SPGID */ + struct { + unsigned int fast:1; /* post with "channel end" */ + unsigned int repall:1; /* report every interrupt status */ + unsigned int pgroup:1; /* do path grouping */ + unsigned int force:1; /* allow forced online */ + } __attribute__ ((packed)) options; + struct { + unsigned int pgid_single:1; /* use single path for Set PGID */ + unsigned int esid:1; /* Ext. SenseID supported by HW */ + unsigned int dosense:1; /* delayed SENSE required */ + unsigned int doverify:1; /* delayed path verification */ + unsigned int donotify:1; /* call notify function */ + unsigned int recog_done:1; /* dev. recog. complete */ + unsigned int fake_irb:1; /* deliver faked irb */ + unsigned int intretry:1; /* retry internal operation */ + } __attribute__((packed)) flags; + unsigned long intparm; /* user interruption parameter */ + struct qdio_irq *qdio_data; + struct irb irb; /* device status */ + struct senseid senseid; /* SenseID info */ + struct pgid pgid[8]; /* path group IDs per chpid*/ + struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */ + struct work_struct kick_work; + wait_queue_head_t wait_q; + struct timer_list timer; + void *cmb; /* measurement information */ + struct list_head cmb_list; /* list of measured devices */ + u64 cmb_start_time; /* clock value of cmb reset */ + void *cmb_wait; /* deferred cmb enable/disable */ +}; + +static inline int ssch(struct subchannel_id schid, volatile struct orb *addr) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " ssch 0(%2)\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); + return ccode; +} + +static inline int rsch(struct subchannel_id schid) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " rsch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); + return ccode; +} + +static inline int csch(struct subchannel_id schid) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " csch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); + return ccode; +} + +static inline int hsch(struct subchannel_id schid) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " hsch\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); + return ccode; +} + +static inline int xsch(struct subchannel_id schid) +{ + register struct subchannel_id reg1 asm("1") = schid; + int ccode; + + asm volatile( + " .insn rre,0xb2760000,%1,0\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "d" (reg1) : "cc"); + return ccode; +} + +#endif diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index 7153dd9..652ea36 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -109,72 +109,6 @@ static inline int tpi( volatile struct tpi_info *addr) return ccode; } -static inline int ssch(struct subchannel_id schid, - volatile struct orb *addr) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " ssch 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); - return ccode; -} - -static inline int rsch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " rsch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1) : "cc"); - return ccode; -} - -static inline int csch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " csch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1) : "cc"); - return ccode; -} - -static inline int hsch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " hsch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1) : "cc"); - return ccode; -} - -static inline int xsch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " .insn rre,0xb2760000,%1,0\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1) : "cc"); - return ccode; -} - static inline int chsc(void *chsc_area) { typedef struct { char _[4096]; } addr_type; -- cgit v0.10.2 From 4e8e56c6713398f417317d449f50c08bf2756c66 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Sat, 26 Jan 2008 14:10:44 +0100 Subject: [S390] cio: Extend adapter interrupt interface. From: Cornelia Huck Change the adapter interrupt interface in order to allow multiple adapter interrupt handlers to be registered. Indicators are now allocated by cio instead of the device driver. The qdio parts have been Acked-by: Ursula Braun Signed-off-by: Peter Oberparleiter Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/Documentation/DocBook/s390-drivers.tmpl b/Documentation/DocBook/s390-drivers.tmpl index 254e769..3d2f31b 100644 --- a/Documentation/DocBook/s390-drivers.tmpl +++ b/Documentation/DocBook/s390-drivers.tmpl @@ -116,6 +116,7 @@ !Iinclude/asm-s390/ccwdev.h !Edrivers/s390/cio/device.c !Edrivers/s390/cio/device_ops.c +!Edrivers/s390/cio/airq.c The channel-measurement facility diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 5287631..b7a07a8 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -1,12 +1,12 @@ /* * drivers/s390/cio/airq.c - * S/390 common I/O routines -- support for adapter interruptions + * Support for adapter interruptions * - * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Ingo Adlung (adlung@de.ibm.com) - * Cornelia Huck (cornelia.huck@de.ibm.com) - * Arnd Bergmann (arndb@de.ibm.com) + * Copyright IBM Corp. 1999,2007 + * Author(s): Ingo Adlung + * Cornelia Huck + * Arnd Bergmann + * Peter Oberparleiter */ #include @@ -14,72 +14,131 @@ #include #include +#include + +#include "cio.h" #include "cio_debug.h" -#include "airq.h" -static adapter_int_handler_t adapter_handler; +#define NR_AIRQS 32 +#define NR_AIRQS_PER_WORD sizeof(unsigned long) +#define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) -/* - * register for adapter interrupts - * - * With HiperSockets the zSeries architecture provides for - * means of adapter interrups, pseudo I/O interrupts that are - * not tied to an I/O subchannel, but to an adapter. However, - * it doesn't disclose the info how to enable/disable them, but - * to recognize them only. Perhaps we should consider them - * being shared interrupts, and thus build a linked list - * of adapter handlers ... to be evaluated ... - */ -int -s390_register_adapter_interrupt (adapter_int_handler_t handler) -{ - int ret; - char dbf_txt[15]; +union indicator_t { + unsigned long word[NR_AIRQ_WORDS]; + unsigned char byte[NR_AIRQS]; +} __attribute__((packed)); - CIO_TRACE_EVENT (4, "rgaint"); +struct airq_t { + adapter_int_handler_t handler; + void *drv_data; +}; - if (handler == NULL) - ret = -EINVAL; - else - ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0); - if (!ret) - synchronize_sched(); /* Allow interrupts to complete. */ +static union indicator_t indicators; +static struct airq_t *airqs[NR_AIRQS]; - sprintf (dbf_txt, "ret:%d", ret); - CIO_TRACE_EVENT (4, dbf_txt); +static int register_airq(struct airq_t *airq) +{ + int i; - return ret; + for (i = 0; i < NR_AIRQS; i++) + if (!cmpxchg(&airqs[i], NULL, airq)) + return i; + return -ENOMEM; } -int -s390_unregister_adapter_interrupt (adapter_int_handler_t handler) +/** + * s390_register_adapter_interrupt() - register adapter interrupt handler + * @handler: adapter handler to be registered + * @drv_data: driver data passed with each call to the handler + * + * Returns: + * Pointer to the indicator to be used on success + * ERR_PTR() if registration failed + */ +void *s390_register_adapter_interrupt(adapter_int_handler_t handler, + void *drv_data) { + struct airq_t *airq; + char dbf_txt[16]; int ret; - char dbf_txt[15]; - CIO_TRACE_EVENT (4, "urgaint"); - - if (handler == NULL) - ret = -EINVAL; - else { - adapter_handler = NULL; - synchronize_sched(); /* Allow interrupts to complete. */ - ret = 0; + airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); + if (!airq) { + ret = -ENOMEM; + goto out; } - sprintf (dbf_txt, "ret:%d", ret); - CIO_TRACE_EVENT (4, dbf_txt); - - return ret; + airq->handler = handler; + airq->drv_data = drv_data; + ret = register_airq(airq); + if (ret < 0) + kfree(airq); +out: + snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); + CIO_TRACE_EVENT(4, dbf_txt); + if (ret < 0) + return ERR_PTR(ret); + else + return &indicators.byte[ret]; } +EXPORT_SYMBOL(s390_register_adapter_interrupt); -void -do_adapter_IO (void) +/** + * s390_unregister_adapter_interrupt - unregister adapter interrupt handler + * @ind: indicator for which the handler is to be unregistered + */ +void s390_unregister_adapter_interrupt(void *ind) { - CIO_TRACE_EVENT (6, "doaio"); + struct airq_t *airq; + char dbf_txt[16]; + int i; - if (adapter_handler) - (*adapter_handler) (); + i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]); + snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); + CIO_TRACE_EVENT(4, dbf_txt); + indicators.byte[i] = 0; + airq = xchg(&airqs[i], NULL); + /* + * Allow interrupts to complete. This will ensure that the airq handle + * is no longer referenced by any interrupt handler. + */ + synchronize_sched(); + kfree(airq); } +EXPORT_SYMBOL(s390_unregister_adapter_interrupt); + +#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) -EXPORT_SYMBOL (s390_register_adapter_interrupt); -EXPORT_SYMBOL (s390_unregister_adapter_interrupt); +void do_adapter_IO(void) +{ + int w; + int i; + unsigned long word; + struct airq_t *airq; + + /* + * Access indicator array in word-sized chunks to minimize storage + * fetch operations. + */ + for (w = 0; w < NR_AIRQ_WORDS; w++) { + word = indicators.word[w]; + i = w * NR_AIRQS_PER_WORD; + /* + * Check bytes within word for active indicators. + */ + while (word) { + if (word & INDICATOR_MASK) { + airq = airqs[i]; + if (likely(airq)) + airq->handler(&indicators.byte[i], + airq->drv_data); + else + /* + * Reset ill-behaved indicator. + */ + indicators.byte[i] = 0; + } + word <<= 8; + i++; + } + } +} diff --git a/drivers/s390/cio/airq.h b/drivers/s390/cio/airq.h deleted file mode 100644 index 7d6be3f..0000000 --- a/drivers/s390/cio/airq.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef S390_AINTERRUPT_H -#define S390_AINTERRUPT_H - -typedef int (*adapter_int_handler_t)(void); - -extern int s390_register_adapter_interrupt(adapter_int_handler_t handler); -extern int s390_unregister_adapter_interrupt(adapter_int_handler_t handler); -extern void do_adapter_IO (void); - -#endif diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index d0bcebd..89ced34 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -23,7 +23,7 @@ #include #include #include -#include "airq.h" +#include #include "cio.h" #include "css.h" #include "chsc.h" diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index a6ef218..d1483d6 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -99,6 +99,7 @@ extern int cio_get_options (struct subchannel *); extern int cio_modify (struct subchannel *); int cio_create_sch_lock(struct subchannel *); +void do_adapter_IO(void); /* Use with care. */ #ifdef CONFIG_CCW_CONSOLE diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 40a3208..7a353db 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -48,11 +48,11 @@ #include #include #include +#include #include "cio.h" #include "css.h" #include "device.h" -#include "airq.h" #include "qdio.h" #include "ioasm.h" #include "chsc.h" @@ -96,7 +96,7 @@ static debug_info_t *qdio_dbf_slsb_in; static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change during a while loop */ static DEFINE_SPINLOCK(ttiq_list_lock); -static int register_thinint_result; +static void *tiqdio_ind; static void tiqdio_tl(unsigned long); static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0); @@ -399,7 +399,7 @@ qdio_get_indicator(void) { int i; - for (i=1;idev_st_chg_ind); } @@ -3740,23 +3738,25 @@ static void tiqdio_register_thinints(void) { char dbf_text[20]; - register_thinint_result= - s390_register_adapter_interrupt(&tiqdio_thinint_handler); - if (register_thinint_result) { - sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff)); + + tiqdio_ind = + s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL); + if (IS_ERR(tiqdio_ind)) { + sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind)); QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_PRINT_ERR("failed to register adapter handler " \ - "(rc=%i).\nAdapter interrupts might " \ + "(rc=%li).\nAdapter interrupts might " \ "not work. Continuing.\n", - register_thinint_result); + PTR_ERR(tiqdio_ind)); + tiqdio_ind = NULL; } } static void tiqdio_unregister_thinints(void) { - if (!register_thinint_result) - s390_unregister_adapter_interrupt(&tiqdio_thinint_handler); + if (tiqdio_ind) + s390_unregister_adapter_interrupt(tiqdio_ind); } static int @@ -3768,8 +3768,8 @@ qdio_get_qdio_memory(void) for (i=1;i + * Cornelia Huck + * Arnd Bergmann + * Peter Oberparleiter + */ + +#ifndef _ASM_S390_AIRQ_H +#define _ASM_S390_AIRQ_H + +typedef void (*adapter_int_handler_t)(void *, void *); + +void *s390_register_adapter_interrupt(adapter_int_handler_t, void *); +void s390_unregister_adapter_interrupt(void *); + +#endif /* _ASM_S390_AIRQ_H */ -- cgit v0.10.2 From b279a4f56d5476a0b9b0a97397f7a7bbe00b9b2f Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:45 +0100 Subject: [S390] cio: I/O subchannel specific fields. Some fields may be !0 only for I/O subchannels. Add some checks where required. Also adapt cio_enable_subchannel() to make the caller specify the intparm, which makes it more generic. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 44a2ea4..12a344c 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -89,7 +89,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) /* Copy data */ ret = 0; memset(ssd, 0, sizeof(struct chsc_ssd_info)); - if ((ssd_area->st != 0) && (ssd_area->st != 2)) + if ((ssd_area->st != SUBCHANNEL_TYPE_IO) && + (ssd_area->st != SUBCHANNEL_TYPE_MSG)) goto out_free; ssd->path_mask = ssd_area->path_mask; ssd->fla_valid_mask = ssd_area->fla_valid_mask; @@ -158,7 +159,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) spin_lock_irq(sch->lock); stsch(sch->schid, &schib); - if (!schib.pmcw.dnv) + if (!css_sch_is_valid(&schib)) goto out_unreg; memcpy(&sch->schib, &schib, sizeof(struct schib)); /* Check for single path devices. */ diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 89ced34..f16c15c 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -406,8 +406,8 @@ cio_modify (struct subchannel *sch) /* * Enable subchannel. */ -int -cio_enable_subchannel (struct subchannel *sch, unsigned int isc) +int cio_enable_subchannel(struct subchannel *sch, unsigned int isc, + u32 intparm) { char dbf_txt[15]; int ccode; @@ -426,7 +426,7 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc) for (retry = 5, ret = 0; retry > 0; retry--) { sch->schib.pmcw.ena = 1; sch->schib.pmcw.isc = isc; - sch->schib.pmcw.intparm = (u32)(addr_t)sch; + sch->schib.pmcw.intparm = intparm; ret = cio_modify(sch); if (ret == -ENODEV) break; @@ -577,11 +577,8 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) } /* Initialization for io subchannels. */ - if (!sch->schib.pmcw.dnv) { - /* io subchannel but device number is invalid. */ - err = -ENODEV; - goto out; - } + if (!css_sch_is_valid(&sch->schib)) + return -ENODEV; /* Devno is valid. */ if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) { /* @@ -745,9 +742,9 @@ cio_test_for_console(struct subchannel_id schid, void *data) { if (stsch_err(schid, &console_subchannel.schib) != 0) return -ENXIO; - if (console_subchannel.schib.pmcw.dnv && - console_subchannel.schib.pmcw.dev == - console_devno) { + if ((console_subchannel.schib.pmcw.st == SUBCHANNEL_TYPE_IO) && + console_subchannel.schib.pmcw.dnv && + (console_subchannel.schib.pmcw.dev == console_devno)) { console_irq = schid.sch_no; return 1; /* found */ } @@ -765,6 +762,7 @@ cio_get_console_sch_no(void) /* VM provided us with the irq number of the console. */ schid.sch_no = console_irq; if (stsch(schid, &console_subchannel.schib) != 0 || + (console_subchannel.schib.pmcw.st != SUBCHANNEL_TYPE_IO) || !console_subchannel.schib.pmcw.dnv) return -1; console_devno = console_subchannel.schib.pmcw.dev; @@ -1029,7 +1027,7 @@ static int __reipl_subchannel_match(struct subchannel_id schid, void *data) if (stsch_reset(schid, &schib)) return -ENXIO; - if (schib.pmcw.dnv && + if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv && (schib.pmcw.dev == match_id->devid.devno) && (schid.ssid == match_id->devid.ssid)) { match_id->schid = schid; @@ -1075,6 +1073,8 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo) return -ENODEV; if (stsch(schid, &schib)) return -ENODEV; + if (schib.pmcw.st != SUBCHANNEL_TYPE_IO) + return -ENODEV; if (!schib.pmcw.dnv) return -ENODEV; iplinfo->devno = schib.pmcw.dev; diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index d1483d6..52afa4c 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -60,7 +60,7 @@ struct subchannel { enum { SUBCHANNEL_TYPE_IO = 0, SUBCHANNEL_TYPE_CHSC = 1, - SUBCHANNEL_TYPE_MESSAGE = 2, + SUBCHANNEL_TYPE_MSG = 2, SUBCHANNEL_TYPE_ADM = 3, } st; /* subchannel type */ @@ -85,7 +85,7 @@ struct subchannel { #define to_subchannel(n) container_of(n, struct subchannel, dev) extern int cio_validate_subchannel (struct subchannel *, struct subchannel_id); -extern int cio_enable_subchannel (struct subchannel *, unsigned int); +extern int cio_enable_subchannel(struct subchannel *, unsigned int, u32); extern int cio_disable_subchannel (struct subchannel *); extern int cio_cancel (struct subchannel *); extern int cio_clear (struct subchannel *); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 2520a44..4d98a0fd 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -237,11 +237,25 @@ get_subchannel_by_schid(struct subchannel_id schid) return dev ? to_subchannel(dev) : NULL; } +/** + * css_sch_is_valid() - check if a subchannel is valid + * @schib: subchannel information block for the subchannel + */ +int css_sch_is_valid(struct schib *schib) +{ + if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv) + return 0; + return 1; +} +EXPORT_SYMBOL_GPL(css_sch_is_valid); + static int css_get_subchannel_status(struct subchannel *sch) { struct schib schib; - if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) + if (stsch(sch->schid, &schib)) + return CIO_GONE; + if (!css_sch_is_valid(&schib)) return CIO_GONE; if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) return CIO_REVALIDATE; @@ -349,7 +363,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) /* Will be done on the slow path. */ return -EAGAIN; } - if (stsch_err(schid, &schib) || !schib.pmcw.dnv) { + if (stsch_err(schid, &schib) || !css_sch_is_valid(&schib)) { /* Unusable - ignore. */ return 0; } diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index b2b4a30..ee65761 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -136,6 +136,8 @@ void css_schedule_eval(struct subchannel_id schid); void css_schedule_eval_all(void); int sch_is_pseudo_sch(struct subchannel *); +struct schib; +int css_sch_is_valid(struct schib *); extern struct workqueue_struct *slow_path_wq; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 44ce7a3..4804d94 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -553,7 +553,8 @@ ccw_device_recognition(struct ccw_device *cdev) (cdev->private->state != DEV_STATE_BOXED)) return -EINVAL; sch = to_subchannel(cdev->dev.parent); - ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc); + ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc, + (u32)(addr_t)sch); if (ret != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return ret; @@ -663,7 +664,8 @@ ccw_device_online(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); if (css_init_done && !get_device(&cdev->dev)) return -ENODEV; - ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc); + ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc, + (u32)(addr_t)sch); if (ret != 0) { /* Couldn't enable the subchannel for i/o. Sick device. */ if (ret == -ENODEV) @@ -1043,7 +1045,8 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); - if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0) + if (cio_enable_subchannel(sch, sch->schib.pmcw.isc, + (u32)(addr_t)sch) != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return; diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 7fd2dad..49b58eb 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -501,7 +501,7 @@ ccw_device_stlck(struct ccw_device *cdev) return -ENOMEM; } spin_lock_irqsave(sch->lock, flags); - ret = cio_enable_subchannel(sch, 3); + ret = cio_enable_subchannel(sch, 3, (u32)(addr_t)sch); if (ret) goto out_unlock; /* -- cgit v0.10.2 From db6a64238a927777e6e7b251927313f186455b1c Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:46 +0100 Subject: [S390] cio: Use dev_{g,s}et_drvdata(). Also define helpers sch_{g,s}et_cdev() to make the intention more clear. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index b3e2c16..b9997fb 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -111,7 +111,7 @@ ccwgroup_release (struct device *dev) gdev = to_ccwgroupdev(dev); for (i = 0; i < gdev->count; i++) { - gdev->cdev[i]->dev.driver_data = NULL; + dev_set_drvdata(&gdev->cdev[i]->dev, NULL); put_device(&gdev->cdev[i]->dev); } kfree(gdev); @@ -196,11 +196,11 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, goto error; } /* Don't allow a device to belong to more than one group. */ - if (gdev->cdev[i]->dev.driver_data) { + if (dev_get_drvdata(&gdev->cdev[i]->dev)) { rc = -EINVAL; goto error; } - gdev->cdev[i]->dev.driver_data = gdev; + dev_set_drvdata(&gdev->cdev[i]->dev, gdev); } gdev->creator_id = creator_id; @@ -234,8 +234,8 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, error: 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; + if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) + dev_set_drvdata(&gdev->cdev[i]->dev, NULL); put_device(&gdev->cdev[i]->dev); } mutex_unlock(&gdev->reg_mutex); @@ -463,8 +463,8 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) { struct ccwgroup_device *gdev; - if (cdev->dev.driver_data) { - gdev = (struct ccwgroup_device *)cdev->dev.driver_data; + gdev = dev_get_drvdata(&cdev->dev); + if (gdev) { if (get_device(&gdev->dev)) { mutex_lock(&gdev->reg_mutex); if (device_is_registered(&gdev->dev)) diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 30fe59c..933f763 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -773,7 +773,7 @@ static void sch_attach_device(struct subchannel *sch, { css_update_ssd_info(sch); spin_lock_irq(sch->lock); - sch->dev.driver_data = cdev; + sch_set_cdev(sch, cdev); cdev->private->schid = sch->schid; cdev->ccwlock = sch->lock; device_trigger_reprobe(sch); @@ -795,7 +795,7 @@ static void sch_attach_disconnected_device(struct subchannel *sch, put_device(&other_sch->dev); return; } - other_sch->dev.driver_data = NULL; + sch_set_cdev(other_sch, NULL); /* No need to keep a subchannel without ccw device around. */ css_sch_device_unregister(other_sch); put_device(&other_sch->dev); @@ -831,12 +831,12 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) return; } spin_lock_irq(sch->lock); - sch->dev.driver_data = cdev; + sch_set_cdev(sch, cdev); spin_unlock_irq(sch->lock); /* Start recognition for the new ccw device. */ if (io_subchannel_recog(cdev, sch)) { spin_lock_irq(sch->lock); - sch->dev.driver_data = NULL; + sch_set_cdev(sch, NULL); spin_unlock_irq(sch->lock); if (cdev->dev.release) cdev->dev.release(&cdev->dev); @@ -940,7 +940,7 @@ io_subchannel_register(struct work_struct *work) cdev->private->dev_id.devno, ret); put_device(&cdev->dev); spin_lock_irqsave(sch->lock, flags); - sch->dev.driver_data = NULL; + sch_set_cdev(sch, NULL); spin_unlock_irqrestore(sch->lock, flags); kfree (cdev->private); kfree (cdev); @@ -1022,7 +1022,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) int rc; struct ccw_device_private *priv; - sch->dev.driver_data = cdev; + sch_set_cdev(sch, cdev); sch->driver = &io_subchannel_driver; cdev->ccwlock = sch->lock; @@ -1082,7 +1082,7 @@ static void ccw_device_move_to_sch(struct work_struct *work) } if (former_parent) { spin_lock_irq(former_parent->lock); - former_parent->dev.driver_data = NULL; + sch_set_cdev(former_parent, NULL); spin_unlock_irq(former_parent->lock); css_sch_device_unregister(former_parent); /* Reset intparm to zeroes. */ @@ -1100,7 +1100,7 @@ static void io_subchannel_irq(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); CIO_TRACE_EVENT(3, "IRQ"); CIO_TRACE_EVENT(3, sch->dev.bus_id); @@ -1116,13 +1116,13 @@ io_subchannel_probe (struct subchannel *sch) unsigned long flags; struct ccw_dev_id dev_id; - if (sch->dev.driver_data) { + cdev = sch_get_cdev(sch); + if (cdev) { /* * This subchannel already has an associated ccw_device. * Register it and exit. This happens for all early * device, e.g. the console. */ - cdev = sch->dev.driver_data; cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); ccw_device_register(cdev); @@ -1173,7 +1173,7 @@ io_subchannel_probe (struct subchannel *sch) rc = io_subchannel_recog(cdev, sch); if (rc) { spin_lock_irqsave(sch->lock, flags); - sch->dev.driver_data = NULL; + sch_set_cdev(sch, NULL); spin_unlock_irqrestore(sch->lock, flags); if (cdev->dev.release) cdev->dev.release(&cdev->dev); @@ -1189,12 +1189,12 @@ io_subchannel_remove (struct subchannel *sch) struct ccw_device *cdev; unsigned long flags; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return 0; - cdev = sch->dev.driver_data; /* Set ccw device to not operational and drop reference. */ spin_lock_irqsave(cdev->ccwlock, flags); - sch->dev.driver_data = NULL; + sch_set_cdev(sch, NULL); cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); ccw_device_unregister(cdev); @@ -1207,7 +1207,7 @@ static int io_subchannel_notify(struct subchannel *sch, int event) { struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (!cdev) return 0; if (!cdev->drv) @@ -1221,7 +1221,7 @@ static void io_subchannel_verify(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (cdev) dev_fsm_event(cdev, DEV_EVENT_VERIFY); } @@ -1230,7 +1230,7 @@ static void io_subchannel_ioterm(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (!cdev) return; /* Internal I/O will be retried by the interrupt handler. */ @@ -1248,7 +1248,7 @@ io_subchannel_shutdown(struct subchannel *sch) struct ccw_device *cdev; int ret; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (cio_is_console(sch->schid)) return; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 4804d94..669e6a7 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -32,9 +32,9 @@ device_is_online(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return 0; - cdev = sch->dev.driver_data; return (cdev->private->state == DEV_STATE_ONLINE); } @@ -43,9 +43,9 @@ device_is_disconnected(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return 0; - cdev = sch->dev.driver_data; return (cdev->private->state == DEV_STATE_DISCONNECTED || cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID); } @@ -55,9 +55,9 @@ device_set_disconnected(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return; - cdev = sch->dev.driver_data; ccw_device_set_timeout(cdev, 0); cdev->private->flags.fake_irb = 0; cdev->private->state = DEV_STATE_DISCONNECTED; @@ -67,7 +67,7 @@ void device_set_intretry(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (!cdev) return; cdev->private->flags.intretry = 1; @@ -77,7 +77,7 @@ int device_trigger_verify(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); if (!cdev || !cdev->online) return -EINVAL; dev_fsm_event(cdev, DEV_EVENT_VERIFY); @@ -175,9 +175,9 @@ device_kill_pending_timer(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return; - cdev = sch->dev.driver_data; ccw_device_set_timeout(cdev, 0); } @@ -992,7 +992,7 @@ void device_kill_io(struct subchannel *sch) int ret; struct ccw_device *cdev; - cdev = sch->dev.driver_data; + cdev = sch_get_cdev(sch); ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); @@ -1062,9 +1062,9 @@ device_trigger_reprobe(struct subchannel *sch) { struct ccw_device *cdev; - if (!sch->dev.driver_data) + cdev = sch_get_cdev(sch); + if (!cdev) return; - cdev = sch->dev.driver_data; if (cdev->private->state != DEV_STATE_DISCONNECTED) return; diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index d49e5a7..8c61316 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -34,6 +34,8 @@ struct io_subchannel_private { } __attribute__ ((aligned(8))); #define to_io_private(n) ((struct io_subchannel_private *)n->private) +#define sch_get_cdev(n) (dev_get_drvdata(&n->dev)) +#define sch_set_cdev(n, c) (dev_set_drvdata(&n->dev, c)) #define MAX_CIWS 8 -- cgit v0.10.2 From 4beee64685e116b01c47655daf6d88df87e053c8 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:47 +0100 Subject: [S390] cio: Set driver->owner on css, ccw and ccwgroup busses. Set the owner field in the embedded struct device_driver to the value provided in the {css,ccw,ccwgroup}_driver. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index b9997fb..dc1c5f4 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -408,6 +408,7 @@ int ccwgroup_driver_register(struct ccwgroup_driver *cdriver) /* register our new driver with the core */ cdriver->driver.bus = &ccwgroup_bus_type; cdriver->driver.name = cdriver->name; + cdriver->driver.owner = cdriver->owner; return driver_register(&cdriver->driver); } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 4d98a0fd..69d56c7 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -862,6 +862,7 @@ int css_driver_register(struct css_driver *cdrv) { cdrv->drv.name = cdrv->name; cdrv->drv.bus = &css_bus_type; + cdrv->drv.owner = cdrv->owner; return driver_register(&cdrv->drv); } EXPORT_SYMBOL_GPL(css_driver_register); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index ee65761..40598b8 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -65,6 +65,7 @@ struct pgid { */ struct subchannel; struct css_driver { + struct module *owner; unsigned int subchannel_type; struct device_driver drv; void (*irq)(struct subchannel *); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 933f763..f80d7f5 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -125,6 +125,7 @@ static void io_subchannel_ioterm(struct subchannel *); static void io_subchannel_shutdown(struct subchannel *); static struct css_driver io_subchannel_driver = { + .owner = THIS_MODULE, .subchannel_type = SUBCHANNEL_TYPE_IO, .name = "io_subchannel", .irq = io_subchannel_irq, @@ -1476,6 +1477,7 @@ int ccw_driver_register(struct ccw_driver *cdriver) drv->bus = &ccw_bus_type; drv->name = cdriver->name; + drv->owner = cdriver->owner; return driver_register(drv); } -- cgit v0.10.2 From e82a1567e4b22eb035da2499d20ddd573c9acf75 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Sat, 26 Jan 2008 14:10:48 +0100 Subject: [S390] cio: reduce cpu utilization during device scan Minimize calls to cpu intensive function get_subchannel_by_schid() by introducing function for_each_subchannel_staged() which temporarily caches the information about registered subchannels in a bitmap. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 12a344c..93e6f74 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -136,17 +136,13 @@ static void terminate_internal_io(struct subchannel *sch) sch->driver->termination(sch); } -static int -s390_subchannel_remove_chpid(struct device *dev, void *data) +static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) { int j; int mask; - struct subchannel *sch; - struct chp_id *chpid; + struct chp_id *chpid = data; struct schib schib; - sch = to_subchannel(dev); - chpid = data; for (j = 0; j < 8; j++) { mask = 0x80 >> j; if ((sch->schib.pmcw.pim & mask) && @@ -202,12 +198,10 @@ void chsc_chp_offline(struct chp_id chpid) if (chp_get_status(chpid) <= 0) return; - bus_for_each_dev(&css_bus_type, NULL, &chpid, - s390_subchannel_remove_chpid); + for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid); } -static int -s390_process_res_acc_new_sch(struct subchannel_id schid) +static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) { struct schib schib; /* @@ -253,18 +247,10 @@ static int get_res_chpid_mask(struct chsc_ssd_info *ssd, return 0; } -static int -__s390_process_res_acc(struct subchannel_id schid, void *data) +static int __s390_process_res_acc(struct subchannel *sch, void *data) { int chp_mask, old_lpm; - struct res_acc_data *res_data; - struct subchannel *sch; - - res_data = data; - sch = get_subchannel_by_schid(schid); - if (!sch) - /* Check if a subchannel is newly available. */ - return s390_process_res_acc_new_sch(schid); + struct res_acc_data *res_data = data; spin_lock_irq(sch->lock); chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); @@ -283,7 +269,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) sch->driver->verify(sch); out: spin_unlock_irq(sch->lock); - put_device(&sch->dev); + return 0; } @@ -306,7 +292,8 @@ static void s390_process_res_acc (struct res_acc_data *res_data) * The more information we have (info), the less scanning * will we have to do. */ - for_each_subchannel(__s390_process_res_acc, res_data); + for_each_subchannel_staged(__s390_process_res_acc, + s390_process_res_acc_new_sch, res_data); } static int @@ -500,8 +487,7 @@ void chsc_process_crw(void) } while (sei_area->flags & 0x80); } -static int -__chp_add_new_sch(struct subchannel_id schid) +static int __chp_add_new_sch(struct subchannel_id schid, void *data) { struct schib schib; @@ -515,35 +501,27 @@ __chp_add_new_sch(struct subchannel_id schid) } -static int -__chp_add(struct subchannel_id schid, void *data) +static int __chp_add(struct subchannel *sch, void *data) { int i, mask; - struct chp_id *chpid; - struct subchannel *sch; - - chpid = data; - sch = get_subchannel_by_schid(schid); - if (!sch) - /* Check if the subchannel is now available. */ - return __chp_add_new_sch(schid); + struct chp_id *chpid = data; + spin_lock_irq(sch->lock); for (i=0; i<8; i++) { mask = 0x80 >> i; if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[i] == chpid->id)) { - if (stsch(sch->schid, &sch->schib) != 0) { - /* Endgame. */ - spin_unlock_irq(sch->lock); - return -ENXIO; - } + (sch->schib.pmcw.chpid[i] == chpid->id)) break; - } } if (i==8) { spin_unlock_irq(sch->lock); return 0; } + if (stsch(sch->schid, &sch->schib)) { + spin_unlock_irq(sch->lock); + css_schedule_eval(sch->schid); + return 0; + } sch->lpm = ((sch->schib.pmcw.pim & sch->schib.pmcw.pam & sch->schib.pmcw.pom) @@ -553,7 +531,7 @@ __chp_add(struct subchannel_id schid, void *data) sch->driver->verify(sch); spin_unlock_irq(sch->lock); - put_device(&sch->dev); + return 0; } @@ -565,7 +543,8 @@ void chsc_chp_online(struct chp_id chpid) CIO_TRACE_EVENT(2, dbf_txt); if (chp_get_status(chpid) != 0) - for_each_subchannel(__chp_add, &chpid); + for_each_subchannel_staged(__chp_add, __chp_add_new_sch, + &chpid); } static void __s390_subchannel_vary_chpid(struct subchannel *sch, @@ -616,25 +595,17 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, 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 subchannel *sch, void *data) { - struct subchannel *sch; - struct chp_id *chpid; - - sch = to_subchannel(dev); - chpid = data; + struct chp_id *chpid = data; __s390_subchannel_vary_chpid(sch, *chpid, 0); return 0; } -static int s390_subchannel_vary_chpid_on(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data) { - struct subchannel *sch; - struct chp_id *chpid; - - sch = to_subchannel(dev); - chpid = data; + struct chp_id *chpid = data; __s390_subchannel_vary_chpid(sch, *chpid, 1); return 0; @@ -644,13 +615,7 @@ static int __s390_vary_chpid_on(struct subchannel_id schid, void *data) { struct schib schib; - struct subchannel *sch; - sch = get_subchannel_by_schid(schid); - if (sch) { - put_device(&sch->dev); - return 0; - } if (stsch_err(schid, &schib)) /* We're through */ return -ENXIO; @@ -670,12 +635,13 @@ int chsc_chp_vary(struct chp_id chpid, int on) * Redo PathVerification on the devices the chpid connects to */ - bus_for_each_dev(&css_bus_type, NULL, &chpid, on ? - s390_subchannel_vary_chpid_on : - s390_subchannel_vary_chpid_off); if (on) - /* Scan for new devices on varied on path. */ - for_each_subchannel(__s390_vary_chpid_on, NULL); + for_each_subchannel_staged(s390_subchannel_vary_chpid_on, + __s390_vary_chpid_on, &chpid); + else + for_each_subchannel_staged(s390_subchannel_vary_chpid_off, + NULL, &chpid); + return 0; } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 69d56c7..3b45bbe 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -51,6 +51,62 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data) return ret; } +struct cb_data { + void *data; + struct idset *set; + int (*fn_known_sch)(struct subchannel *, void *); + int (*fn_unknown_sch)(struct subchannel_id, void *); +}; + +static int call_fn_known_sch(struct device *dev, void *data) +{ + struct subchannel *sch = to_subchannel(dev); + struct cb_data *cb = data; + int rc = 0; + + idset_sch_del(cb->set, sch->schid); + if (cb->fn_known_sch) + rc = cb->fn_known_sch(sch, cb->data); + return rc; +} + +static int call_fn_unknown_sch(struct subchannel_id schid, void *data) +{ + struct cb_data *cb = data; + int rc = 0; + + if (idset_sch_contains(cb->set, schid)) + rc = cb->fn_unknown_sch(schid, cb->data); + return rc; +} + +int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), + int (*fn_unknown)(struct subchannel_id, + void *), void *data) +{ + struct cb_data cb; + int rc; + + cb.set = idset_sch_new(); + if (!cb.set) + return -ENOMEM; + idset_fill(cb.set); + cb.data = data; + cb.fn_known_sch = fn_known; + cb.fn_unknown_sch = fn_unknown; + /* Process registered subchannels. */ + rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch); + if (rc) + goto out; + /* Process unregistered subchannels. */ + if (fn_unknown) + rc = for_each_subchannel(call_fn_unknown_sch, &cb); +out: + idset_free(cb.set); + + return rc; +} + static struct subchannel * css_alloc_subchannel(struct subchannel_id schid) { @@ -402,20 +458,56 @@ static int __init slow_subchannel_init(void) return 0; } -static void css_slow_path_func(struct work_struct *unused) +static int slow_eval_known_fn(struct subchannel *sch, void *data) { - struct subchannel_id schid; + int eval; + int rc; - CIO_TRACE_EVENT(4, "slowpath"); spin_lock_irq(&slow_subchannel_lock); - 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(schid, 1); - spin_lock_irq(&slow_subchannel_lock); + eval = idset_sch_contains(slow_subchannel_set, sch->schid); + idset_sch_del(slow_subchannel_set, sch->schid); + spin_unlock_irq(&slow_subchannel_lock); + if (eval) { + rc = css_evaluate_known_subchannel(sch, 1); + if (rc == -EAGAIN) + css_schedule_eval(sch->schid); } + return 0; +} + +static int slow_eval_unknown_fn(struct subchannel_id schid, void *data) +{ + int eval; + int rc = 0; + + spin_lock_irq(&slow_subchannel_lock); + eval = idset_sch_contains(slow_subchannel_set, schid); + idset_sch_del(slow_subchannel_set, schid); spin_unlock_irq(&slow_subchannel_lock); + if (eval) { + rc = css_evaluate_new_subchannel(schid, 1); + switch (rc) { + case -EAGAIN: + css_schedule_eval(schid); + rc = 0; + break; + case -ENXIO: + case -ENOMEM: + case -EIO: + /* These should abort looping */ + break; + default: + rc = 0; + } + } + return rc; +} + +static void css_slow_path_func(struct work_struct *unused) +{ + CIO_TRACE_EVENT(4, "slowpath"); + for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn, + NULL); } static DECLARE_WORK(slow_path_work, css_slow_path_func); @@ -444,7 +536,6 @@ void css_schedule_eval_all(void) /* Reprobe subchannel if unregistered. */ static int reprobe_subchannel(struct subchannel_id schid, void *data) { - struct subchannel *sch; int ret; CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", @@ -452,13 +543,6 @@ static int reprobe_subchannel(struct subchannel_id schid, void *data) if (need_reprobe) return -EAGAIN; - sch = get_subchannel_by_schid(schid); - if (sch) { - /* Already known. */ - put_device(&sch->dev); - return 0; - } - ret = css_probe_device(schid); switch (ret) { case 0: @@ -486,7 +570,7 @@ static void reprobe_all(struct work_struct *unused) /* Make sure initial subchannel scan is done. */ wait_event(ccw_device_init_wq, atomic_read(&ccw_device_init_count) == 0); - ret = for_each_subchannel(reprobe_subchannel, NULL); + ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL); CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, need_reprobe); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 40598b8..b705545 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -91,6 +91,9 @@ extern void css_driver_unregister(struct css_driver *); extern void css_sch_device_unregister(struct subchannel *); extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; +int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), + int (*fn_unknown)(struct subchannel_id, + void *), void *data); 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); -- cgit v0.10.2 From 46258ab5e42eed5cbe20a83b732d6538c49ea2fe Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:49 +0100 Subject: [S390] cio: Delete timer in ccw_device_verify_done(). Make sure pending timers are always deleted, even if we don't go through ccw_device_done(). Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 669e6a7..485741a 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -641,9 +641,10 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) default: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; - if (cdev->online) + if (cdev->online) { + ccw_device_set_timeout(cdev, 0); dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - else + } else ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } -- cgit v0.10.2 From 1a908c735aed44c8bbed303371202e416813b271 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Sat, 26 Jan 2008 14:10:50 +0100 Subject: [S390] cio: Fix ungroup vs. machine check race in ccwgroup. There are two ways for a ccwgroup device to be unregistered: Via the ungroup attribute, or when on of the slave devices is killed by a machine check. As we have to unregister the ccwgroup device via a callback, we'll now have to check whether it hasn't been already unregistered because of a machine check. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index dc1c5f4..3964056 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); + if (device_is_registered(&gdev->dev)) { + __ccwgroup_remove_symlinks(gdev); + device_unregister(dev); + } mutex_unlock(&gdev->reg_mutex); } -- cgit v0.10.2 From 808e48882316dd4a325cd1cc382516945edad77d Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 26 Jan 2008 14:10:51 +0100 Subject: [S390] cio: memory leak in cio processing Allocated kernel memory for locks is not freed in case of subchannel found to be invalid. Signed-off-by: Michael Ernst Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index f16c15c..11f632f 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -577,8 +577,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) } /* Initialization for io subchannels. */ - if (!css_sch_is_valid(&sch->schib)) - return -ENODEV; + if (!css_sch_is_valid(&sch->schib)) { + err = -ENODEV; + goto out; + } + /* Devno is valid. */ if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) { /* -- cgit v0.10.2 From 90ab133603d066e850fc9ed297b6eb52f888dd25 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Sat, 26 Jan 2008 14:10:52 +0100 Subject: [S390] cio: introduce timed recovery procedure Add a timed recovery procedure to reactivate ccw devices in cases where HW/VM events are not sufficient to allow for proper recovery of reappearing channel paths. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index f80d7f5..d35dc3f 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,11 @@ #include "ioasm.h" #include "io_sch.h" +static struct timer_list recovery_timer; +static spinlock_t recovery_lock; +static int recovery_phase; +static const unsigned long recovery_delay[] = { 3, 30, 300 }; + /******************* bus type handling ***********************/ /* The Linux driver model distinguishes between a bus type and @@ -142,6 +148,8 @@ struct workqueue_struct *ccw_device_notify_work; wait_queue_head_t ccw_device_init_wq; atomic_t ccw_device_init_count; +static void recovery_func(unsigned long data); + static int __init init_ccw_bus_type (void) { @@ -149,6 +157,7 @@ init_ccw_bus_type (void) init_waitqueue_head(&ccw_device_init_wq); atomic_set(&ccw_device_init_count, 0); + setup_timer(&recovery_timer, recovery_func, 0); ccw_device_work = create_singlethread_workqueue("cio"); if (!ccw_device_work) @@ -1503,6 +1512,60 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev) return sch->schid; } +static int recovery_check(struct device *dev, void *data) +{ + struct ccw_device *cdev = to_ccwdev(dev); + int *redo = data; + + spin_lock_irq(cdev->ccwlock); + switch (cdev->private->state) { + case DEV_STATE_DISCONNECTED: + CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + *redo = 1; + break; + case DEV_STATE_DISCONNECTED_SENSE_ID: + *redo = 1; + break; + } + spin_unlock_irq(cdev->ccwlock); + + return 0; +} + +static void recovery_func(unsigned long data) +{ + int redo = 0; + + bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check); + if (redo) { + spin_lock_irq(&recovery_lock); + if (!timer_pending(&recovery_timer)) { + if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1) + recovery_phase++; + mod_timer(&recovery_timer, jiffies + + recovery_delay[recovery_phase] * HZ); + } + spin_unlock_irq(&recovery_lock); + } else + CIO_MSG_EVENT(2, "recovery: end\n"); +} + +void ccw_device_schedule_recovery(void) +{ + unsigned long flags; + + CIO_MSG_EVENT(2, "recovery: schedule\n"); + spin_lock_irqsave(&recovery_lock, flags); + if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { + recovery_phase = 0; + mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ); + } + spin_unlock_irqrestore(&recovery_lock, flags); +} + MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_online); EXPORT_SYMBOL(ccw_device_set_offline); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 87576fe..d40a2ff 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -88,6 +88,8 @@ int ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); int ccw_device_offline(struct ccw_device *); +void ccw_device_schedule_recovery(void); + /* Function prototypes for device status and basic sense stuff. */ void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 485741a..4b92c84 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -61,6 +61,8 @@ device_set_disconnected(struct subchannel *sch) ccw_device_set_timeout(cdev, 0); cdev->private->flags.fake_irb = 0; cdev->private->state = DEV_STATE_DISCONNECTED; + if (cdev->online) + ccw_device_schedule_recovery(); } void device_set_intretry(struct subchannel *sch) -- cgit v0.10.2 From 8d08be928eb9e90f1456d34aa0712fa9038674e0 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Sat, 26 Jan 2008 14:10:53 +0100 Subject: [S390] qdio: Remove double checked value. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 7a353db..e2a781b 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -1408,8 +1408,7 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) if (q->hydra_gives_outbound_pcis) { if (!q->siga_sync_done_on_thinints) { SYNC_MEMORY_ALL; - } else if ((!q->siga_sync_done_on_outb_tis)&& - (q->hydra_gives_outbound_pcis)) { + } else if (!q->siga_sync_done_on_outb_tis) { SYNC_MEMORY_ALL_OUTB; } } else { -- cgit v0.10.2 From dfa77f611ff295598e218aa0eb6efa73a5cf26d0 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Sat, 26 Jan 2008 14:10:54 +0100 Subject: [S390] qdio: set QDIO_ACTIVATE_TIMEOUT to 5s Current definition of QDIO_ACTIVATE_TIMEOUT results in value 0. Thus it may cause endless wait in function qdio_activate(). Signed-off-by: Ursula Braun Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 6d7aad1..37870e4 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -57,7 +57,7 @@ of the queue to 0 */ #define QDIO_ESTABLISH_TIMEOUT (1*HZ) -#define QDIO_ACTIVATE_TIMEOUT ((5*HZ)>>10) +#define QDIO_ACTIVATE_TIMEOUT (5*HZ) #define QDIO_CLEANUP_CLEAR_TIMEOUT (20*HZ) #define QDIO_CLEANUP_HALT_TIMEOUT (10*HZ) #define QDIO_FORCE_CHECK_TIMEOUT (10*HZ) -- cgit v0.10.2 From c05ffc4f2b208da8ba7d3a9b5ab886c76f8939b5 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 26 Jan 2008 14:10:55 +0100 Subject: [S390] sclp: sysfs interface for SCLP cpi Signed-off-by: Michael Ernst Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 130de19..bee3a3a 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_info.o sclp_config.o sclp_chp.o + sclp_info.o sclp_config.o sclp_chp.o sclp_cpi_sys.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c index 82a13d9..5716487 100644 --- a/drivers/s390/char/sclp_cpi.c +++ b/drivers/s390/char/sclp_cpi.c @@ -1,255 +1,41 @@ /* - * Author: Martin Peschke - * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation + * drivers/s390/char/sclp_cpi.c + * SCLP control programm identification * - * SCLP Control-Program Identification. + * Copyright IBM Corp. 2001, 2007 + * Author(s): Martin Peschke + * Michael Ernst */ -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include - -#include "sclp.h" -#include "sclp_rw.h" - -#define CPI_LENGTH_SYSTEM_TYPE 8 -#define CPI_LENGTH_SYSTEM_NAME 8 -#define CPI_LENGTH_SYSPLEX_NAME 8 - -struct cpi_evbuf { - struct evbuf_header header; - u8 id_format; - u8 reserved0; - u8 system_type[CPI_LENGTH_SYSTEM_TYPE]; - u64 reserved1; - u8 system_name[CPI_LENGTH_SYSTEM_NAME]; - u64 reserved2; - u64 system_level; - u64 reserved3; - u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME]; - u8 reserved4[16]; -} __attribute__((packed)); - -struct cpi_sccb { - struct sccb_header header; - struct cpi_evbuf cpi_evbuf; -} __attribute__((packed)); - -/* Event type structure for write message and write priority message */ -static struct sclp_register sclp_cpi_event = -{ - .send_mask = EVTYP_CTLPROGIDENT_MASK -}; +#include +#include "sclp_cpi_sys.h" MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Identify this operating system instance " + "to the System z hardware"); +MODULE_AUTHOR("Martin Peschke , " + "Michael Ernst "); -MODULE_AUTHOR( - "Martin Peschke, IBM Deutschland Entwicklung GmbH " - ""); - -MODULE_DESCRIPTION( - "identify this operating system instance to the S/390 " - "or zSeries hardware"); +static char *system_name = ""; +static char *sysplex_name = ""; -static char *system_name = NULL; module_param(system_name, charp, 0); MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters"); - -static char *sysplex_name = NULL; -#ifdef ALLOW_SYSPLEX_NAME module_param(sysplex_name, charp, 0); MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters"); -#endif - -/* use default value for this field (as well as for system level) */ -static char *system_type = "LINUX"; -static int -cpi_check_parms(void) +static int __init cpi_module_init(void) { - /* reject if no system type specified */ - if (!system_type) { - printk("cpi: bug: no system type specified\n"); - return -EINVAL; - } - - /* reject if system type larger than 8 characters */ - if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) { - printk("cpi: bug: system type has length of %li characters - " - "only %i characters supported\n", - strlen(system_type), CPI_LENGTH_SYSTEM_TYPE); - return -EINVAL; - } - - /* reject if no system name specified */ - if (!system_name) { - printk("cpi: no system name specified\n"); - return -EINVAL; - } - - /* reject if system name larger than 8 characters */ - if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) { - printk("cpi: system name has length of %li characters - " - "only %i characters supported\n", - strlen(system_name), CPI_LENGTH_SYSTEM_NAME); - return -EINVAL; - } - - /* reject if specified sysplex name larger than 8 characters */ - if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) { - printk("cpi: sysplex name has length of %li characters" - " - only %i characters supported\n", - strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME); - return -EINVAL; - } - return 0; + return sclp_cpi_set_data(system_name, sysplex_name, "LINUX", + LINUX_VERSION_CODE); } -static void -cpi_callback(struct sclp_req *req, void *data) -{ - struct semaphore *sem; - - sem = (struct semaphore *) data; - up(sem); -} - -static struct sclp_req * -cpi_prepare_req(void) -{ - struct sclp_req *req; - struct cpi_sccb *sccb; - struct cpi_evbuf *evb; - - req = kmalloc(sizeof(struct sclp_req), GFP_KERNEL); - if (req == NULL) - return ERR_PTR(-ENOMEM); - sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA); - if (sccb == NULL) { - kfree(req); - return ERR_PTR(-ENOMEM); - } - memset(sccb, 0, sizeof(struct cpi_sccb)); - - /* setup SCCB for Control-Program Identification */ - sccb->header.length = sizeof(struct cpi_sccb); - sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); - sccb->cpi_evbuf.header.type = 0x0B; - evb = &sccb->cpi_evbuf; - - /* set system type */ - memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE); - memcpy(evb->system_type, system_type, strlen(system_type)); - sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); - EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); - - /* set system name */ - memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME); - memcpy(evb->system_name, system_name, strlen(system_name)); - sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME); - EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME); - - /* set system level */ - evb->system_level = LINUX_VERSION_CODE; - - /* set sysplex name */ - if (sysplex_name) { - memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME); - memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name)); - sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); - EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); - } - - /* prepare request data structure presented to SCLP driver */ - req->command = SCLP_CMDW_WRITE_EVENT_DATA; - req->sccb = sccb; - req->status = SCLP_REQ_FILLED; - req->callback = cpi_callback; - return req; -} - -static void -cpi_free_req(struct sclp_req *req) -{ - free_page((unsigned long) req->sccb); - kfree(req); -} - -static int __init -cpi_module_init(void) -{ - struct semaphore sem; - struct sclp_req *req; - int rc; - - rc = cpi_check_parms(); - if (rc) - return rc; - - rc = sclp_register(&sclp_cpi_event); - if (rc) { - /* could not register sclp event. Die. */ - printk(KERN_WARNING "cpi: could not register to hardware " - "console.\n"); - return -EINVAL; - } - if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { - printk(KERN_WARNING "cpi: no control program identification " - "support\n"); - sclp_unregister(&sclp_cpi_event); - return -EOPNOTSUPP; - } - - req = cpi_prepare_req(); - if (IS_ERR(req)) { - printk(KERN_WARNING "cpi: couldn't allocate request\n"); - sclp_unregister(&sclp_cpi_event); - return PTR_ERR(req); - } - - /* Prepare semaphore */ - sema_init(&sem, 0); - req->callback_data = &sem; - /* Add request to sclp queue */ - rc = sclp_add_request(req); - if (rc) { - printk(KERN_WARNING "cpi: could not start request\n"); - cpi_free_req(req); - sclp_unregister(&sclp_cpi_event); - return rc; - } - /* make "insmod" sleep until callback arrives */ - down(&sem); - - rc = ((struct cpi_sccb *) req->sccb)->header.response_code; - if (rc != 0x0020) { - printk(KERN_WARNING "cpi: failed with response code 0x%x\n", - rc); - rc = -ECOMM; - } else - rc = 0; - - cpi_free_req(req); - sclp_unregister(&sclp_cpi_event); - - return rc; -} - - static void __exit cpi_module_exit(void) { } - -/* declare driver module init/cleanup functions */ module_init(cpi_module_init); module_exit(cpi_module_exit); - diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c new file mode 100644 index 0000000..4161703 --- /dev/null +++ b/drivers/s390/char/sclp_cpi_sys.c @@ -0,0 +1,400 @@ +/* + * drivers/s390/char/sclp_cpi_sys.c + * SCLP control program identification sysfs interface + * + * Copyright IBM Corp. 2001, 2007 + * Author(s): Martin Peschke + * Michael Ernst + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sclp.h" +#include "sclp_rw.h" +#include "sclp_cpi_sys.h" + +#define CPI_LENGTH_NAME 8 +#define CPI_LENGTH_LEVEL 16 + +struct cpi_evbuf { + struct evbuf_header header; + u8 id_format; + u8 reserved0; + u8 system_type[CPI_LENGTH_NAME]; + u64 reserved1; + u8 system_name[CPI_LENGTH_NAME]; + u64 reserved2; + u64 system_level; + u64 reserved3; + u8 sysplex_name[CPI_LENGTH_NAME]; + u8 reserved4[16]; +} __attribute__((packed)); + +struct cpi_sccb { + struct sccb_header header; + struct cpi_evbuf cpi_evbuf; +} __attribute__((packed)); + +static struct sclp_register sclp_cpi_event = { + .send_mask = EVTYP_CTLPROGIDENT_MASK, +}; + +static char system_name[CPI_LENGTH_NAME + 1]; +static char sysplex_name[CPI_LENGTH_NAME + 1]; +static char system_type[CPI_LENGTH_NAME + 1]; +static u64 system_level; + +static void set_data(char *field, char *data) +{ + memset(field, ' ', CPI_LENGTH_NAME); + memcpy(field, data, strlen(data)); + sclp_ascebc_str(field, CPI_LENGTH_NAME); +} + +static void cpi_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +static struct sclp_req *cpi_prepare_req(void) +{ + struct sclp_req *req; + struct cpi_sccb *sccb; + struct cpi_evbuf *evb; + + req = kzalloc(sizeof(struct sclp_req), GFP_KERNEL); + if (!req) + return ERR_PTR(-ENOMEM); + sccb = (struct cpi_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) { + kfree(req); + return ERR_PTR(-ENOMEM); + } + + /* setup SCCB for Control-Program Identification */ + sccb->header.length = sizeof(struct cpi_sccb); + sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); + sccb->cpi_evbuf.header.type = 0x0b; + evb = &sccb->cpi_evbuf; + + /* set system type */ + set_data(evb->system_type, system_type); + + /* set system name */ + set_data(evb->system_name, system_name); + + /* set sytem level */ + evb->system_level = system_level; + + /* set sysplex name */ + set_data(evb->sysplex_name, sysplex_name); + + /* prepare request data structure presented to SCLP driver */ + req->command = SCLP_CMDW_WRITE_EVENT_DATA; + req->sccb = sccb; + req->status = SCLP_REQ_FILLED; + req->callback = cpi_callback; + return req; +} + +static void cpi_free_req(struct sclp_req *req) +{ + free_page((unsigned long) req->sccb); + kfree(req); +} + +static int cpi_req(void) +{ + struct completion completion; + struct sclp_req *req; + int rc; + int response; + + rc = sclp_register(&sclp_cpi_event); + if (rc) { + printk(KERN_WARNING "cpi: could not register " + "to hardware console.\n"); + goto out; + } + if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { + printk(KERN_WARNING "cpi: no control program " + "identification support\n"); + rc = -EOPNOTSUPP; + goto out_unregister; + } + + req = cpi_prepare_req(); + if (IS_ERR(req)) { + printk(KERN_WARNING "cpi: could not allocate request\n"); + rc = PTR_ERR(req); + goto out_unregister; + } + + init_completion(&completion); + req->callback_data = &completion; + + /* Add request to sclp queue */ + rc = sclp_add_request(req); + if (rc) { + printk(KERN_WARNING "cpi: could not start request\n"); + goto out_free_req; + } + + wait_for_completion(&completion); + + if (req->status != SCLP_REQ_DONE) { + printk(KERN_WARNING "cpi: request failed (status=0x%02x)\n", + req->status); + rc = -EIO; + goto out_free_req; + } + + response = ((struct cpi_sccb *) req->sccb)->header.response_code; + if (response != 0x0020) { + printk(KERN_WARNING "cpi: failed with " + "response code 0x%x\n", response); + rc = -EIO; + } + +out_free_req: + cpi_free_req(req); + +out_unregister: + sclp_unregister(&sclp_cpi_event); + +out: + return rc; +} + +static int check_string(const char *attr, const char *str) +{ + size_t len; + size_t i; + + len = strlen(str); + + if ((len > 0) && (str[len - 1] == '\n')) + len--; + + if (len > CPI_LENGTH_NAME) + return -EINVAL; + + for (i = 0; i < len ; i++) { + if (isalpha(str[i]) || isdigit(str[i]) || + strchr("$@# ", str[i])) + continue; + return -EINVAL; + } + + return 0; +} + +static void set_string(char *attr, const char *value) +{ + size_t len; + size_t i; + + len = strlen(value); + + if ((len > 0) && (value[len - 1] == '\n')) + len--; + + for (i = 0; i < CPI_LENGTH_NAME; i++) { + if (i < len) + attr[i] = toupper(value[i]); + else + attr[i] = ' '; + } +} + +static ssize_t system_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return snprintf(page, PAGE_SIZE, "%s\n", system_name); +} + +static ssize_t system_name_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t len) +{ + int rc; + + rc = check_string("system_name", buf); + if (rc) + return rc; + + set_string(system_name, buf); + + return len; +} + +static struct kobj_attribute system_name_attr = + __ATTR(system_name, 0644, system_name_show, system_name_store); + +static ssize_t sysplex_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return snprintf(page, PAGE_SIZE, "%s\n", sysplex_name); +} + +static ssize_t sysplex_name_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t len) +{ + int rc; + + rc = check_string("sysplex_name", buf); + if (rc) + return rc; + + set_string(sysplex_name, buf); + + return len; +} + +static struct kobj_attribute sysplex_name_attr = + __ATTR(sysplex_name, 0644, sysplex_name_show, sysplex_name_store); + +static ssize_t system_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return snprintf(page, PAGE_SIZE, "%s\n", system_type); +} + +static ssize_t system_type_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t len) +{ + int rc; + + rc = check_string("system_type", buf); + if (rc) + return rc; + + set_string(system_type, buf); + + return len; +} + +static struct kobj_attribute system_type_attr = + __ATTR(system_type, 0644, system_type_show, system_type_store); + +static ssize_t system_level_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + unsigned long long level = system_level; + + return snprintf(page, PAGE_SIZE, "%#018llx\n", level); +} + +static ssize_t system_level_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t len) +{ + unsigned long long level; + char *endp; + + level = simple_strtoull(buf, &endp, 16); + + if (endp == buf) + return -EINVAL; + if (*endp == '\n') + endp++; + if (*endp) + return -EINVAL; + + system_level = level; + + return len; +} + +static struct kobj_attribute system_level_attr = + __ATTR(system_level, 0644, system_level_show, system_level_store); + +static ssize_t set_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int rc; + + rc = cpi_req(); + if (rc) + return rc; + + return len; +} + +static struct kobj_attribute set_attr = __ATTR(set, 0200, NULL, set_store); + +static struct attribute *cpi_attrs[] = { + &system_name_attr.attr, + &sysplex_name_attr.attr, + &system_type_attr.attr, + &system_level_attr.attr, + &set_attr.attr, + NULL, +}; + +static struct attribute_group cpi_attr_group = { + .attrs = cpi_attrs, +}; + +static struct kset *cpi_kset; + +int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type, + const u64 level) +{ + int rc; + + rc = check_string("system_name", system); + if (rc) + return rc; + rc = check_string("sysplex_name", sysplex); + if (rc) + return rc; + rc = check_string("system_type", type); + if (rc) + return rc; + + set_string(system_name, system); + set_string(sysplex_name, sysplex); + set_string(system_type, type); + system_level = level; + + return cpi_req(); +} +EXPORT_SYMBOL(sclp_cpi_set_data); + +static int __init cpi_init(void) +{ + int rc; + + cpi_kset = kset_create_and_add("cpi", NULL, firmware_kobj); + if (!cpi_kset) + return -ENOMEM; + + rc = sysfs_create_group(&cpi_kset->kobj, &cpi_attr_group); + if (rc) + kset_unregister(cpi_kset); + + return rc; +} + +__initcall(cpi_init); diff --git a/drivers/s390/char/sclp_cpi_sys.h b/drivers/s390/char/sclp_cpi_sys.h new file mode 100644 index 0000000..deef3e6 --- /dev/null +++ b/drivers/s390/char/sclp_cpi_sys.h @@ -0,0 +1,15 @@ +/* + * drivers/s390/char/sclp_cpi_sys.h + * SCLP control program identification sysfs interface + * + * Copyright IBM Corp. 2007 + * Author(s): Michael Ernst + */ + +#ifndef __SCLP_CPI_SYS_H__ +#define __SCLP_CPI_SYS_H__ + +int sclp_cpi_set_data(const char *system, const char *sysplex, + const char *type, u64 level); + +#endif /* __SCLP_CPI_SYS_H__ */ -- cgit v0.10.2 From 08d07968277cd898c88bf12b7720d89c02c4f139 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:10:56 +0100 Subject: [S390] Standby cpu activation/deactivation. Add a new interface so that cpus can be put into standby state and configured state. Only offline cpus can be put into standby state or configured state. For that the new percpu sysfs attribute "configure" must be used. To put a cpu in standby state a "0" must be written to the attribute. In order to switch it into configured state a "1" must be written to the attribute. Only cpus in configured state can be brought online. In addition this patch introduces a static mapping of physical to logical cpus. As a result only the sysfs directories of present cpus will be created. To scan for new cpus the new sysfs attribute "rescan" must be used. Writing to /sys/devices/system/cpu/rescan will trigger a rescan of cpus and will create directories for new cpus. On IPL only configured cpus will be used. And on reboot/shutdown all cpus will remain in their current state (configured/standby). Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 1b3af7d..c7cbb01 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -276,8 +276,9 @@ void __init startup_init(void) create_kernel_nss(); sort_main_extable(); setup_lowcore_early(); - sclp_readinfo_early(); + sclp_read_info_early(); sclp_facilities_detect(); + sclp_read_cpu_info_early(); memsize = sclp_memory_detect(); #ifndef CONFIG_64BIT /* diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 577aa7d..d68a402 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -920,7 +920,7 @@ setup_arch(char **cmdline_p) void __cpuinit print_cpu_info(struct cpuinfo_S390 *cpuinfo) { - printk("cpu %d " + printk(KERN_INFO "cpu %d " #ifdef CONFIG_SMP "phys_idx=%d " #endif diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 264ea90..66fe289 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -42,6 +42,7 @@ #include #include #include +#include #include /* @@ -58,6 +59,22 @@ EXPORT_SYMBOL(cpu_possible_map); static struct task_struct *current_set[NR_CPUS]; +static u8 smp_cpu_type; +static int smp_use_sigp_detection; + +enum s390_cpu_state { + CPU_STATE_STANDBY, + CPU_STATE_CONFIGURED, +}; + +#ifdef CONFIG_HOTPLUG_CPU +static DEFINE_MUTEX(smp_cpu_state_mutex); +#endif +static int smp_cpu_state[NR_CPUS]; + +static DEFINE_PER_CPU(struct cpu, cpu_devices); +DEFINE_PER_CPU(struct s390_idle_data, s390_idle); + static void smp_ext_bitcall(int, ec_bit_sig); /* @@ -355,6 +372,13 @@ void smp_ctl_clear_bit(int cr, int bit) } EXPORT_SYMBOL(smp_ctl_clear_bit); +/* + * In early ipl state a temp. logically cpu number is needed, so the sigp + * functions can be used to sense other cpus. Since NR_CPUS is >= 2 on + * CONFIG_SMP and the ipl cpu is logical cpu 0, it must be 1. + */ +#define CPU_INIT_NO 1 + #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) /* @@ -376,8 +400,9 @@ static void __init smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) return; } zfcpdump_save_areas[cpu] = alloc_bootmem(sizeof(union save_area)); - __cpu_logical_map[1] = (__u16) phy_cpu; - while (signal_processor(1, sigp_stop_and_store_status) == sigp_busy) + __cpu_logical_map[CPU_INIT_NO] = (__u16) phy_cpu; + while (signal_processor(CPU_INIT_NO, sigp_stop_and_store_status) == + sigp_busy) cpu_relax(); memcpy(zfcpdump_save_areas[cpu], (void *)(unsigned long) store_prefix() + SAVE_AREA_BASE, @@ -397,32 +422,166 @@ static inline void smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) { } #endif /* CONFIG_ZFCPDUMP || CONFIG_ZFCPDUMP_MODULE */ +static int cpu_stopped(int cpu) +{ + __u32 status; + + /* Check for stopped state */ + if (signal_processor_ps(&status, 0, cpu, sigp_sense) == + sigp_status_stored) { + if (status & 0x40) + return 1; + } + return 0; +} + /* * Lets check how many CPUs we have. */ -static unsigned int __init smp_count_cpus(void) +static void __init smp_count_cpus(unsigned int *configured_cpus, + unsigned int *standby_cpus) { - unsigned int cpu, num_cpus; - __u16 boot_cpu_addr; + unsigned int cpu; + struct sclp_cpu_info *info; + u16 boot_cpu_addr, cpu_addr; - /* - * cpu 0 is the boot cpu. See smp_prepare_boot_cpu. - */ boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; current_thread_info()->cpu = 0; - num_cpus = 1; - for (cpu = 0; cpu <= 65535; cpu++) { - if ((__u16) cpu == boot_cpu_addr) + *configured_cpus = 1; + *standby_cpus = 0; + + info = alloc_bootmem_pages(sizeof(*info)); + if (!info) + disabled_wait((unsigned long) __builtin_return_address(0)); + + /* Use sigp detection algorithm if sclp doesn't work. */ + if (sclp_get_cpu_info(info)) { + smp_use_sigp_detection = 1; + for (cpu = 0; cpu <= 65535; cpu++) { + if (cpu == boot_cpu_addr) + continue; + __cpu_logical_map[CPU_INIT_NO] = cpu; + if (cpu_stopped(CPU_INIT_NO)) + (*configured_cpus)++; + } + goto out; + } + + if (info->has_cpu_type) { + for (cpu = 0; cpu < info->combined; cpu++) { + if (info->cpu[cpu].address == boot_cpu_addr) { + smp_cpu_type = info->cpu[cpu].type; + break; + } + } + } + /* Count cpus. */ + for (cpu = 0; cpu < info->combined; cpu++) { + if (info->has_cpu_type && info->cpu[cpu].type != smp_cpu_type) + continue; + cpu_addr = info->cpu[cpu].address; + if (cpu_addr == boot_cpu_addr) continue; - __cpu_logical_map[1] = (__u16) cpu; - if (signal_processor(1, sigp_sense) == sigp_not_operational) + __cpu_logical_map[CPU_INIT_NO] = cpu_addr; + if (!cpu_stopped(CPU_INIT_NO)) { + (*standby_cpus)++; continue; - smp_get_save_area(num_cpus, cpu); - num_cpus++; + } + smp_get_save_area(*configured_cpus, cpu_addr); + (*configured_cpus)++; } - printk("Detected %d CPU's\n", (int) num_cpus); - printk("Boot cpu address %2X\n", boot_cpu_addr); - return num_cpus; +out: + printk(KERN_INFO "CPUs: %d configured, %d standby\n", + *configured_cpus, *standby_cpus); + free_bootmem((unsigned long) info, sizeof(*info)); +} + +static int cpu_known(int cpu_id) +{ + int cpu; + + for_each_present_cpu(cpu) { + if (__cpu_logical_map[cpu] == cpu_id) + return 1; + } + return 0; +} + +static int smp_rescan_cpus_sigp(cpumask_t avail) +{ + int cpu_id, logical_cpu; + + logical_cpu = first_cpu(avail); + if (logical_cpu == NR_CPUS) + return 0; + for (cpu_id = 0; cpu_id <= 65535; cpu_id++) { + if (cpu_known(cpu_id)) + continue; + __cpu_logical_map[logical_cpu] = cpu_id; + if (!cpu_stopped(logical_cpu)) + continue; + cpu_set(logical_cpu, cpu_present_map); + smp_cpu_state[logical_cpu] = CPU_STATE_CONFIGURED; + logical_cpu = next_cpu(logical_cpu, avail); + if (logical_cpu == NR_CPUS) + break; + } + return 0; +} + +static int __init_refok smp_rescan_cpus_sclp(cpumask_t avail) +{ + struct sclp_cpu_info *info; + int cpu_id, logical_cpu, cpu; + int rc; + + logical_cpu = first_cpu(avail); + if (logical_cpu == NR_CPUS) + return 0; + if (slab_is_available()) + info = kmalloc(sizeof(*info), GFP_KERNEL); + else + info = alloc_bootmem(sizeof(*info)); + if (!info) + return -ENOMEM; + rc = sclp_get_cpu_info(info); + if (rc) + goto out; + for (cpu = 0; cpu < info->combined; cpu++) { + if (info->has_cpu_type && info->cpu[cpu].type != smp_cpu_type) + continue; + cpu_id = info->cpu[cpu].address; + if (cpu_known(cpu_id)) + continue; + __cpu_logical_map[logical_cpu] = cpu_id; + cpu_set(logical_cpu, cpu_present_map); + if (cpu >= info->configured) + smp_cpu_state[logical_cpu] = CPU_STATE_STANDBY; + else + smp_cpu_state[logical_cpu] = CPU_STATE_CONFIGURED; + logical_cpu = next_cpu(logical_cpu, avail); + if (logical_cpu == NR_CPUS) + break; + } +out: + if (slab_is_available()) + kfree(info); + else + free_bootmem((unsigned long) info, sizeof(*info)); + return rc; +} + +static int smp_rescan_cpus(void) +{ + cpumask_t avail; + + cpus_setall(avail); + cpus_and(avail, avail, cpu_possible_map); + cpus_andnot(avail, avail, cpu_present_map); + if (smp_use_sigp_detection) + return smp_rescan_cpus_sigp(avail); + else + return smp_rescan_cpus_sclp(avail); } /* @@ -453,8 +612,6 @@ int __cpuinit start_secondary(void *cpuvoid) return 0; } -DEFINE_PER_CPU(struct s390_idle_data, s390_idle); - static void __init smp_create_idle(unsigned int cpu) { struct task_struct *p; @@ -470,37 +627,16 @@ static void __init smp_create_idle(unsigned int cpu) spin_lock_init(&(&per_cpu(s390_idle, cpu))->lock); } -static int cpu_stopped(int cpu) -{ - __u32 status; - - /* Check for stopped state */ - if (signal_processor_ps(&status, 0, cpu, sigp_sense) == - sigp_status_stored) { - if (status & 0x40) - return 1; - } - return 0; -} - /* Upping and downing of CPUs */ - int __cpu_up(unsigned int cpu) { struct task_struct *idle; struct _lowcore *cpu_lowcore; struct stack_frame *sf; sigp_ccode ccode; - int curr_cpu; - - for (curr_cpu = 0; curr_cpu <= 65535; curr_cpu++) { - __cpu_logical_map[cpu] = (__u16) curr_cpu; - if (cpu_stopped(cpu)) - break; - } - if (!cpu_stopped(cpu)) - return -ENODEV; + if (smp_cpu_state[cpu] != CPU_STATE_CONFIGURED) + return -EIO; ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]), cpu, sigp_set_prefix); @@ -543,21 +679,18 @@ static unsigned int __initdata possible_cpus; void __init smp_setup_cpu_possible_map(void) { - unsigned int phy_cpus, pos_cpus, cpu; - - phy_cpus = smp_count_cpus(); - pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS); + unsigned int pos_cpus, cpu; + unsigned int configured_cpus, standby_cpus; + smp_count_cpus(&configured_cpus, &standby_cpus); + pos_cpus = min(configured_cpus + standby_cpus + additional_cpus, + (unsigned int) NR_CPUS); if (possible_cpus) pos_cpus = min(possible_cpus, (unsigned int) NR_CPUS); - for (cpu = 0; cpu < pos_cpus; cpu++) cpu_set(cpu, cpu_possible_map); - - phy_cpus = min(phy_cpus, pos_cpus); - - for (cpu = 0; cpu < phy_cpus; cpu++) - cpu_set(cpu, cpu_present_map); + cpu_present_map = cpumask_of_cpu(0); + smp_rescan_cpus(); } #ifdef CONFIG_HOTPLUG_CPU @@ -612,7 +745,7 @@ void __cpu_die(unsigned int cpu) /* Wait until target cpu is down */ while (!smp_cpu_not_running(cpu)) cpu_relax(); - printk("Processor %d spun down\n", cpu); + printk(KERN_INFO "Processor %d spun down\n", cpu); } void cpu_die(void) @@ -686,12 +819,12 @@ void __init smp_prepare_boot_cpu(void) cpu_set(0, cpu_online_map); S390_lowcore.percpu_offset = __per_cpu_offset[0]; current_set[0] = current; + smp_cpu_state[0] = CPU_STATE_CONFIGURED; spin_lock_init(&(&__get_cpu_var(s390_idle))->lock); } void __init smp_cpus_done(unsigned int max_cpus) { - cpu_present_map = cpu_possible_map; } /* @@ -705,7 +838,79 @@ int setup_profiling_timer(unsigned int multiplier) return 0; } -static DEFINE_PER_CPU(struct cpu, cpu_devices); +#ifdef CONFIG_HOTPLUG_CPU +static ssize_t cpu_configure_show(struct sys_device *dev, char *buf) +{ + ssize_t count; + + mutex_lock(&smp_cpu_state_mutex); + count = sprintf(buf, "%d\n", smp_cpu_state[dev->id]); + mutex_unlock(&smp_cpu_state_mutex); + return count; +} + +static ssize_t cpu_configure_store(struct sys_device *dev, const char *buf, + size_t count) +{ + int cpu = dev->id; + int val, rc; + char delim; + + if (sscanf(buf, "%d %c", &val, &delim) != 1) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&smp_cpu_state_mutex); + lock_cpu_hotplug(); + rc = -EBUSY; + if (cpu_online(cpu)) + goto out; + rc = 0; + switch (val) { + case 0: + if (smp_cpu_state[cpu] == CPU_STATE_CONFIGURED) { + rc = sclp_cpu_deconfigure(__cpu_logical_map[cpu]); + if (!rc) + smp_cpu_state[cpu] = CPU_STATE_STANDBY; + } + break; + case 1: + if (smp_cpu_state[cpu] == CPU_STATE_STANDBY) { + rc = sclp_cpu_configure(__cpu_logical_map[cpu]); + if (!rc) + smp_cpu_state[cpu] = CPU_STATE_CONFIGURED; + } + break; + default: + break; + } +out: + unlock_cpu_hotplug(); + mutex_unlock(&smp_cpu_state_mutex); + return rc ? rc : count; +} +static SYSDEV_ATTR(configure, 0644, cpu_configure_show, cpu_configure_store); +#endif /* CONFIG_HOTPLUG_CPU */ + +static ssize_t show_cpu_address(struct sys_device *dev, char *buf) +{ + return sprintf(buf, "%d\n", __cpu_logical_map[dev->id]); +} +static SYSDEV_ATTR(address, 0444, show_cpu_address, NULL); + + +static struct attribute *cpu_common_attrs[] = { +#ifdef CONFIG_HOTPLUG_CPU + &attr_configure.attr, +#endif + &attr_address.attr, + NULL, +}; + +static struct attribute_group cpu_common_attr_group = { + .attrs = cpu_common_attrs, +}; static ssize_t show_capability(struct sys_device *dev, char *buf) { @@ -750,15 +955,15 @@ static ssize_t show_idle_time(struct sys_device *dev, char *buf) } static SYSDEV_ATTR(idle_time_us, 0444, show_idle_time, NULL); -static struct attribute *cpu_attrs[] = { +static struct attribute *cpu_online_attrs[] = { &attr_capability.attr, &attr_idle_count.attr, &attr_idle_time_us.attr, NULL, }; -static struct attribute_group cpu_attr_group = { - .attrs = cpu_attrs, +static struct attribute_group cpu_online_attr_group = { + .attrs = cpu_online_attrs, }; static int __cpuinit smp_cpu_notify(struct notifier_block *self, @@ -778,12 +983,12 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self, idle->idle_time = 0; idle->idle_count = 0; spin_unlock_irq(&idle->lock); - if (sysfs_create_group(&s->kobj, &cpu_attr_group)) + if (sysfs_create_group(&s->kobj, &cpu_online_attr_group)) return NOTIFY_BAD; break; case CPU_DEAD: case CPU_DEAD_FROZEN: - sysfs_remove_group(&s->kobj, &cpu_attr_group); + sysfs_remove_group(&s->kobj, &cpu_online_attr_group); break; } return NOTIFY_OK; @@ -793,6 +998,62 @@ static struct notifier_block __cpuinitdata smp_cpu_nb = { .notifier_call = smp_cpu_notify, }; +static int smp_add_present_cpu(int cpu) +{ + struct cpu *c = &per_cpu(cpu_devices, cpu); + struct sys_device *s = &c->sysdev; + int rc; + + c->hotpluggable = 1; + rc = register_cpu(c, cpu); + if (rc) + goto out; + rc = sysfs_create_group(&s->kobj, &cpu_common_attr_group); + if (rc) + goto out_cpu; + if (!cpu_online(cpu)) + goto out; + rc = sysfs_create_group(&s->kobj, &cpu_online_attr_group); + if (!rc) + return 0; + sysfs_remove_group(&s->kobj, &cpu_common_attr_group); +out_cpu: +#ifdef CONFIG_HOTPLUG_CPU + unregister_cpu(c); +#endif +out: + return rc; +} + +#ifdef CONFIG_HOTPLUG_CPU +static ssize_t rescan_store(struct sys_device *dev, const char *buf, + size_t count) +{ + cpumask_t newcpus; + int cpu; + int rc; + + mutex_lock(&smp_cpu_state_mutex); + lock_cpu_hotplug(); + newcpus = cpu_present_map; + rc = smp_rescan_cpus(); + if (rc) + goto out; + cpus_andnot(newcpus, cpu_present_map, newcpus); + for_each_cpu_mask(cpu, newcpus) { + rc = smp_add_present_cpu(cpu); + if (rc) + cpu_clear(cpu, cpu_present_map); + } + rc = 0; +out: + unlock_cpu_hotplug(); + mutex_unlock(&smp_cpu_state_mutex); + return rc ? rc : count; +} +static SYSDEV_ATTR(rescan, 0200, NULL, rescan_store); +#endif /* CONFIG_HOTPLUG_CPU */ + static int __init topology_init(void) { int cpu; @@ -800,16 +1061,14 @@ static int __init topology_init(void) register_cpu_notifier(&smp_cpu_nb); - for_each_possible_cpu(cpu) { - struct cpu *c = &per_cpu(cpu_devices, cpu); - struct sys_device *s = &c->sysdev; - - c->hotpluggable = 1; - register_cpu(c, cpu); - if (!cpu_online(cpu)) - continue; - s = &c->sysdev; - rc = sysfs_create_group(&s->kobj, &cpu_attr_group); +#ifdef CONFIG_HOTPLUG_CPU + rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj, + &attr_rescan.attr); + if (rc) + return rc; +#endif + for_each_present_cpu(cpu) { + rc = smp_add_present_cpu(cpu); if (rc) return rc; } diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index bee3a3a..9317333 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_info.o sclp_config.o sclp_chp.o sclp_cpi_sys.o + sclp_cmd.o sclp_config.o sclp_chp.o sclp_cpi_sys.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index c7318a1..aa8186d 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -56,8 +56,6 @@ typedef unsigned int sclp_cmdw_t; #define SCLP_CMDW_READ_EVENT_DATA 0x00770005 #define SCLP_CMDW_WRITE_EVENT_DATA 0x00760005 #define SCLP_CMDW_WRITE_EVENT_MASK 0x00780005 -#define SCLP_CMDW_READ_SCP_INFO 0x00020001 -#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 #define GDS_ID_MDSMU 0x1310 #define GDS_ID_MDSROUTEINFO 0x1311 @@ -83,6 +81,8 @@ extern u64 sclp_facilities; #define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL) #define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) +#define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL) +#define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL) struct gds_subvector { u8 length; diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c new file mode 100644 index 0000000..ba004fd --- /dev/null +++ b/drivers/s390/char/sclp_cmd.c @@ -0,0 +1,319 @@ +/* + * drivers/s390/char/sclp_cmd.c + * + * Copyright IBM Corp. 2007 + * Author(s): Heiko Carstens + */ + +#include +#include +#include +#include +#include +#include +#include "sclp.h" + +#define TAG "sclp_cmd: " + +#define SCLP_CMDW_READ_SCP_INFO 0x00020001 +#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 + +struct read_info_sccb { + struct sccb_header header; /* 0-7 */ + u16 rnmax; /* 8-9 */ + u8 rnsize; /* 10 */ + u8 _reserved0[24 - 11]; /* 11-15 */ + u8 loadparm[8]; /* 24-31 */ + u8 _reserved1[48 - 32]; /* 32-47 */ + u64 facilities; /* 48-55 */ + u8 _reserved2[84 - 56]; /* 56-83 */ + u8 fac84; /* 84 */ + u8 _reserved3[91 - 85]; /* 85-90 */ + u8 flags; /* 91 */ + u8 _reserved4[100 - 92]; /* 92-99 */ + u32 rnsize2; /* 100-103 */ + u64 rnmax2; /* 104-111 */ + u8 _reserved5[4096 - 112]; /* 112-4095 */ +} __attribute__((packed, aligned(PAGE_SIZE))); + +static struct read_info_sccb __initdata early_read_info_sccb; +static int __initdata early_read_info_sccb_valid; + +u64 sclp_facilities; +static u8 sclp_fac84; + +static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb) +{ + int rc; + + __ctl_set_bit(0, 9); + rc = sclp_service_call(cmd, sccb); + if (rc) + goto out; + __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT | + PSW_MASK_WAIT | PSW_DEFAULT_KEY); + local_irq_disable(); +out: + /* Contents of the sccb might have changed. */ + barrier(); + __ctl_clear_bit(0, 9); + return rc; +} + +void __init sclp_read_info_early(void) +{ + int rc; + int i; + struct read_info_sccb *sccb; + sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, + SCLP_CMDW_READ_SCP_INFO}; + + sccb = &early_read_info_sccb; + for (i = 0; i < ARRAY_SIZE(commands); i++) { + do { + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->header.control_mask[2] = 0x80; + rc = sclp_cmd_sync_early(commands[i], sccb); + } while (rc == -EBUSY); + + if (rc) + break; + if (sccb->header.response_code == 0x10) { + early_read_info_sccb_valid = 1; + break; + } + if (sccb->header.response_code != 0x1f0) + break; + } +} + +void __init sclp_facilities_detect(void) +{ + if (!early_read_info_sccb_valid) + return; + sclp_facilities = early_read_info_sccb.facilities; + sclp_fac84 = early_read_info_sccb.fac84; +} + +unsigned long long __init sclp_memory_detect(void) +{ + unsigned long long memsize; + struct read_info_sccb *sccb; + + if (!early_read_info_sccb_valid) + return 0; + sccb = &early_read_info_sccb; + if (sccb->rnsize) + memsize = sccb->rnsize << 20; + else + memsize = sccb->rnsize2 << 20; + if (sccb->rnmax) + memsize *= sccb->rnmax; + else + memsize *= sccb->rnmax2; + return memsize; +} + +/* + * This function will be called after sclp_memory_detect(), which gets called + * early from early.c code. Therefore the sccb should have valid contents. + */ +void __init sclp_get_ipl_info(struct sclp_ipl_info *info) +{ + struct read_info_sccb *sccb; + + if (!early_read_info_sccb_valid) + return; + sccb = &early_read_info_sccb; + info->is_valid = 1; + if (sccb->flags & 0x2) + info->has_dump = 1; + memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN); +} + +static void sclp_sync_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +static int do_sync_request(sclp_cmdw_t cmd, void *sccb) +{ + struct completion completion; + struct sclp_req *request; + int rc; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + request->command = cmd; + request->sccb = sccb; + request->status = SCLP_REQ_FILLED; + request->callback = sclp_sync_callback; + request->callback_data = &completion; + init_completion(&completion); + + /* Perform sclp request. */ + rc = sclp_add_request(request); + if (rc) + goto out; + wait_for_completion(&completion); + + /* Check response. */ + if (request->status != SCLP_REQ_DONE) { + printk(KERN_WARNING TAG "sync request failed " + "(cmd=0x%08x, status=0x%02x)\n", cmd, request->status); + rc = -EIO; + } +out: + kfree(request); + return rc; +} + +/* + * CPU configuration related functions. + */ + +#define SCLP_CMDW_READ_CPU_INFO 0x00010001 +#define SCLP_CMDW_CONFIGURE_CPU 0x00110001 +#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001 + +struct read_cpu_info_sccb { + struct sccb_header header; + u16 nr_configured; + u16 offset_configured; + u16 nr_standby; + u16 offset_standby; + u8 reserved[4096 - 16]; +} __attribute__((packed, aligned(PAGE_SIZE))); + +static struct read_cpu_info_sccb __initdata early_read_cpu_info_sccb; +static struct sclp_cpu_info __initdata sclp_cpu_info; + +static void sclp_fill_cpu_info(struct sclp_cpu_info *info, + struct read_cpu_info_sccb *sccb) +{ + char *page = (char *) sccb; + + memset(info, 0, sizeof(*info)); + info->configured = sccb->nr_configured; + info->standby = sccb->nr_standby; + info->combined = sccb->nr_configured + sccb->nr_standby; + info->has_cpu_type = sclp_fac84 & 0x1; + memcpy(&info->cpu, page + sccb->offset_configured, + info->combined * sizeof(struct sclp_cpu_entry)); +} + +void __init sclp_read_cpu_info_early(void) +{ + int rc; + struct read_cpu_info_sccb *sccb; + + if (!SCLP_HAS_CPU_INFO) + return; + + sccb = &early_read_cpu_info_sccb; + do { + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + rc = sclp_cmd_sync_early(SCLP_CMDW_READ_CPU_INFO, sccb); + } while (rc == -EBUSY); + + if (rc) + return; + if (sccb->header.response_code != 0x10) + return; + sclp_fill_cpu_info(&sclp_cpu_info, sccb); +} + +static int __init sclp_get_cpu_info_early(struct sclp_cpu_info *info) +{ + if (!SCLP_HAS_CPU_INFO) + return -EOPNOTSUPP; + *info = sclp_cpu_info; + return 0; +} + +static int sclp_get_cpu_info_late(struct sclp_cpu_info *info) +{ + int rc; + struct read_cpu_info_sccb *sccb; + + if (!SCLP_HAS_CPU_INFO) + return -EOPNOTSUPP; + sccb = (struct read_cpu_info_sccb *) __get_free_page(GFP_KERNEL + | GFP_DMA); + if (!sccb) + return -ENOMEM; + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); + if (rc) + goto out; + if (sccb->header.response_code != 0x0010) { + printk(KERN_WARNING TAG "readcpuinfo failed " + "(response=0x%04x)\n", sccb->header.response_code); + rc = -EIO; + goto out; + } + sclp_fill_cpu_info(info, sccb); +out: + free_page((unsigned long) sccb); + return rc; +} + +int __init_refok sclp_get_cpu_info(struct sclp_cpu_info *info) +{ + if (slab_is_available()) + return sclp_get_cpu_info_late(info); + return sclp_get_cpu_info_early(info); +} + +struct cpu_configure_sccb { + struct sccb_header header; +} __attribute__((packed, aligned(8))); + +static int do_cpu_configure(sclp_cmdw_t cmd) +{ + struct cpu_configure_sccb *sccb; + int rc; + + if (!SCLP_HAS_CPU_RECONFIG) + return -EOPNOTSUPP; + /* + * This is not going to cross a page boundary since we force + * kmalloc to have a minimum alignment of 8 bytes on s390. + */ + sccb = kzalloc(sizeof(*sccb), GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = sizeof(*sccb); + rc = do_sync_request(cmd, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0020: + case 0x0120: + break; + default: + printk(KERN_WARNING TAG "configure cpu failed (cmd=0x%08x, " + "response=0x%04x)\n", cmd, sccb->header.response_code); + rc = -EIO; + break; + } +out: + kfree(sccb); + return rc; +} + +int sclp_cpu_configure(u8 cpu) +{ + return do_cpu_configure(SCLP_CMDW_CONFIGURE_CPU | cpu << 8); +} + +int sclp_cpu_deconfigure(u8 cpu) +{ + return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8); +} diff --git a/drivers/s390/char/sclp_info.c b/drivers/s390/char/sclp_info.c deleted file mode 100644 index a1136e0..0000000 --- a/drivers/s390/char/sclp_info.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * drivers/s390/char/sclp_info.c - * - * Copyright IBM Corp. 2007 - * Author(s): Heiko Carstens - */ - -#include -#include -#include -#include -#include "sclp.h" - -struct sclp_readinfo_sccb { - struct sccb_header header; /* 0-7 */ - u16 rnmax; /* 8-9 */ - u8 rnsize; /* 10 */ - u8 _reserved0[24 - 11]; /* 11-23 */ - u8 loadparm[8]; /* 24-31 */ - u8 _reserved1[48 - 32]; /* 32-47 */ - u64 facilities; /* 48-55 */ - u8 _reserved2[91 - 56]; /* 56-90 */ - u8 flags; /* 91 */ - u8 _reserved3[100 - 92]; /* 92-99 */ - u32 rnsize2; /* 100-103 */ - u64 rnmax2; /* 104-111 */ - u8 _reserved4[4096 - 112]; /* 112-4095 */ -} __attribute__((packed, aligned(4096))); - -static struct sclp_readinfo_sccb __initdata early_readinfo_sccb; -static int __initdata early_readinfo_sccb_valid; - -u64 sclp_facilities; - -void __init sclp_readinfo_early(void) -{ - int ret; - int i; - struct sclp_readinfo_sccb *sccb; - sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, - SCLP_CMDW_READ_SCP_INFO}; - - /* Enable service signal subclass mask. */ - __ctl_set_bit(0, 9); - sccb = &early_readinfo_sccb; - for (i = 0; i < ARRAY_SIZE(commands); i++) { - do { - memset(sccb, 0, sizeof(*sccb)); - sccb->header.length = sizeof(*sccb); - sccb->header.control_mask[2] = 0x80; - ret = sclp_service_call(commands[i], sccb); - } while (ret == -EBUSY); - - if (ret) - break; - __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT | - PSW_MASK_WAIT | PSW_DEFAULT_KEY); - local_irq_disable(); - /* - * Contents of the sccb might have changed - * therefore a barrier is needed. - */ - barrier(); - if (sccb->header.response_code == 0x10) { - early_readinfo_sccb_valid = 1; - break; - } - if (sccb->header.response_code != 0x1f0) - break; - } - /* Disable service signal subclass mask again. */ - __ctl_clear_bit(0, 9); -} - -void __init sclp_facilities_detect(void) -{ - if (!early_readinfo_sccb_valid) - return; - sclp_facilities = early_readinfo_sccb.facilities; -} - -unsigned long long __init sclp_memory_detect(void) -{ - unsigned long long memsize; - struct sclp_readinfo_sccb *sccb; - - if (!early_readinfo_sccb_valid) - return 0; - sccb = &early_readinfo_sccb; - if (sccb->rnsize) - memsize = sccb->rnsize << 20; - else - memsize = sccb->rnsize2 << 20; - if (sccb->rnmax) - memsize *= sccb->rnmax; - else - memsize *= sccb->rnmax2; - return memsize; -} - -/* - * This function will be called after sclp_memory_detect(), which gets called - * early from early.c code. Therefore the sccb should have valid contents. - */ -void __init sclp_get_ipl_info(struct sclp_ipl_info *info) -{ - struct sclp_readinfo_sccb *sccb; - - if (!early_readinfo_sccb_valid) - return; - sccb = &early_readinfo_sccb; - info->is_valid = 1; - if (sccb->flags & 0x2) - info->has_dump = 1; - memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN); -} diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h index cb9faf1..b8c7695 100644 --- a/include/asm-s390/sclp.h +++ b/include/asm-s390/sclp.h @@ -27,7 +27,26 @@ struct sclp_ipl_info { char loadparm[LOADPARM_LEN]; }; -void sclp_readinfo_early(void); +struct sclp_cpu_entry { + u8 address; + u8 reserved0[13]; + u8 type; + u8 reserved1; +} __attribute__((packed)); + +struct sclp_cpu_info { + unsigned int configured; + unsigned int standby; + unsigned int combined; + int has_cpu_type; + struct sclp_cpu_entry cpu[255]; +}; + +int sclp_get_cpu_info(struct sclp_cpu_info *info); +int sclp_cpu_configure(u8 cpu); +int sclp_cpu_deconfigure(u8 cpu); +void sclp_read_info_early(void); +void sclp_read_cpu_info_early(void); void sclp_facilities_detect(void); unsigned long long sclp_memory_detect(void); int sclp_sdias_blk_count(void); -- cgit v0.10.2 From 4b28a8fe78bd593cdc4454cf28af71ca9556914d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:10:57 +0100 Subject: [S390] sclp: convert channel path configure code to use sync interface. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 9317333..7e73e39 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_cmd.o sclp_config.o sclp_chp.o sclp_cpi_sys.o + sclp_cmd.o sclp_config.o sclp_cpi_sys.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c deleted file mode 100644 index c68f5e7..0000000 --- a/drivers/s390/char/sclp_chp.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * drivers/s390/char/sclp_chp.c - * - * Copyright IBM Corp. 2007 - * Author(s): Peter Oberparleiter - */ - -#include -#include -#include -#include -#include -#include - -#include "sclp.h" - -#define TAG "sclp_chp: " - -#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001 -#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001 -#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001 - -static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid) -{ - return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8; -} - -static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid) -{ - return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8; -} - -static void chp_callback(struct sclp_req *req, void *data) -{ - struct completion *completion = data; - - complete(completion); -} - -struct chp_cfg_sccb { - struct sccb_header header; - u8 ccm; - u8 reserved[6]; - u8 cssid; -} __attribute__((packed)); - -struct chp_cfg_data { - struct chp_cfg_sccb sccb; - struct sclp_req req; - struct completion completion; -} __attribute__((packed)); - -static int do_configure(sclp_cmdw_t cmd) -{ - struct chp_cfg_data *data; - int rc; - - if (!SCLP_HAS_CHP_RECONFIG) - return -EOPNOTSUPP; - /* Prepare sccb. */ - data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!data) - return -ENOMEM; - data->sccb.header.length = sizeof(struct chp_cfg_sccb); - data->req.command = cmd; - data->req.sccb = &(data->sccb); - data->req.status = SCLP_REQ_FILLED; - data->req.callback = chp_callback; - data->req.callback_data = &(data->completion); - init_completion(&data->completion); - - /* Perform sclp request. */ - rc = sclp_add_request(&(data->req)); - if (rc) - goto out; - wait_for_completion(&data->completion); - - /* Check response .*/ - if (data->req.status != SCLP_REQ_DONE) { - printk(KERN_WARNING TAG "configure channel-path request failed " - "(status=0x%02x)\n", data->req.status); - rc = -EIO; - goto out; - } - switch (data->sccb.header.response_code) { - case 0x0020: - case 0x0120: - case 0x0440: - case 0x0450: - break; - default: - printk(KERN_WARNING TAG "configure channel-path failed " - "(cmd=0x%08x, response=0x%04x)\n", cmd, - data->sccb.header.response_code); - rc = -EIO; - break; - } -out: - free_page((unsigned long) data); - - return rc; -} - -/** - * sclp_chp_configure - perform configure channel-path sclp command - * @chpid: channel-path ID - * - * Perform configure channel-path command sclp command for specified chpid. - * Return 0 after command successfully finished, non-zero otherwise. - */ -int sclp_chp_configure(struct chp_id chpid) -{ - return do_configure(get_configure_cmdw(chpid)); -} - -/** - * sclp_chp_deconfigure - perform deconfigure channel-path sclp command - * @chpid: channel-path ID - * - * Perform deconfigure channel-path command sclp command for specified chpid - * and wait for completion. On success return 0. Return non-zero otherwise. - */ -int sclp_chp_deconfigure(struct chp_id chpid) -{ - return do_configure(get_deconfigure_cmdw(chpid)); -} - -struct chp_info_sccb { - struct sccb_header header; - u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; - u8 standby[SCLP_CHP_INFO_MASK_SIZE]; - u8 configured[SCLP_CHP_INFO_MASK_SIZE]; - u8 ccm; - u8 reserved[6]; - u8 cssid; -} __attribute__((packed)); - -struct chp_info_data { - struct chp_info_sccb sccb; - struct sclp_req req; - struct completion completion; -} __attribute__((packed)); - -/** - * sclp_chp_read_info - perform read channel-path information sclp command - * @info: resulting channel-path information data - * - * Perform read channel-path information sclp command and wait for completion. - * On success, store channel-path information in @info and return 0. Return - * non-zero otherwise. - */ -int sclp_chp_read_info(struct sclp_chp_info *info) -{ - struct chp_info_data *data; - int rc; - - if (!SCLP_HAS_CHP_INFO) - return -EOPNOTSUPP; - /* Prepare sccb. */ - data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!data) - return -ENOMEM; - data->sccb.header.length = sizeof(struct chp_info_sccb); - data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION; - data->req.sccb = &(data->sccb); - data->req.status = SCLP_REQ_FILLED; - data->req.callback = chp_callback; - data->req.callback_data = &(data->completion); - init_completion(&data->completion); - - /* Perform sclp request. */ - rc = sclp_add_request(&(data->req)); - if (rc) - goto out; - wait_for_completion(&data->completion); - - /* Check response .*/ - if (data->req.status != SCLP_REQ_DONE) { - printk(KERN_WARNING TAG "read channel-path info request failed " - "(status=0x%02x)\n", data->req.status); - rc = -EIO; - goto out; - } - if (data->sccb.header.response_code != 0x0010) { - printk(KERN_WARNING TAG "read channel-path info failed " - "(response=0x%04x)\n", data->sccb.header.response_code); - rc = -EIO; - goto out; - } - memcpy(info->recognized, data->sccb.recognized, - SCLP_CHP_INFO_MASK_SIZE); - memcpy(info->standby, data->sccb.standby, - SCLP_CHP_INFO_MASK_SIZE); - memcpy(info->configured, data->sccb.configured, - SCLP_CHP_INFO_MASK_SIZE); -out: - free_page((unsigned long) data); - - return rc; -} diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index ba004fd..d7e6f4d 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -2,7 +2,8 @@ * drivers/s390/char/sclp_cmd.c * * Copyright IBM Corp. 2007 - * Author(s): Heiko Carstens + * Author(s): Heiko Carstens , + * Peter Oberparleiter */ #include @@ -10,6 +11,7 @@ #include #include #include +#include #include #include "sclp.h" @@ -317,3 +319,122 @@ int sclp_cpu_deconfigure(u8 cpu) { return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8); } + +/* + * Channel path configuration related functions. + */ + +#define SCLP_CMDW_CONFIGURE_CHPATH 0x000f0001 +#define SCLP_CMDW_DECONFIGURE_CHPATH 0x000e0001 +#define SCLP_CMDW_READ_CHPATH_INFORMATION 0x00030001 + +struct chp_cfg_sccb { + struct sccb_header header; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +static int do_chp_configure(sclp_cmdw_t cmd) +{ + struct chp_cfg_sccb *sccb; + int rc; + + if (!SCLP_HAS_CHP_RECONFIG) + return -EOPNOTSUPP; + /* Prepare sccb. */ + sccb = (struct chp_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = sizeof(*sccb); + rc = do_sync_request(cmd, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0020: + case 0x0120: + case 0x0440: + case 0x0450: + break; + default: + printk(KERN_WARNING TAG "configure channel-path failed " + "(cmd=0x%08x, response=0x%04x)\n", cmd, + sccb->header.response_code); + rc = -EIO; + break; + } +out: + free_page((unsigned long) sccb); + return rc; +} + +/** + * sclp_chp_configure - perform configure channel-path sclp command + * @chpid: channel-path ID + * + * Perform configure channel-path command sclp command for specified chpid. + * Return 0 after command successfully finished, non-zero otherwise. + */ +int sclp_chp_configure(struct chp_id chpid) +{ + return do_chp_configure(SCLP_CMDW_CONFIGURE_CHPATH | chpid.id << 8); +} + +/** + * sclp_chp_deconfigure - perform deconfigure channel-path sclp command + * @chpid: channel-path ID + * + * Perform deconfigure channel-path command sclp command for specified chpid + * and wait for completion. On success return 0. Return non-zero otherwise. + */ +int sclp_chp_deconfigure(struct chp_id chpid) +{ + return do_chp_configure(SCLP_CMDW_DECONFIGURE_CHPATH | chpid.id << 8); +} + +struct chp_info_sccb { + struct sccb_header header; + u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; + u8 standby[SCLP_CHP_INFO_MASK_SIZE]; + u8 configured[SCLP_CHP_INFO_MASK_SIZE]; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +/** + * sclp_chp_read_info - perform read channel-path information sclp command + * @info: resulting channel-path information data + * + * Perform read channel-path information sclp command and wait for completion. + * On success, store channel-path information in @info and return 0. Return + * non-zero otherwise. + */ +int sclp_chp_read_info(struct sclp_chp_info *info) +{ + struct chp_info_sccb *sccb; + int rc; + + if (!SCLP_HAS_CHP_INFO) + return -EOPNOTSUPP; + /* Prepare sccb. */ + sccb = (struct chp_info_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = sizeof(*sccb); + rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); + if (rc) + goto out; + if (sccb->header.response_code != 0x0010) { + printk(KERN_WARNING TAG "read channel-path info failed " + "(response=0x%04x)\n", sccb->header.response_code); + rc = -EIO; + goto out; + } + memcpy(info->recognized, sccb->recognized, SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->standby, sccb->standby, SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->configured, sccb->configured, SCLP_CHP_INFO_MASK_SIZE); +out: + free_page((unsigned long) sccb); + return rc; +} -- cgit v0.10.2 From 6f457e1a149eb39ee58d51913e8023fc27c52806 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Sat, 26 Jan 2008 14:10:58 +0100 Subject: [S390] Fix tlb flushing with idte. The clear-by-asce operation of the idte instruction gets an asce (address-space-control-element) as argument to specify which TLBs need to get flushed. The current code passes a plain pointer to the start of the pgd without the additional bits which would make the pointer an asce. The current machines don't mind the difference but a future model might want to use the designation type control bits in the asce as a filter for the TLBs to flush. Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index a87b197..79dccd2 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -157,7 +157,7 @@ startup_continue: .long 0xb2b10000 # store facility list tm 0xc8,0x08 # check bit for clearing-by-ASCE bno 0f-.LPG1(%r13) - lhi %r1,2094 + lhi %r1,2048 lhi %r2,0 .long 0xb98e2001 oi 7(%r12),0x80 # set IDTE flag diff --git a/include/asm-s390/mmu_context.h b/include/asm-s390/mmu_context.h index 05b8421..a77d4ba 100644 --- a/include/asm-s390/mmu_context.h +++ b/include/asm-s390/mmu_context.h @@ -12,10 +12,15 @@ #include #include -/* - * get a new mmu context.. S390 don't know about contexts. - */ -#define init_new_context(tsk,mm) 0 +static inline int init_new_context(struct task_struct *tsk, + struct mm_struct *mm) +{ + mm->context = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS; +#ifdef CONFIG_64BIT + mm->context |= _ASCE_TYPE_REGION3; +#endif + return 0; +} #define destroy_context(mm) do { } while (0) @@ -27,19 +32,11 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk) { - pgd_t *pgd = mm->pgd; - unsigned long asce_bits; - - /* Calculate asce bits from the first pgd table entry. */ - asce_bits = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS; -#ifdef CONFIG_64BIT - asce_bits |= _ASCE_TYPE_REGION3; -#endif - S390_lowcore.user_asce = asce_bits | __pa(pgd); + S390_lowcore.user_asce = mm->context | __pa(mm->pgd); if (switch_amode) { /* Load primary space page table origin. */ - pgd_t *shadow_pgd = get_shadow_table(pgd) ? : pgd; - S390_lowcore.user_exec_asce = asce_bits | __pa(shadow_pgd); + pgd_t *shadow_pgd = get_shadow_table(mm->pgd) ? : mm->pgd; + S390_lowcore.user_exec_asce = mm->context | __pa(shadow_pgd); asm volatile(LCTL_OPCODE" 1,1,%0\n" : : "m" (S390_lowcore.user_exec_asce) ); } else diff --git a/include/asm-s390/tlbflush.h b/include/asm-s390/tlbflush.h index a69bd24..a5e8765 100644 --- a/include/asm-s390/tlbflush.h +++ b/include/asm-s390/tlbflush.h @@ -42,11 +42,11 @@ static inline void __tlb_flush_global(void) /* * Flush all tlb entries of a page table on all cpus. */ -static inline void __tlb_flush_idte(pgd_t *pgd) +static inline void __tlb_flush_idte(unsigned long asce) { asm volatile( " .insn rrf,0xb98e0000,0,%0,%1,0" - : : "a" (2048), "a" (__pa(pgd) & PAGE_MASK) : "cc" ); + : : "a" (2048), "a" (asce) : "cc" ); } static inline void __tlb_flush_mm(struct mm_struct * mm) @@ -61,11 +61,11 @@ static inline void __tlb_flush_mm(struct mm_struct * mm) * only ran on the local cpu. */ if (MACHINE_HAS_IDTE) { - pgd_t *shadow_pgd = get_shadow_table(mm->pgd); + pgd_t *shadow = get_shadow_table(mm->pgd); - if (shadow_pgd) - __tlb_flush_idte(shadow_pgd); - __tlb_flush_idte(mm->pgd); + if (shadow) + __tlb_flush_idte((unsigned long) shadow | mm->context); + __tlb_flush_idte((unsigned long) mm->pgd | mm->context); return; } preempt_disable(); -- cgit v0.10.2 From 8ffd74a0924e4e04f6455eb2d2187a9564678d01 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Sat, 26 Jan 2008 14:10:59 +0100 Subject: [S390] Avoid warnings in tlblush.h Signed-off-by: Martin Schwidefsky diff --git a/include/asm-s390/tlbflush.h b/include/asm-s390/tlbflush.h index a5e8765..70fa5ae 100644 --- a/include/asm-s390/tlbflush.h +++ b/include/asm-s390/tlbflush.h @@ -106,9 +106,23 @@ static inline void __tlb_flush_mm_cond(struct mm_struct * mm) */ #define flush_tlb() do { } while (0) #define flush_tlb_all() do { } while (0) -#define flush_tlb_mm(mm) __tlb_flush_mm_cond(mm) #define flush_tlb_page(vma, addr) do { } while (0) -#define flush_tlb_range(vma, start, end) __tlb_flush_mm_cond(mm) -#define flush_tlb_kernel_range(start, end) __tlb_flush_mm(&init_mm) + +static inline void flush_tlb_mm(struct mm_struct *mm) +{ + __tlb_flush_mm_cond(mm); +} + +static inline void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + __tlb_flush_mm_cond(vma->vm_mm); +} + +static inline void flush_tlb_kernel_range(unsigned long start, + unsigned long end) +{ + __tlb_flush_mm(&init_mm); +} #endif /* _S390_TLBFLUSH_H */ -- cgit v0.10.2 From 5fd9c6e214547a32d3da6ee4284c79004d667bc8 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Sat, 26 Jan 2008 14:11:00 +0100 Subject: [S390] Change vmalloc defintions Currently the vmalloc area starts at a dynamic address depending on the memory size. There was also an 8MB security hole after the physical memory to catch out-of-bounds accesses. We can simplify the code by putting the vmalloc area explicitely at the top of the kernel mapping and setting the vmalloc size to a fixed value of 128MB/128GB for 31bit/64bit systems. Part of the vmalloc area will be used for the vmem_map. This leaves an area of 96MB/1GB for normal vmalloc allocations. Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index d68a402..d071a81 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -617,7 +617,7 @@ EXPORT_SYMBOL_GPL(real_memory_size); static void __init setup_memory_end(void) { unsigned long memory_size; - unsigned long max_mem, max_phys; + unsigned long max_mem; int i; #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) @@ -625,10 +625,10 @@ static void __init setup_memory_end(void) memory_end = ZFCPDUMP_HSA_SIZE; #endif memory_size = 0; - max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE; memory_end &= PAGE_MASK; - max_mem = memory_end ? min(max_phys, memory_end) : max_phys; + max_mem = memory_end ? min(VMALLOC_START, memory_end) : VMALLOC_START; + memory_end = min(max_mem, memory_end); for (i = 0; i < MEMORY_CHUNKS; i++) { struct mem_chunk *chunk = &memory_chunk[i]; diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index fb9c5a8..ee625c8 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -15,10 +15,6 @@ #include #include -unsigned long vmalloc_end; -EXPORT_SYMBOL(vmalloc_end); - -static struct page *vmem_map; static DEFINE_MUTEX(vmem_mutex); struct memory_segment { @@ -188,8 +184,8 @@ static int vmem_add_mem_map(unsigned long start, unsigned long size) pte_t pte; int ret = -ENOMEM; - map_start = vmem_map + PFN_DOWN(start); - map_end = vmem_map + PFN_DOWN(start + size); + map_start = VMEM_MAP + PFN_DOWN(start); + map_end = VMEM_MAP + PFN_DOWN(start + size); start_addr = (unsigned long) map_start & PAGE_MASK; end_addr = PFN_ALIGN((unsigned long) map_end); @@ -254,7 +250,7 @@ static int insert_memory_segment(struct memory_segment *seg) { struct memory_segment *tmp; - if (PFN_DOWN(seg->start + seg->size) > max_pfn || + if (seg->start + seg->size >= VMALLOC_START || seg->start + seg->size < seg->start) return -ERANGE; @@ -357,17 +353,15 @@ out: /* * map whole physical memory to virtual memory (identity mapping) + * we reserve enough space in the vmalloc area for vmemmap to hotplug + * additional memory segments. */ void __init vmem_map_init(void) { - unsigned long map_size; int i; - map_size = ALIGN(max_low_pfn, MAX_ORDER_NR_PAGES) * sizeof(struct page); - vmalloc_end = PFN_ALIGN(VMALLOC_END_INIT) - PFN_ALIGN(map_size); - vmem_map = (struct page *) vmalloc_end; - NODE_DATA(0)->node_mem_map = vmem_map; - + BUILD_BUG_ON((unsigned long)VMEM_MAP + VMEM_MAP_SIZE > VMEM_MAP_MAX); + NODE_DATA(0)->node_mem_map = VMEM_MAP; for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size); } diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index 1f530f8..79b9eab 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -104,41 +104,27 @@ extern char empty_zero_page[PAGE_SIZE]; #ifndef __ASSEMBLY__ /* - * Just any arbitrary offset to the start of the vmalloc VM area: the - * current 8MB value just means that there will be a 8MB "hole" after the - * physical memory until the kernel virtual memory starts. That means that - * any out-of-bounds memory accesses will hopefully be caught. - * The vmalloc() routines leaves a hole of 4kB between each vmalloced - * area for the same reason. ;) - * vmalloc area starts at 4GB to prevent syscall table entry exchanging - * from modules. - */ -extern unsigned long vmalloc_end; - -#ifdef CONFIG_64BIT -#define VMALLOC_ADDR (max(0x100000000UL, (unsigned long) high_memory)) -#else -#define VMALLOC_ADDR ((unsigned long) high_memory) -#endif -#define VMALLOC_OFFSET (8*1024*1024) -#define VMALLOC_START ((VMALLOC_ADDR + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) -#define VMALLOC_END vmalloc_end - -/* - * We need some free virtual space to be able to do vmalloc. - * VMALLOC_MIN_SIZE defines the minimum size of the vmalloc - * area. On a machine with 2GB memory we make sure that we - * have at least 128MB free space for vmalloc. On a machine - * with 4TB we make sure we have at least 128GB. + * The vmalloc area will always be on the topmost area of the kernel + * mapping. We reserve 96MB (31bit) / 1GB (64bit) for vmalloc, + * which should be enough for any sane case. + * By putting vmalloc at the top, we maximise the gap between physical + * memory and vmalloc to catch misplaced memory accesses. As a side + * effect, this also makes sure that 64 bit module code cannot be used + * as system call address. */ #ifndef __s390x__ -#define VMALLOC_MIN_SIZE 0x8000000UL -#define VMALLOC_END_INIT 0x80000000UL +#define VMALLOC_START 0x78000000UL +#define VMALLOC_END 0x7e000000UL +#define VMEM_MAP_MAX 0x80000000UL #else /* __s390x__ */ -#define VMALLOC_MIN_SIZE 0x2000000000UL -#define VMALLOC_END_INIT 0x40000000000UL +#define VMALLOC_START 0x3e000000000UL +#define VMALLOC_END 0x3e040000000UL +#define VMEM_MAP_MAX 0x40000000000UL #endif /* __s390x__ */ +#define VMEM_MAP ((struct page *) VMALLOC_END) +#define VMEM_MAP_SIZE ((VMALLOC_START / PAGE_SIZE) * sizeof(struct page)) + /* * A 31 bit pagetable entry of S390 has following format: * | PFRA | | OS | -- cgit v0.10.2 From 5c699714d01ac358c58be9943234081b36b69cdd Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:11:01 +0100 Subject: [S390] Print kernel version in dump_stack() and show_regs(). Also print PREEMPT and/or SMP if the kernel was configured that way. Makes s390 look a bit more like other architectures. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 29f7884..0e7aca0 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -36,7 +36,7 @@ #include #include #include - +#include #include #include #include @@ -182,13 +182,15 @@ void cpu_idle(void) void show_regs(struct pt_regs *regs) { - struct task_struct *tsk = current; - - printk("CPU: %d %s\n", task_thread_info(tsk)->cpu, print_tainted()); - printk("Process %s (pid: %d, task: %p, ksp: %p)\n", - current->comm, task_pid_nr(current), (void *) tsk, - (void *) tsk->thread.ksp); - + print_modules(); + printk("CPU: %d %s %s %.*s\n", + task_thread_info(current)->cpu, print_tainted(), + init_utsname()->release, + (int)strcspn(init_utsname()->version, " "), + init_utsname()->version); + printk("Process %s (pid: %d, task: %p, ksp: %p)\n", + current->comm, current->pid, current, + (void *) current->thread.ksp); show_registers(regs); /* Show stack backtrace if pt_regs is from kernel mode */ if (!(regs->psw.mask & PSW_MASK_PSTATE)) diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 8ed16a8..52b8342 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -168,9 +169,16 @@ void show_stack(struct task_struct *task, unsigned long *sp) */ void dump_stack(void) { + printk("CPU: %d %s %s %.*s\n", + task_thread_info(current)->cpu, print_tainted(), + init_utsname()->release, + (int)strcspn(init_utsname()->version, " "), + init_utsname()->version); + printk("Process %s (pid: %d, task: %p, ksp: %p)\n", + current->comm, current->pid, current, + (void *) current->thread.ksp); show_stack(NULL, NULL); } - EXPORT_SYMBOL(dump_stack); static inline int mask_bits(struct pt_regs *regs, unsigned long bits) @@ -258,8 +266,14 @@ void die(const char * str, struct pt_regs * regs, long err) console_verbose(); spin_lock_irq(&die_lock); bust_spinlocks(1); - printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); - print_modules(); + printk("%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); +#ifdef CONFIG_PREEMPT + printk("PREEMPT "); +#endif +#ifdef CONFIG_SMP + printk("SMP"); +#endif + printk("\n"); notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV); show_regs(regs); bust_spinlocks(0); -- cgit v0.10.2 From 9f4b0ba81f158df459fa2cfc98ab1475c090f29c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:11:02 +0100 Subject: [S390] Get rid of HOLES_IN_ZONE requirement. Align everything to MAX_ORDER so we can get rid of the extra checks. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 1330061..6ef54d2 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -276,9 +276,6 @@ source "kernel/Kconfig.preempt" source "mm/Kconfig" -config HOLES_IN_ZONE - def_bool y - comment "I/O subsystem configuration" config MACHCHK_WARNING diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index d071a81..3a61bfc 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -559,7 +559,9 @@ setup_resources(void) data_resource.start = (unsigned long) &_etext; data_resource.end = (unsigned long) &_edata - 1; - for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { + for (i = 0; i < MEMORY_CHUNKS; i++) { + if (!memory_chunk[i].size) + continue; res = alloc_bootmem_low(sizeof(struct resource)); res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; switch (memory_chunk[i].type) { @@ -630,6 +632,27 @@ static void __init setup_memory_end(void) max_mem = memory_end ? min(VMALLOC_START, memory_end) : VMALLOC_START; memory_end = min(max_mem, memory_end); + /* + * Make sure all chunks are MAX_ORDER aligned so we don't need the + * extra checks that HOLES_IN_ZONE would require. + */ + for (i = 0; i < MEMORY_CHUNKS; i++) { + unsigned long start, end; + struct mem_chunk *chunk; + unsigned long align; + + chunk = &memory_chunk[i]; + align = 1UL << (MAX_ORDER + PAGE_SHIFT - 1); + start = (chunk->addr + align - 1) & ~(align - 1); + end = (chunk->addr + chunk->size) & ~(align - 1); + if (start >= end) + memset(chunk, 0, sizeof(*chunk)); + else { + chunk->addr = start; + chunk->size = end - start; + } + } + for (i = 0; i < MEMORY_CHUNKS; i++) { struct mem_chunk *chunk = &memory_chunk[i]; diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index ee625c8..1fc9989 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -376,7 +376,7 @@ static int __init vmem_convert_memory_chunk(void) int i; mutex_lock(&vmem_mutex); - for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { + for (i = 0; i < MEMORY_CHUNKS; i++) { if (!memory_chunk[i].size) continue; seg = kzalloc(sizeof(*seg), GFP_KERNEL); -- cgit v0.10.2 From 3b4beb31759765efdda9f9431aebfedf828bbfe0 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:11:03 +0100 Subject: [S390] Remove owner_pc member from raw_spinlock_t. Used to contain the address of the holder of the lock. But since the spinlock code is not inlined anymore all locks contain the same address anyway. And since in addtition nobody complained about that for ages its obviously unused. So remove it. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index 8d76403..59c56c3 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c @@ -39,7 +39,7 @@ static inline void _raw_yield_cpu(int cpu) _raw_yield(); } -void _raw_spin_lock_wait(raw_spinlock_t *lp, unsigned int pc) +void _raw_spin_lock_wait(raw_spinlock_t *lp) { int count = spin_retry; unsigned int cpu = ~smp_processor_id(); @@ -53,15 +53,13 @@ void _raw_spin_lock_wait(raw_spinlock_t *lp, unsigned int pc) } if (__raw_spin_is_locked(lp)) continue; - if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) { - lp->owner_pc = pc; + if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) return; - } } } EXPORT_SYMBOL(_raw_spin_lock_wait); -int _raw_spin_trylock_retry(raw_spinlock_t *lp, unsigned int pc) +int _raw_spin_trylock_retry(raw_spinlock_t *lp) { unsigned int cpu = ~smp_processor_id(); int count; @@ -69,10 +67,8 @@ int _raw_spin_trylock_retry(raw_spinlock_t *lp, unsigned int pc) for (count = spin_retry; count > 0; count--) { if (__raw_spin_is_locked(lp)) continue; - if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) { - lp->owner_pc = pc; + if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) return 1; - } } return 0; } diff --git a/include/asm-s390/spinlock.h b/include/asm-s390/spinlock.h index 3fd4382..c1d6e7e 100644 --- a/include/asm-s390/spinlock.h +++ b/include/asm-s390/spinlock.h @@ -58,39 +58,32 @@ _raw_compare_and_swap(volatile unsigned int *lock, do { while (__raw_spin_is_locked(lock)) \ _raw_spin_relax(lock); } while (0) -extern void _raw_spin_lock_wait(raw_spinlock_t *, unsigned int pc); -extern int _raw_spin_trylock_retry(raw_spinlock_t *, unsigned int pc); +extern void _raw_spin_lock_wait(raw_spinlock_t *); +extern int _raw_spin_trylock_retry(raw_spinlock_t *); extern void _raw_spin_relax(raw_spinlock_t *lock); static inline void __raw_spin_lock(raw_spinlock_t *lp) { - unsigned long pc = 1 | (unsigned long) __builtin_return_address(0); int old; old = _raw_compare_and_swap(&lp->owner_cpu, 0, ~smp_processor_id()); - if (likely(old == 0)) { - lp->owner_pc = pc; + if (likely(old == 0)) return; - } - _raw_spin_lock_wait(lp, pc); + _raw_spin_lock_wait(lp); } static inline int __raw_spin_trylock(raw_spinlock_t *lp) { - unsigned long pc = 1 | (unsigned long) __builtin_return_address(0); int old; old = _raw_compare_and_swap(&lp->owner_cpu, 0, ~smp_processor_id()); - if (likely(old == 0)) { - lp->owner_pc = pc; + if (likely(old == 0)) return 1; - } - return _raw_spin_trylock_retry(lp, pc); + return _raw_spin_trylock_retry(lp); } static inline void __raw_spin_unlock(raw_spinlock_t *lp) { - lp->owner_pc = 0; _raw_compare_and_swap(&lp->owner_cpu, lp->owner_cpu, 0); } diff --git a/include/asm-s390/spinlock_types.h b/include/asm-s390/spinlock_types.h index b7ac13f..654abc4 100644 --- a/include/asm-s390/spinlock_types.h +++ b/include/asm-s390/spinlock_types.h @@ -7,7 +7,6 @@ typedef struct { volatile unsigned int owner_cpu; - volatile unsigned int owner_pc; } __attribute__ ((aligned (4))) raw_spinlock_t; #define __RAW_SPIN_LOCK_UNLOCKED { 0 } -- cgit v0.10.2 From 519580fc179be6d06a40edd918747bd5f0e1b899 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:11:04 +0100 Subject: [S390] Use new style spinlock initializer in __RWSEM_INITIALIZER. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/include/asm-s390/rwsem.h b/include/asm-s390/rwsem.h index 90f4ecc..9d2a179 100644 --- a/include/asm-s390/rwsem.h +++ b/include/asm-s390/rwsem.h @@ -91,8 +91,8 @@ struct rw_semaphore { #endif #define __RWSEM_INITIALIZER(name) \ -{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEP_MAP_INIT(name) } + { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait.lock), \ + LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) -- cgit v0.10.2 From 48483b3290988952a593c6e66ca354c19f1a4350 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:11:05 +0100 Subject: [S390] Get rid of additional_cpus kernel parameter. It caused only a lot of confusion. From now on cpu hotplug of up to NR_CPUS will work by default. If somebody wants to limit that then the possible_cpus parameter can be used. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/Documentation/cpu-hotplug.txt b/Documentation/cpu-hotplug.txt index fb94f5a..ba0aacd 100644 --- a/Documentation/cpu-hotplug.txt +++ b/Documentation/cpu-hotplug.txt @@ -50,7 +50,7 @@ additional_cpus=n (*) Use this to limit hotpluggable cpus. This option sets cpu_possible_map = cpu_present_map + additional_cpus (*) Option valid only for following architectures -- x86_64, ia64, s390 +- x86_64, ia64 ia64 and x86_64 use the number of disabled local apics in ACPI tables MADT to determine the number of potentially hot-pluggable cpus. The implementation diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index c7cbb01..9f7b73b 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -278,7 +278,6 @@ void __init startup_init(void) setup_lowcore_early(); sclp_read_info_early(); sclp_facilities_detect(); - sclp_read_cpu_info_early(); memsize = sclp_memory_detect(); #ifndef CONFIG_64BIT /* diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 3a61bfc..cbdf3fb 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -922,7 +922,6 @@ setup_arch(char **cmdline_p) cpu_init(); __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr; - smp_setup_cpu_possible_map(); /* * Setup capabilities (ELF_HWCAP & ELF_PLATFORM). diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 66fe289..320e4e9 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -54,7 +54,7 @@ EXPORT_SYMBOL(lowcore_ptr); cpumask_t cpu_online_map = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); -cpumask_t cpu_possible_map = CPU_MASK_NONE; +cpumask_t cpu_possible_map = CPU_MASK_ALL; EXPORT_SYMBOL(cpu_possible_map); static struct task_struct *current_set[NR_CPUS]; @@ -399,7 +399,7 @@ static void __init smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) "kernel was compiled with NR_CPUS=%i\n", cpu, NR_CPUS); return; } - zfcpdump_save_areas[cpu] = alloc_bootmem(sizeof(union save_area)); + zfcpdump_save_areas[cpu] = kmalloc(sizeof(union save_area), GFP_KERNEL); __cpu_logical_map[CPU_INIT_NO] = (__u16) phy_cpu; while (signal_processor(CPU_INIT_NO, sigp_stop_and_store_status) == sigp_busy) @@ -435,67 +435,6 @@ static int cpu_stopped(int cpu) return 0; } -/* - * Lets check how many CPUs we have. - */ -static void __init smp_count_cpus(unsigned int *configured_cpus, - unsigned int *standby_cpus) -{ - unsigned int cpu; - struct sclp_cpu_info *info; - u16 boot_cpu_addr, cpu_addr; - - boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; - current_thread_info()->cpu = 0; - *configured_cpus = 1; - *standby_cpus = 0; - - info = alloc_bootmem_pages(sizeof(*info)); - if (!info) - disabled_wait((unsigned long) __builtin_return_address(0)); - - /* Use sigp detection algorithm if sclp doesn't work. */ - if (sclp_get_cpu_info(info)) { - smp_use_sigp_detection = 1; - for (cpu = 0; cpu <= 65535; cpu++) { - if (cpu == boot_cpu_addr) - continue; - __cpu_logical_map[CPU_INIT_NO] = cpu; - if (cpu_stopped(CPU_INIT_NO)) - (*configured_cpus)++; - } - goto out; - } - - if (info->has_cpu_type) { - for (cpu = 0; cpu < info->combined; cpu++) { - if (info->cpu[cpu].address == boot_cpu_addr) { - smp_cpu_type = info->cpu[cpu].type; - break; - } - } - } - /* Count cpus. */ - for (cpu = 0; cpu < info->combined; cpu++) { - if (info->has_cpu_type && info->cpu[cpu].type != smp_cpu_type) - continue; - cpu_addr = info->cpu[cpu].address; - if (cpu_addr == boot_cpu_addr) - continue; - __cpu_logical_map[CPU_INIT_NO] = cpu_addr; - if (!cpu_stopped(CPU_INIT_NO)) { - (*standby_cpus)++; - continue; - } - smp_get_save_area(*configured_cpus, cpu_addr); - (*configured_cpus)++; - } -out: - printk(KERN_INFO "CPUs: %d configured, %d standby\n", - *configured_cpus, *standby_cpus); - free_bootmem((unsigned long) info, sizeof(*info)); -} - static int cpu_known(int cpu_id) { int cpu; @@ -529,7 +468,7 @@ static int smp_rescan_cpus_sigp(cpumask_t avail) return 0; } -static int __init_refok smp_rescan_cpus_sclp(cpumask_t avail) +static int smp_rescan_cpus_sclp(cpumask_t avail) { struct sclp_cpu_info *info; int cpu_id, logical_cpu, cpu; @@ -538,10 +477,7 @@ static int __init_refok smp_rescan_cpus_sclp(cpumask_t avail) logical_cpu = first_cpu(avail); if (logical_cpu == NR_CPUS) return 0; - if (slab_is_available()) - info = kmalloc(sizeof(*info), GFP_KERNEL); - else - info = alloc_bootmem(sizeof(*info)); + info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; rc = sclp_get_cpu_info(info); @@ -564,10 +500,7 @@ static int __init_refok smp_rescan_cpus_sclp(cpumask_t avail) break; } out: - if (slab_is_available()) - kfree(info); - else - free_bootmem((unsigned long) info, sizeof(*info)); + kfree(info); return rc; } @@ -575,15 +508,71 @@ static int smp_rescan_cpus(void) { cpumask_t avail; - cpus_setall(avail); - cpus_and(avail, avail, cpu_possible_map); - cpus_andnot(avail, avail, cpu_present_map); + cpus_xor(avail, cpu_possible_map, cpu_present_map); if (smp_use_sigp_detection) return smp_rescan_cpus_sigp(avail); else return smp_rescan_cpus_sclp(avail); } +static void __init smp_detect_cpus(void) +{ + unsigned int cpu, c_cpus, s_cpus; + struct sclp_cpu_info *info; + u16 boot_cpu_addr, cpu_addr; + + c_cpus = 1; + s_cpus = 0; + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + panic("smp_detect_cpus failed to allocate memory\n"); + /* Use sigp detection algorithm if sclp doesn't work. */ + if (sclp_get_cpu_info(info)) { + smp_use_sigp_detection = 1; + for (cpu = 0; cpu <= 65535; cpu++) { + if (cpu == boot_cpu_addr) + continue; + __cpu_logical_map[CPU_INIT_NO] = cpu; + if (!cpu_stopped(CPU_INIT_NO)) + continue; + smp_get_save_area(c_cpus, cpu); + c_cpus++; + } + goto out; + } + + if (info->has_cpu_type) { + for (cpu = 0; cpu < info->combined; cpu++) { + if (info->cpu[cpu].address == boot_cpu_addr) { + smp_cpu_type = info->cpu[cpu].type; + break; + } + } + } + + for (cpu = 0; cpu < info->combined; cpu++) { + if (info->has_cpu_type && info->cpu[cpu].type != smp_cpu_type) + continue; + cpu_addr = info->cpu[cpu].address; + if (cpu_addr == boot_cpu_addr) + continue; + __cpu_logical_map[CPU_INIT_NO] = cpu_addr; + if (!cpu_stopped(CPU_INIT_NO)) { + s_cpus++; + continue; + } + smp_get_save_area(c_cpus, cpu_addr); + c_cpus++; + } +out: + kfree(info); + printk(KERN_INFO "CPUs: %d configured, %d standby\n", c_cpus, s_cpus); + lock_cpu_hotplug(); + smp_rescan_cpus(); + unlock_cpu_hotplug(); +} + /* * Activate a secondary processor. */ @@ -674,41 +663,20 @@ int __cpu_up(unsigned int cpu) return 0; } -static unsigned int __initdata additional_cpus; -static unsigned int __initdata possible_cpus; - -void __init smp_setup_cpu_possible_map(void) -{ - unsigned int pos_cpus, cpu; - unsigned int configured_cpus, standby_cpus; - - smp_count_cpus(&configured_cpus, &standby_cpus); - pos_cpus = min(configured_cpus + standby_cpus + additional_cpus, - (unsigned int) NR_CPUS); - if (possible_cpus) - pos_cpus = min(possible_cpus, (unsigned int) NR_CPUS); - for (cpu = 0; cpu < pos_cpus; cpu++) - cpu_set(cpu, cpu_possible_map); - cpu_present_map = cpumask_of_cpu(0); - smp_rescan_cpus(); -} - -#ifdef CONFIG_HOTPLUG_CPU - -static int __init setup_additional_cpus(char *s) -{ - additional_cpus = simple_strtoul(s, NULL, 0); - return 0; -} -early_param("additional_cpus", setup_additional_cpus); - static int __init setup_possible_cpus(char *s) { - possible_cpus = simple_strtoul(s, NULL, 0); + int pcpus, cpu; + + pcpus = simple_strtoul(s, NULL, 0); + cpu_possible_map = cpumask_of_cpu(0); + for (cpu = 1; cpu < pcpus && cpu < NR_CPUS; cpu++) + cpu_set(cpu, cpu_possible_map); return 0; } early_param("possible_cpus", setup_possible_cpus); +#ifdef CONFIG_HOTPLUG_CPU + int __cpu_disable(void) { struct ec_creg_mask_parms cr_parms; @@ -768,6 +736,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus) unsigned int cpu; int i; + smp_detect_cpus(); + /* request the 0x1201 emergency signal external interrupt */ if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0) panic("Couldn't request external interrupt 0x1201"); @@ -816,6 +786,8 @@ void __init smp_prepare_boot_cpu(void) { BUG_ON(smp_processor_id() != 0); + current_thread_info()->cpu = 0; + cpu_set(0, cpu_present_map); cpu_set(0, cpu_online_map); S390_lowcore.percpu_offset = __per_cpu_offset[0]; current_set[0] = current; diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index d7e6f4d..b5c2339 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -191,9 +191,6 @@ struct read_cpu_info_sccb { u8 reserved[4096 - 16]; } __attribute__((packed, aligned(PAGE_SIZE))); -static struct read_cpu_info_sccb __initdata early_read_cpu_info_sccb; -static struct sclp_cpu_info __initdata sclp_cpu_info; - static void sclp_fill_cpu_info(struct sclp_cpu_info *info, struct read_cpu_info_sccb *sccb) { @@ -208,48 +205,16 @@ static void sclp_fill_cpu_info(struct sclp_cpu_info *info, info->combined * sizeof(struct sclp_cpu_entry)); } -void __init sclp_read_cpu_info_early(void) -{ - int rc; - struct read_cpu_info_sccb *sccb; - - if (!SCLP_HAS_CPU_INFO) - return; - - sccb = &early_read_cpu_info_sccb; - do { - memset(sccb, 0, sizeof(*sccb)); - sccb->header.length = sizeof(*sccb); - rc = sclp_cmd_sync_early(SCLP_CMDW_READ_CPU_INFO, sccb); - } while (rc == -EBUSY); - - if (rc) - return; - if (sccb->header.response_code != 0x10) - return; - sclp_fill_cpu_info(&sclp_cpu_info, sccb); -} - -static int __init sclp_get_cpu_info_early(struct sclp_cpu_info *info) -{ - if (!SCLP_HAS_CPU_INFO) - return -EOPNOTSUPP; - *info = sclp_cpu_info; - return 0; -} - -static int sclp_get_cpu_info_late(struct sclp_cpu_info *info) +int sclp_get_cpu_info(struct sclp_cpu_info *info) { int rc; struct read_cpu_info_sccb *sccb; if (!SCLP_HAS_CPU_INFO) return -EOPNOTSUPP; - sccb = (struct read_cpu_info_sccb *) __get_free_page(GFP_KERNEL - | GFP_DMA); + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sccb) return -ENOMEM; - memset(sccb, 0, sizeof(*sccb)); sccb->header.length = sizeof(*sccb); rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); if (rc) @@ -266,13 +231,6 @@ out: return rc; } -int __init_refok sclp_get_cpu_info(struct sclp_cpu_info *info) -{ - if (slab_is_available()) - return sclp_get_cpu_info_late(info); - return sclp_get_cpu_info_early(info); -} - struct cpu_configure_sccb { struct sccb_header header; } __attribute__((packed, aligned(8))); diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h index b8c7695..b5f2843 100644 --- a/include/asm-s390/sclp.h +++ b/include/asm-s390/sclp.h @@ -46,7 +46,6 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info); int sclp_cpu_configure(u8 cpu); int sclp_cpu_deconfigure(u8 cpu); void sclp_read_info_early(void); -void sclp_read_cpu_info_early(void); void sclp_facilities_detect(void); unsigned long long sclp_memory_detect(void); int sclp_sdias_blk_count(void); diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h index 07708c0..218454b 100644 --- a/include/asm-s390/smp.h +++ b/include/asm-s390/smp.h @@ -35,8 +35,6 @@ extern void machine_restart_smp(char *); extern void machine_halt_smp(void); extern void machine_power_off_smp(void); -extern void smp_setup_cpu_possible_map(void); - #define NO_PROC_ID 0xFF /* No processor magic marker */ /* @@ -103,7 +101,6 @@ static inline void smp_send_stop(void) #define hard_smp_processor_id() 0 #define smp_cpu_not_running(cpu) 1 -#define smp_setup_cpu_possible_map() do { } while (0) #endif extern union save_area *zfcpdump_save_areas[NR_CPUS + 1]; -- cgit v0.10.2 From 81ef16e763bb899053e06f6050603a305456a085 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:11:06 +0100 Subject: [S390] Remove appldata include from sysctl_check.c Forgot to remove this when removing the appldata binary sysctls. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c index a68425a..d8a5558 100644 --- a/kernel/sysctl_check.c +++ b/kernel/sysctl_check.c @@ -1,6 +1,5 @@ #include #include -#include "../arch/s390/appldata/appldata.h" #include "../fs/xfs/linux-2.6/xfs_sysctl.h" #include #include -- cgit v0.10.2 From 3f5615e012b4593943bbc2a6c3ce150bb33e1966 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Sat, 26 Jan 2008 14:11:07 +0100 Subject: [S390] crypto: move s390 Kconfig options. Move s390 crypto Kconfig options to drivers/crypto/Kconfig to have all hardware crypto devices in one place. This also makes messing up the kernel source tree easier for some people. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/crypto/Kconfig b/arch/s390/crypto/Kconfig deleted file mode 100644 index d1defbb..0000000 --- a/arch/s390/crypto/Kconfig +++ /dev/null @@ -1,60 +0,0 @@ -config CRYPTO_SHA1_S390 - tristate "SHA1 digest algorithm" - depends on S390 - select CRYPTO_ALGAPI - help - This is the s390 hardware accelerated implementation of the - SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2). - -config CRYPTO_SHA256_S390 - tristate "SHA256 digest algorithm" - depends on S390 - select CRYPTO_ALGAPI - help - This is the s390 hardware accelerated implementation of the - SHA256 secure hash standard (DFIPS 180-2). - - This version of SHA implements a 256 bit hash with 128 bits of - security against collision attacks. - -config CRYPTO_DES_S390 - tristate "DES and Triple DES cipher algorithms" - depends on S390 - select CRYPTO_ALGAPI - select CRYPTO_BLKCIPHER - help - This us the s390 hardware accelerated implementation of the - DES cipher algorithm (FIPS 46-2), and Triple DES EDE (FIPS 46-3). - -config CRYPTO_AES_S390 - tristate "AES cipher algorithms" - depends on S390 - select CRYPTO_ALGAPI - select CRYPTO_BLKCIPHER - help - This is the s390 hardware accelerated implementation of the - AES cipher algorithms (FIPS-197). AES uses the Rijndael - algorithm. - - Rijndael appears to be consistently a very good performer in - both hardware and software across a wide range of computing - environments regardless of its use in feedback or non-feedback - modes. Its key setup time is excellent, and its key agility is - good. Rijndael's very low memory requirements make it very well - suited for restricted-space environments, in which it also - demonstrates excellent performance. Rijndael's operations are - among the easiest to defend against power and timing attacks. - - On s390 the System z9-109 currently only supports the key size - of 128 bit. - -config S390_PRNG - tristate "Pseudo random number generator device driver" - depends on S390 - default "m" - help - Select this option if you want to use the s390 pseudo random number - generator. The PRNG is part of the cryptographic processor functions - and uses triple-DES to generate secure random numbers like the - ANSI X9.17 standard. The PRNG is usable via the char device - /dev/prandom. diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 8a70a9e..6b658d8 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -48,8 +48,6 @@ config CRYPTO_DEV_PADLOCK_SHA If unsure say M. The compiled module will be called padlock-sha.ko -source "arch/s390/crypto/Kconfig" - config CRYPTO_DEV_GEODE tristate "Support for the Geode LX AES engine" depends on X86_32 && PCI @@ -83,6 +81,67 @@ config ZCRYPT_MONOLITHIC that contains all parts of the crypto device driver (ap bus, request router and all the card drivers). +config CRYPTO_SHA1_S390 + tristate "SHA1 digest algorithm" + depends on S390 + select CRYPTO_ALGAPI + help + This is the s390 hardware accelerated implementation of the + SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2). + +config CRYPTO_SHA256_S390 + tristate "SHA256 digest algorithm" + depends on S390 + select CRYPTO_ALGAPI + help + This is the s390 hardware accelerated implementation of the + SHA256 secure hash standard (DFIPS 180-2). + + This version of SHA implements a 256 bit hash with 128 bits of + security against collision attacks. + +config CRYPTO_DES_S390 + tristate "DES and Triple DES cipher algorithms" + depends on S390 + select CRYPTO_ALGAPI + select CRYPTO_BLKCIPHER + help + This us the s390 hardware accelerated implementation of the + DES cipher algorithm (FIPS 46-2), and Triple DES EDE (FIPS 46-3). + +config CRYPTO_AES_S390 + tristate "AES cipher algorithms" + depends on S390 + select CRYPTO_ALGAPI + select CRYPTO_BLKCIPHER + help + This is the s390 hardware accelerated implementation of the + AES cipher algorithms (FIPS-197). AES uses the Rijndael + algorithm. + + Rijndael appears to be consistently a very good performer in + both hardware and software across a wide range of computing + environments regardless of its use in feedback or non-feedback + modes. Its key setup time is excellent, and its key agility is + good. Rijndael's very low memory requirements make it very well + suited for restricted-space environments, in which it also + demonstrates excellent performance. Rijndael's operations are + among the easiest to defend against power and timing attacks. + + On s390 the System z9-109 currently only supports the key size + of 128 bit. + +config S390_PRNG + tristate "Pseudo random number generator device driver" + depends on S390 + default "m" + help + Select this option if you want to use the s390 pseudo random number + generator. The PRNG is part of the cryptographic processor functions + and uses triple-DES to generate secure random numbers like the + ANSI X9.17 standard. The PRNG is usable via the char device + /dev/prandom. + config CRYPTO_DEV_HIFN_795X tristate "Driver HIFN 795x crypto accelerator chips" select CRYPTO_DES -- cgit v0.10.2 From de3e0da1270bccb046885fbf1baa9140721de7e0 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Sat, 26 Jan 2008 14:11:08 +0100 Subject: [S390] dasd: fix return value of dasd_generic_probe() Using the return value of ccw_device_set_online as return value for dasd_generic_probe() causes the DASD to fail setting online Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index e6bfce6..da4fe1e 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1969,9 +1969,10 @@ dasd_generic_probe (struct ccw_device *cdev, ret = ccw_device_set_online(cdev); if (ret) printk(KERN_WARNING - "dasd_generic_probe: could not initially online " - "ccw-device %s\n", cdev->dev.bus_id); - return ret; + "dasd_generic_probe: could not initially " + "online ccw-device %s; return code: %d\n", + cdev->dev.bus_id, ret); + return 0; } /* -- cgit v0.10.2 From cfe7381cea542248766808e7de9a9e7bceecce1a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 26 Jan 2008 14:11:09 +0100 Subject: [S390] arch/s390: Add missing "space" Signed-off-by: Joe Perches Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c index 46c97058..a3f67f8 100644 --- a/arch/s390/crypto/aes_s390.c +++ b/arch/s390/crypto/aes_s390.c @@ -516,7 +516,7 @@ static int __init aes_init(void) /* z9 109 and z9 BC/EC only support 128 bit key length */ if (keylen_flag == AES_KEYLEN_128) printk(KERN_INFO - "aes_s390: hardware acceleration only available for" + "aes_s390: hardware acceleration only available for " "128 bit keys\n"); ret = crypto_register_alg(&aes_alg); -- cgit v0.10.2 From ceb3dfbae1d3faacaedfc64c913fd18cd3624f72 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 26 Jan 2008 14:11:10 +0100 Subject: [S390] drivers/s390: Add missing "space" Signed-off-by: Joe Perches Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 20442fb..a86c053 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -295,7 +295,7 @@ module_init(mon_init); module_exit(mon_exit); module_param_named(max_bufs, mon_max_bufs, int, 0644); -MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers" +MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers " "that can be active at one time"); MODULE_AUTHOR("Melissa Howland "); diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index e0c4c50..d364e0b 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -683,7 +683,7 @@ static int vmlogrdr_register_driver(void) /* Register with iucv driver */ ret = iucv_register(&vmlogrdr_iucv_handler, 1); if (ret) { - printk (KERN_ERR "vmlogrdr: failed to register with" + printk (KERN_ERR "vmlogrdr: failed to register with " "iucv driver\n"); goto out; } diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 93e6f74..e7ba16a 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1042,7 +1042,7 @@ chsc_determine_css_characteristics(void) scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scsc_area) { - CIO_MSG_EVENT(0, "Was not able to determine available" + CIO_MSG_EVENT(0, "Was not able to determine available " "CHSCs due to no memory.\n"); return -ENOMEM; } diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 3561982..c307621 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -2416,7 +2416,7 @@ init_ccw_bk(struct net_device *dev) privptr->p_buff_pages_perwrite); #endif if (p_buff==NULL) { - printk(KERN_INFO "%s:%s __get_free_pages" + printk(KERN_INFO "%s:%s __get_free_pages " "for writes buf failed : get is for %d pages\n", dev->name, __FUNCTION__, diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 0fd663b..7bfe8d7 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1115,7 +1115,7 @@ list_modified: rc = lcs_send_setipm(card, ipm); spin_lock_irqsave(&card->ipm_lock, flags); if (rc) { - PRINT_INFO("Adding multicast address failed." + PRINT_INFO("Adding multicast address failed. " "Table possibly full!\n"); /* store ipm in failed list -> will be added * to ipm_list again, so a retry will be done diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index fe57941..e45f85f 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -502,7 +502,7 @@ zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req) fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_SQ_NO_RECOM: - ZFCP_LOG_NORMAL("bug: No recommendation could be given for a" + ZFCP_LOG_NORMAL("bug: No recommendation could be given for a " "problem on the adapter %s " "Stopping all operations on this adapter. ", zfcp_get_busid_by_adapter(fsf_req->adapter)); @@ -813,7 +813,7 @@ zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req) read_unlock_irqrestore(&zfcp_data.config_lock, flags); if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) { - ZFCP_LOG_NORMAL("bug: Reopen port indication received for" + ZFCP_LOG_NORMAL("bug: Reopen port indication received for " "nonexisting port with d_id 0x%06x on " "adapter %s. Ignored.\n", status_buffer->d_id & ZFCP_DID_MASK, @@ -2281,7 +2281,7 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action) &lock_flags, &fsf_req); if (retval) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " - "exchange port data request for" + "exchange port data request for " "the adapter %s.\n", zfcp_get_busid_by_adapter(adapter)); write_unlock_irqrestore(&adapter->request_queue.queue_lock, @@ -2340,7 +2340,7 @@ zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter, 0, NULL, &lock_flags, &fsf_req); if (retval) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " - "exchange port data request for" + "exchange port data request for " "the adapter %s.\n", zfcp_get_busid_by_adapter(adapter)); write_unlock_irqrestore(&adapter->request_queue.queue_lock, @@ -4725,7 +4725,7 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags, /* allocate new FSF request */ fsf_req = zfcp_fsf_req_alloc(pool, req_flags); if (unlikely(NULL == fsf_req)) { - ZFCP_LOG_DEBUG("error: Could not put an FSF request into" + ZFCP_LOG_DEBUG("error: Could not put an FSF request into " "the outbound (send) queue.\n"); ret = -ENOMEM; goto failed_fsf_req; -- cgit v0.10.2 From 99ca4e582d4a4088969681efff97be44d98421a1 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Sat, 26 Jan 2008 14:11:11 +0100 Subject: [S390] kernel: Shutdown Actions Interface In case of a kernel panic it is currently possible to specify that a dump should be created, the system should be rebooted or stopped. Virtual sysfs files under the directory /sys/firmware/ are used for that configuration. In addition to that, there are kernel parameters 'vmhalt', 'vmpoff' and 'vmpanic', which can be used to specify z/VM commands, which are automatically executed in case of halt, power off or a kernel panic. This patch combines both functionalities and allows to specify the z/VM CP commands also via sysfs attributes. In addition to that, it enhances the existing handling of shutdown triggers (e.g. halt or panic) and associated shutdown actions (e.g. dump or reipl) and makes it more flexible. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index b97694f..d73aff6 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -2,7 +2,7 @@ * arch/s390/kernel/ipl.c * ipl/reipl/dump support for Linux on s390. * - * Copyright (C) IBM Corp. 2005,2006 + * Copyright IBM Corp. 2005,2007 * Author(s): Michael Holzheu * Heiko Carstens * Volker Sameske @@ -31,6 +31,43 @@ #define IPL_FCP_DUMP_STR "fcp_dump" #define IPL_NSS_STR "nss" +#define DUMP_CCW_STR "ccw" +#define DUMP_FCP_STR "fcp" +#define DUMP_NONE_STR "none" + +/* + * Four shutdown trigger types are supported: + * - panic + * - halt + * - power off + * - reipl + */ +#define ON_PANIC_STR "on_panic" +#define ON_HALT_STR "on_halt" +#define ON_POFF_STR "on_poff" +#define ON_REIPL_STR "on_reboot" + +struct shutdown_action; +struct shutdown_trigger { + char *name; + struct shutdown_action *action; +}; + +/* + * Five shutdown action types are supported: + */ +#define SHUTDOWN_ACTION_IPL_STR "ipl" +#define SHUTDOWN_ACTION_REIPL_STR "reipl" +#define SHUTDOWN_ACTION_DUMP_STR "dump" +#define SHUTDOWN_ACTION_VMCMD_STR "vmcmd" +#define SHUTDOWN_ACTION_STOP_STR "stop" + +struct shutdown_action { + char *name; + void (*fn) (struct shutdown_trigger *trigger); + int (*init) (void); +}; + static char *ipl_type_str(enum ipl_type type) { switch (type) { @@ -54,10 +91,6 @@ enum dump_type { DUMP_TYPE_FCP = 4, }; -#define DUMP_NONE_STR "none" -#define DUMP_CCW_STR "ccw" -#define DUMP_FCP_STR "fcp" - static char *dump_type_str(enum dump_type type) { switch (type) { @@ -99,30 +132,6 @@ enum dump_method { DUMP_METHOD_FCP_DIAG, }; -enum shutdown_action { - SHUTDOWN_REIPL, - SHUTDOWN_DUMP, - SHUTDOWN_STOP, -}; - -#define SHUTDOWN_REIPL_STR "reipl" -#define SHUTDOWN_DUMP_STR "dump" -#define SHUTDOWN_STOP_STR "stop" - -static char *shutdown_action_str(enum shutdown_action action) -{ - switch (action) { - case SHUTDOWN_REIPL: - return SHUTDOWN_REIPL_STR; - case SHUTDOWN_DUMP: - return SHUTDOWN_DUMP_STR; - case SHUTDOWN_STOP: - return SHUTDOWN_STOP_STR; - default: - return NULL; - } -} - static int diag308_set_works = 0; static int reipl_capabilities = IPL_TYPE_UNKNOWN; @@ -140,8 +149,6 @@ static enum dump_method dump_method = DUMP_METHOD_NONE; static struct ipl_parameter_block *dump_block_fcp; static struct ipl_parameter_block *dump_block_ccw; -static enum shutdown_action on_panic_action = SHUTDOWN_STOP; - static struct sclp_ipl_info sclp_ipl_info; int diag308(unsigned long subcode, void *addr) @@ -205,8 +212,8 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ struct kobj_attribute *attr, \ const char *buf, size_t len) \ { \ - if (sscanf(buf, _fmt_in, _value) != 1) \ - return -EINVAL; \ + strncpy(_value, buf, sizeof(_value) - 1); \ + strstrip(_value); \ return len; \ } \ static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ @@ -245,33 +252,6 @@ static __init enum ipl_type get_ipl_type(void) return IPL_TYPE_FCP; } -void __init setup_ipl_info(void) -{ - ipl_info.type = get_ipl_type(); - switch (ipl_info.type) { - case IPL_TYPE_CCW: - ipl_info.data.ccw.dev_id.devno = ipl_devno; - ipl_info.data.ccw.dev_id.ssid = 0; - break; - case IPL_TYPE_FCP: - case IPL_TYPE_FCP_DUMP: - ipl_info.data.fcp.dev_id.devno = - IPL_PARMBLOCK_START->ipl_info.fcp.devno; - ipl_info.data.fcp.dev_id.ssid = 0; - ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn; - ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun; - break; - case IPL_TYPE_NSS: - strncpy(ipl_info.data.nss.name, kernel_nss_name, - sizeof(ipl_info.data.nss.name)); - break; - case IPL_TYPE_UNKNOWN: - default: - /* We have no info to copy */ - break; - } -} - struct ipl_info ipl_info; EXPORT_SYMBOL_GPL(ipl_info); @@ -428,8 +408,74 @@ static struct attribute_group ipl_unknown_attr_group = { static struct kset *ipl_kset; +static int __init ipl_register_fcp_files(void) +{ + int rc; + + rc = sysfs_create_group(&ipl_kset->kobj, &ipl_fcp_attr_group); + if (rc) + goto out; + rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_parameter_attr); + if (rc) + goto out_ipl_parm; + rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_scp_data_attr); + if (!rc) + goto out; + + sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr); + +out_ipl_parm: + sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group); +out: + return rc; +} + +static void ipl_run(struct shutdown_trigger *trigger) +{ + diag308(DIAG308_IPL, NULL); + if (MACHINE_IS_VM) + __cpcmd("IPL", NULL, 0, NULL); + else if (ipl_info.type == IPL_TYPE_CCW) + reipl_ccw_dev(&ipl_info.data.ccw.dev_id); +} + +static int ipl_init(void) +{ + int rc; + + ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj); + if (!ipl_kset) { + rc = -ENOMEM; + goto out; + } + switch (ipl_info.type) { + case IPL_TYPE_CCW: + rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group); + break; + case IPL_TYPE_FCP: + case IPL_TYPE_FCP_DUMP: + rc = ipl_register_fcp_files(); + break; + case IPL_TYPE_NSS: + rc = sysfs_create_group(&ipl_kset->kobj, &ipl_nss_attr_group); + break; + default: + rc = sysfs_create_group(&ipl_kset->kobj, + &ipl_unknown_attr_group); + break; + } +out: + if (rc) + panic("ipl_init failed: rc = %i\n", rc); + + return 0; +} + +static struct shutdown_action ipl_action = {SHUTDOWN_ACTION_IPL_STR, ipl_run, + ipl_init}; + /* - * reipl section + * reipl shutdown action: Reboot Linux on shutdown. */ /* FCP reipl device attributes */ @@ -600,143 +646,11 @@ static ssize_t reipl_type_store(struct kobject *kobj, } static struct kobj_attribute reipl_type_attr = - __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); + __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); static struct kset *reipl_kset; -/* - * dump section - */ - -/* FCP dump device attributes */ - -DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n", - dump_block_fcp->ipl_info.fcp.wwpn); -DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n", - dump_block_fcp->ipl_info.fcp.lun); -DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n", - dump_block_fcp->ipl_info.fcp.bootprog); -DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n", - dump_block_fcp->ipl_info.fcp.br_lba); -DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", - dump_block_fcp->ipl_info.fcp.devno); - -static struct attribute *dump_fcp_attrs[] = { - &sys_dump_fcp_device_attr.attr, - &sys_dump_fcp_wwpn_attr.attr, - &sys_dump_fcp_lun_attr.attr, - &sys_dump_fcp_bootprog_attr.attr, - &sys_dump_fcp_br_lba_attr.attr, - NULL, -}; - -static struct attribute_group dump_fcp_attr_group = { - .name = IPL_FCP_STR, - .attrs = dump_fcp_attrs, -}; - -/* CCW dump device attributes */ - -DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", - dump_block_ccw->ipl_info.ccw.devno); - -static struct attribute *dump_ccw_attrs[] = { - &sys_dump_ccw_device_attr.attr, - NULL, -}; - -static struct attribute_group dump_ccw_attr_group = { - .name = IPL_CCW_STR, - .attrs = dump_ccw_attrs, -}; - -/* dump type */ - -static int dump_set_type(enum dump_type type) -{ - if (!(dump_capabilities & type)) - return -EINVAL; - switch(type) { - case DUMP_TYPE_CCW: - if (MACHINE_IS_VM) - dump_method = DUMP_METHOD_CCW_VM; - else if (diag308_set_works) - dump_method = DUMP_METHOD_CCW_DIAG; - else - dump_method = DUMP_METHOD_CCW_CIO; - break; - case DUMP_TYPE_FCP: - dump_method = DUMP_METHOD_FCP_DIAG; - break; - default: - dump_method = DUMP_METHOD_NONE; - } - dump_type = type; - return 0; -} - -static ssize_t dump_type_show(struct kobject *kobj, - struct kobj_attribute *attr, char *page) -{ - return sprintf(page, "%s\n", dump_type_str(dump_type)); -} - -static ssize_t dump_type_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t len) -{ - int rc = -EINVAL; - - if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) - rc = dump_set_type(DUMP_TYPE_NONE); - else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) - rc = dump_set_type(DUMP_TYPE_CCW); - else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) - rc = dump_set_type(DUMP_TYPE_FCP); - return (rc != 0) ? rc : len; -} - -static struct kobj_attribute dump_type_attr = - __ATTR(dump_type, 0644, dump_type_show, dump_type_store); - -static struct kset *dump_kset; - -/* - * Shutdown actions section - */ - -static struct kset *shutdown_actions_kset; - -/* on panic */ - -static ssize_t on_panic_show(struct kobject *kobj, - struct kobj_attribute *attr, char *page) -{ - return sprintf(page, "%s\n", shutdown_action_str(on_panic_action)); -} - -static ssize_t on_panic_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t len) -{ - if (strncmp(buf, SHUTDOWN_REIPL_STR, strlen(SHUTDOWN_REIPL_STR)) == 0) - on_panic_action = SHUTDOWN_REIPL; - else if (strncmp(buf, SHUTDOWN_DUMP_STR, - strlen(SHUTDOWN_DUMP_STR)) == 0) - on_panic_action = SHUTDOWN_DUMP; - else if (strncmp(buf, SHUTDOWN_STOP_STR, - strlen(SHUTDOWN_STOP_STR)) == 0) - on_panic_action = SHUTDOWN_STOP; - else - return -EINVAL; - - return len; -} - -static struct kobj_attribute on_panic_attr = - __ATTR(on_panic, 0644, on_panic_show, on_panic_store); - -void do_reipl(void) +void reipl_run(struct shutdown_trigger *trigger) { struct ccw_dev_id devid; static char buf[100]; @@ -787,119 +701,27 @@ void do_reipl(void) default: break; } - signal_processor(smp_processor_id(), sigp_stop_and_store_status); } -static void do_dump(void) +static void __init reipl_probe(void) { - struct ccw_dev_id devid; - static char buf[100]; + void *buffer; - switch (dump_method) { - case DUMP_METHOD_CCW_CIO: - smp_send_stop(); - devid.devno = dump_block_ccw->ipl_info.ccw.devno; - devid.ssid = 0; - reipl_ccw_dev(&devid); - break; - case DUMP_METHOD_CCW_VM: - smp_send_stop(); - sprintf(buf, "STORE STATUS"); - __cpcmd(buf, NULL, 0, NULL); - sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); - __cpcmd(buf, NULL, 0, NULL); - break; - case DUMP_METHOD_CCW_DIAG: - diag308(DIAG308_SET, dump_block_ccw); - diag308(DIAG308_DUMP, NULL); - break; - case DUMP_METHOD_FCP_DIAG: - diag308(DIAG308_SET, dump_block_fcp); - diag308(DIAG308_DUMP, NULL); - break; - case DUMP_METHOD_NONE: - default: + buffer = (void *) get_zeroed_page(GFP_KERNEL); + if (!buffer) return; - } - printk(KERN_EMERG "Dump failed!\n"); + if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK) + diag308_set_works = 1; + free_page((unsigned long)buffer); } -/* init functions */ - -static int __init ipl_register_fcp_files(void) +static int __init reipl_nss_init(void) { int rc; - rc = sysfs_create_group(&ipl_kset->kobj, - &ipl_fcp_attr_group); - if (rc) - goto out; - rc = sysfs_create_bin_file(&ipl_kset->kobj, - &ipl_parameter_attr); - if (rc) - goto out_ipl_parm; - rc = sysfs_create_bin_file(&ipl_kset->kobj, - &ipl_scp_data_attr); - if (!rc) - goto out; - - sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr); - -out_ipl_parm: - sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group); -out: - return rc; -} - -static int __init ipl_init(void) -{ - int rc; - - ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj); - if (!ipl_kset) - return -ENOMEM; - switch (ipl_info.type) { - case IPL_TYPE_CCW: - rc = sysfs_create_group(&ipl_kset->kobj, - &ipl_ccw_attr_group); - break; - case IPL_TYPE_FCP: - case IPL_TYPE_FCP_DUMP: - rc = ipl_register_fcp_files(); - break; - case IPL_TYPE_NSS: - rc = sysfs_create_group(&ipl_kset->kobj, - &ipl_nss_attr_group); - break; - default: - rc = sysfs_create_group(&ipl_kset->kobj, - &ipl_unknown_attr_group); - break; - } - if (rc) - kset_unregister(ipl_kset); - return rc; -} - -static void __init reipl_probe(void) -{ - void *buffer; - - buffer = (void *) get_zeroed_page(GFP_KERNEL); - if (!buffer) - return; - if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK) - diag308_set_works = 1; - free_page((unsigned long)buffer); -} - -static int __init reipl_nss_init(void) -{ - int rc; - - if (!MACHINE_IS_VM) - return 0; - rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); + if (!MACHINE_IS_VM) + return 0; + rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); if (rc) return rc; strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1); @@ -970,7 +792,7 @@ static int __init reipl_fcp_init(void) return 0; } -static int __init reipl_init(void) +static int reipl_init(void) { int rc; @@ -997,6 +819,138 @@ static int __init reipl_init(void) return 0; } +static struct shutdown_action reipl_action = {SHUTDOWN_ACTION_REIPL_STR, + reipl_run, reipl_init}; + +/* + * dump shutdown action: Dump Linux on shutdown. + */ + +/* FCP dump device attributes */ + +DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n", + dump_block_fcp->ipl_info.fcp.wwpn); +DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n", + dump_block_fcp->ipl_info.fcp.lun); +DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n", + dump_block_fcp->ipl_info.fcp.bootprog); +DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n", + dump_block_fcp->ipl_info.fcp.br_lba); +DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", + dump_block_fcp->ipl_info.fcp.devno); + +static struct attribute *dump_fcp_attrs[] = { + &sys_dump_fcp_device_attr.attr, + &sys_dump_fcp_wwpn_attr.attr, + &sys_dump_fcp_lun_attr.attr, + &sys_dump_fcp_bootprog_attr.attr, + &sys_dump_fcp_br_lba_attr.attr, + NULL, +}; + +static struct attribute_group dump_fcp_attr_group = { + .name = IPL_FCP_STR, + .attrs = dump_fcp_attrs, +}; + +/* CCW dump device attributes */ + +DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", + dump_block_ccw->ipl_info.ccw.devno); + +static struct attribute *dump_ccw_attrs[] = { + &sys_dump_ccw_device_attr.attr, + NULL, +}; + +static struct attribute_group dump_ccw_attr_group = { + .name = IPL_CCW_STR, + .attrs = dump_ccw_attrs, +}; + +/* dump type */ + +static int dump_set_type(enum dump_type type) +{ + if (!(dump_capabilities & type)) + return -EINVAL; + switch (type) { + case DUMP_TYPE_CCW: + if (MACHINE_IS_VM) + dump_method = DUMP_METHOD_CCW_VM; + else + dump_method = DUMP_METHOD_CCW_CIO; + break; + case DUMP_TYPE_FCP: + dump_method = DUMP_METHOD_FCP_DIAG; + break; + default: + dump_method = DUMP_METHOD_NONE; + } + dump_type = type; + return 0; +} + +static ssize_t dump_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return sprintf(page, "%s\n", dump_type_str(dump_type)); +} + +static ssize_t dump_type_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int rc = -EINVAL; + + if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_NONE); + else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_CCW); + else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) + rc = dump_set_type(DUMP_TYPE_FCP); + return (rc != 0) ? rc : len; +} + +static struct kobj_attribute dump_type_attr = + __ATTR(dump_type, 0644, dump_type_show, dump_type_store); + +static struct kset *dump_kset; + +static void dump_run(struct shutdown_trigger *trigger) +{ + struct ccw_dev_id devid; + static char buf[100]; + + switch (dump_method) { + case DUMP_METHOD_CCW_CIO: + smp_send_stop(); + devid.devno = dump_block_ccw->ipl_info.ccw.devno; + devid.ssid = 0; + reipl_ccw_dev(&devid); + break; + case DUMP_METHOD_CCW_VM: + smp_send_stop(); + sprintf(buf, "STORE STATUS"); + __cpcmd(buf, NULL, 0, NULL); + sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); + __cpcmd(buf, NULL, 0, NULL); + break; + case DUMP_METHOD_CCW_DIAG: + diag308(DIAG308_SET, dump_block_ccw); + diag308(DIAG308_DUMP, NULL); + break; + case DUMP_METHOD_FCP_DIAG: + diag308(DIAG308_SET, dump_block_fcp); + diag308(DIAG308_DUMP, NULL); + break; + case DUMP_METHOD_NONE: + default: + return; + } + printk(KERN_EMERG "Dump failed!\n"); +} + static int __init dump_ccw_init(void) { int rc; @@ -1042,31 +996,14 @@ static int __init dump_fcp_init(void) return 0; } -#define SHUTDOWN_ON_PANIC_PRIO 0 - -static int shutdown_on_panic_notify(struct notifier_block *self, - unsigned long event, void *data) -{ - if (on_panic_action == SHUTDOWN_DUMP) - do_dump(); - else if (on_panic_action == SHUTDOWN_REIPL) - do_reipl(); - return NOTIFY_OK; -} - -static struct notifier_block shutdown_on_panic_nb = { - .notifier_call = shutdown_on_panic_notify, - .priority = SHUTDOWN_ON_PANIC_PRIO -}; - -static int __init dump_init(void) +static int dump_init(void) { int rc; dump_kset = kset_create_and_add("dump", NULL, firmware_kobj); if (!dump_kset) return -ENOMEM; - rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr); + rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr.attr); if (rc) { kset_unregister(dump_kset); return rc; @@ -1081,47 +1018,376 @@ static int __init dump_init(void) return 0; } -static int __init shutdown_actions_init(void) +static struct shutdown_action dump_action = {SHUTDOWN_ACTION_DUMP_STR, + dump_run, dump_init}; + +/* + * vmcmd shutdown action: Trigger vm command on shutdown. + */ + +static char vmcmd_on_reboot[128]; +static char vmcmd_on_panic[128]; +static char vmcmd_on_halt[128]; +static char vmcmd_on_poff[128]; + +DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot); +DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic); +DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt); +DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff); + +static struct attribute *vmcmd_attrs[] = { + &sys_vmcmd_on_reboot_attr.attr, + &sys_vmcmd_on_panic_attr.attr, + &sys_vmcmd_on_halt_attr.attr, + &sys_vmcmd_on_poff_attr.attr, + NULL, +}; + +static struct attribute_group vmcmd_attr_group = { + .attrs = vmcmd_attrs, +}; + +static struct kset *vmcmd_kset; + +static void vmcmd_run(struct shutdown_trigger *trigger) { - int rc; + char *cmd, *next_cmd; + + if (strcmp(trigger->name, ON_REIPL_STR) == 0) + cmd = vmcmd_on_reboot; + else if (strcmp(trigger->name, ON_PANIC_STR) == 0) + cmd = vmcmd_on_panic; + else if (strcmp(trigger->name, ON_HALT_STR) == 0) + cmd = vmcmd_on_halt; + else if (strcmp(trigger->name, ON_POFF_STR) == 0) + cmd = vmcmd_on_poff; + else + return; + + if (strlen(cmd) == 0) + return; + do { + next_cmd = strchr(cmd, '\n'); + if (next_cmd) { + next_cmd[0] = 0; + next_cmd += 1; + } + __cpcmd(cmd, NULL, 0, NULL); + cmd = next_cmd; + } while (cmd != NULL); +} + +static int vmcmd_init(void) +{ + if (!MACHINE_IS_VM) + return -ENOTSUPP; + vmcmd_kset = kset_create_and_add("vmcmd", NULL, firmware_kobj); + if (!vmcmd_kset) + return -ENOMEM; + return sysfs_create_group(&vmcmd_kset->kobj, &vmcmd_attr_group); +} + +static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR, + vmcmd_run, vmcmd_init}; + +/* + * stop shutdown action: Stop Linux on shutdown. + */ + +static void stop_run(struct shutdown_trigger *trigger) +{ + signal_processor(smp_processor_id(), sigp_stop_and_store_status); + for (;;); +} + +static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR, + stop_run, NULL}; + +/* action list */ + +static struct shutdown_action *shutdown_actions_list[] = { + &ipl_action, &reipl_action, &dump_action, &vmcmd_action, &stop_action}; +#define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *)) + +/* + * Trigger section + */ + +static struct kset *shutdown_actions_kset; + +static int set_trigger(const char *buf, struct shutdown_trigger *trigger, + size_t len) +{ + int i; + for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { + if (!shutdown_actions_list[i]) + continue; + if (strncmp(buf, shutdown_actions_list[i]->name, + strlen(shutdown_actions_list[i]->name)) == 0) { + trigger->action = shutdown_actions_list[i]; + return len; + } + } + return -EINVAL; +} + +/* on reipl */ + +static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR, + &reipl_action}; + +static ssize_t on_reboot_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return sprintf(page, "%s\n", on_reboot_trigger.action->name); +} + +static ssize_t on_reboot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return set_trigger(buf, &on_reboot_trigger, len); +} + +static struct kobj_attribute on_reboot_attr = + __ATTR(on_reboot, 0644, on_reboot_show, on_reboot_store); + +static void do_machine_restart(char *__unused) +{ + smp_send_stop(); + on_reboot_trigger.action->fn(&on_reboot_trigger); + reipl_run(NULL); +} +void (*_machine_restart)(char *command) = do_machine_restart; + +/* on panic */ + +static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action}; + +static ssize_t on_panic_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return sprintf(page, "%s\n", on_panic_trigger.action->name); +} + +static ssize_t on_panic_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return set_trigger(buf, &on_panic_trigger, len); +} + +static struct kobj_attribute on_panic_attr = + __ATTR(on_panic, 0644, on_panic_show, on_panic_store); + +static void do_panic(void) +{ + on_panic_trigger.action->fn(&on_panic_trigger); + stop_run(&on_panic_trigger); +} + +/* on halt */ + +static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action}; + +static ssize_t on_halt_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return sprintf(page, "%s\n", on_halt_trigger.action->name); +} + +static ssize_t on_halt_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return set_trigger(buf, &on_halt_trigger, len); +} + +static struct kobj_attribute on_halt_attr = + __ATTR(on_halt, 0644, on_halt_show, on_halt_store); + +static void do_machine_halt(void) +{ + smp_send_stop(); + on_halt_trigger.action->fn(&on_halt_trigger); + stop_run(&on_halt_trigger); +} +void (*_machine_halt)(void) = do_machine_halt; + +/* on power off */ + +static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action}; + +static ssize_t on_poff_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return sprintf(page, "%s\n", on_poff_trigger.action->name); +} + +static ssize_t on_poff_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return set_trigger(buf, &on_poff_trigger, len); +} + +static struct kobj_attribute on_poff_attr = + __ATTR(on_poff, 0644, on_poff_show, on_poff_store); + + +static void do_machine_power_off(void) +{ + smp_send_stop(); + on_poff_trigger.action->fn(&on_poff_trigger); + stop_run(&on_poff_trigger); +} +void (*_machine_power_off)(void) = do_machine_power_off; + +static void __init shutdown_triggers_init(void) +{ shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL, firmware_kobj); if (!shutdown_actions_kset) - return -ENOMEM; - rc = sysfs_create_file(&shutdown_actions_kset->kobj, &on_panic_attr); - if (rc) { - kset_unregister(shutdown_actions_kset); - return rc; + goto fail; + if (sysfs_create_file(&shutdown_actions_kset->kobj, + &on_reboot_attr.attr)) + goto fail; + if (sysfs_create_file(&shutdown_actions_kset->kobj, + &on_panic_attr.attr)) + goto fail; + if (sysfs_create_file(&shutdown_actions_kset->kobj, + &on_halt_attr.attr)) + goto fail; + if (sysfs_create_file(&shutdown_actions_kset->kobj, + &on_poff_attr.attr)) + goto fail; + + return; +fail: + panic("shutdown_triggers_init failed\n"); +} + +static void __init shutdown_actions_init(void) +{ + int i; + + for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { + if (!shutdown_actions_list[i]->init) + continue; + if (shutdown_actions_list[i]->init()) + shutdown_actions_list[i] = NULL; } - atomic_notifier_chain_register(&panic_notifier_list, - &shutdown_on_panic_nb); - return 0; } static int __init s390_ipl_init(void) { - int rc; - - sclp_get_ipl_info(&sclp_ipl_info); reipl_probe(); - rc = ipl_init(); - if (rc) - return rc; - rc = reipl_init(); - if (rc) - return rc; - rc = dump_init(); - if (rc) - return rc; - rc = shutdown_actions_init(); - if (rc) - return rc; + shutdown_actions_init(); + shutdown_triggers_init(); return 0; } __initcall(s390_ipl_init); +static void __init strncpy_skip_quote(char *dst, char *src, int n) +{ + int sx, dx; + + dx = 0; + for (sx = 0; src[sx] != 0; sx++) { + if (src[sx] == '"') + continue; + dst[dx++] = src[sx]; + if (dx >= n) + break; + } +} + +static int __init vmcmd_on_reboot_setup(char *str) +{ + if (!MACHINE_IS_VM) + return 1; + strncpy_skip_quote(vmcmd_on_reboot, str, 127); + vmcmd_on_reboot[127] = 0; + on_reboot_trigger.action = &vmcmd_action; + return 1; +} +__setup("vmreboot=", vmcmd_on_reboot_setup); + +static int __init vmcmd_on_panic_setup(char *str) +{ + if (!MACHINE_IS_VM) + return 1; + strncpy_skip_quote(vmcmd_on_panic, str, 127); + vmcmd_on_panic[127] = 0; + on_panic_trigger.action = &vmcmd_action; + return 1; +} +__setup("vmpanic=", vmcmd_on_panic_setup); + +static int __init vmcmd_on_halt_setup(char *str) +{ + if (!MACHINE_IS_VM) + return 1; + strncpy_skip_quote(vmcmd_on_halt, str, 127); + vmcmd_on_halt[127] = 0; + on_halt_trigger.action = &vmcmd_action; + return 1; +} +__setup("vmhalt=", vmcmd_on_halt_setup); + +static int __init vmcmd_on_poff_setup(char *str) +{ + if (!MACHINE_IS_VM) + return 1; + strncpy_skip_quote(vmcmd_on_poff, str, 127); + vmcmd_on_poff[127] = 0; + on_poff_trigger.action = &vmcmd_action; + return 1; +} +__setup("vmpoff=", vmcmd_on_poff_setup); + +static int on_panic_notify(struct notifier_block *self, + unsigned long event, void *data) +{ + do_panic(); + return NOTIFY_OK; +} + +static struct notifier_block on_panic_nb = { + .notifier_call = on_panic_notify, + .priority = 0, +}; + +void __init setup_ipl(void) +{ + ipl_info.type = get_ipl_type(); + switch (ipl_info.type) { + case IPL_TYPE_CCW: + ipl_info.data.ccw.dev_id.devno = ipl_devno; + ipl_info.data.ccw.dev_id.ssid = 0; + break; + case IPL_TYPE_FCP: + case IPL_TYPE_FCP_DUMP: + ipl_info.data.fcp.dev_id.devno = + IPL_PARMBLOCK_START->ipl_info.fcp.devno; + ipl_info.data.fcp.dev_id.ssid = 0; + ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn; + ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun; + break; + case IPL_TYPE_NSS: + strncpy(ipl_info.data.nss.name, kernel_nss_name, + sizeof(ipl_info.data.nss.name)); + break; + case IPL_TYPE_UNKNOWN: + default: + /* We have no info to copy */ + break; + } + atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); +} + void __init ipl_save_parameters(void) { struct cio_iplinfo iplinfo; @@ -1202,3 +1468,4 @@ void s390_reset_system(void) do_reset_calls(); } + diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index cbdf3fb..fcebf64 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -126,75 +126,6 @@ void __cpuinit cpu_init(void) } /* - * VM halt and poweroff setup routines - */ -char vmhalt_cmd[128] = ""; -char vmpoff_cmd[128] = ""; -static char vmpanic_cmd[128] = ""; - -static void strncpy_skip_quote(char *dst, char *src, int n) -{ - int sx, dx; - - dx = 0; - for (sx = 0; src[sx] != 0; sx++) { - if (src[sx] == '"') continue; - dst[dx++] = src[sx]; - if (dx >= n) break; - } -} - -static int __init vmhalt_setup(char *str) -{ - strncpy_skip_quote(vmhalt_cmd, str, 127); - vmhalt_cmd[127] = 0; - return 1; -} - -__setup("vmhalt=", vmhalt_setup); - -static int __init vmpoff_setup(char *str) -{ - strncpy_skip_quote(vmpoff_cmd, str, 127); - vmpoff_cmd[127] = 0; - return 1; -} - -__setup("vmpoff=", vmpoff_setup); - -static int vmpanic_notify(struct notifier_block *self, unsigned long event, - void *data) -{ - if (MACHINE_IS_VM && strlen(vmpanic_cmd) > 0) - cpcmd(vmpanic_cmd, NULL, 0, NULL); - - return NOTIFY_OK; -} - -#define PANIC_PRI_VMPANIC 0 - -static struct notifier_block vmpanic_nb = { - .notifier_call = vmpanic_notify, - .priority = PANIC_PRI_VMPANIC -}; - -static int __init vmpanic_setup(char *str) -{ - static int register_done __initdata = 0; - - strncpy_skip_quote(vmpanic_cmd, str, 127); - vmpanic_cmd[127] = 0; - if (!register_done) { - register_done = 1; - atomic_notifier_chain_register(&panic_notifier_list, - &vmpanic_nb); - } - return 1; -} - -__setup("vmpanic=", vmpanic_setup); - -/* * condev= and conmode= setup parameter. */ @@ -308,38 +239,6 @@ static void __init setup_zfcpdump(unsigned int console_devno) static inline void setup_zfcpdump(unsigned int console_devno) {} #endif /* CONFIG_ZFCPDUMP */ -#ifdef CONFIG_SMP -void (*_machine_restart)(char *command) = machine_restart_smp; -void (*_machine_halt)(void) = machine_halt_smp; -void (*_machine_power_off)(void) = machine_power_off_smp; -#else -/* - * Reboot, halt and power_off routines for non SMP. - */ -static void do_machine_restart_nonsmp(char * __unused) -{ - do_reipl(); -} - -static void do_machine_halt_nonsmp(void) -{ - if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) - __cpcmd(vmhalt_cmd, NULL, 0, NULL); - signal_processor(smp_processor_id(), sigp_stop_and_store_status); -} - -static void do_machine_power_off_nonsmp(void) -{ - if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) - __cpcmd(vmpoff_cmd, NULL, 0, NULL); - signal_processor(smp_processor_id(), sigp_stop_and_store_status); -} - -void (*_machine_restart)(char *command) = do_machine_restart_nonsmp; -void (*_machine_halt)(void) = do_machine_halt_nonsmp; -void (*_machine_power_off)(void) = do_machine_power_off_nonsmp; -#endif - /* * Reboot, halt and power_off stubs. They just call _machine_restart, * _machine_halt or _machine_power_off. @@ -913,7 +812,7 @@ setup_arch(char **cmdline_p) parse_early_param(); - setup_ipl_info(); + setup_ipl(); setup_memory_end(); setup_addressing_mode(); setup_memory(); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 320e4e9..d300a7f 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -234,33 +234,6 @@ void smp_send_stop(void) } /* - * Reboot, halt and power_off routines for SMP. - */ -void machine_restart_smp(char *__unused) -{ - smp_send_stop(); - do_reipl(); -} - -void machine_halt_smp(void) -{ - smp_send_stop(); - if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) - __cpcmd(vmhalt_cmd, NULL, 0, NULL); - signal_processor(smp_processor_id(), sigp_stop_and_store_status); - for (;;); -} - -void machine_power_off_smp(void) -{ - smp_send_stop(); - if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) - __cpcmd(vmpoff_cmd, NULL, 0, NULL); - signal_processor(smp_processor_id(), sigp_stop_and_store_status); - for (;;); -} - -/* * This is the main routine where commands issued by other * cpus are handled. */ diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h index 2c40fd3..d0dcc9c 100644 --- a/include/asm-s390/ipl.h +++ b/include/asm-s390/ipl.h @@ -83,6 +83,8 @@ extern u32 dump_prefix_page; extern unsigned int zfcpdump_prefix_array[]; extern void do_reipl(void); +extern void do_halt(void); +extern void do_poff(void); extern void ipl_save_parameters(void); enum { @@ -118,7 +120,7 @@ struct ipl_info }; extern struct ipl_info ipl_info; -extern void setup_ipl_info(void); +extern void setup_ipl(void); /* * DIAG 308 support -- cgit v0.10.2 From c654749777e8624187b53fbb94cea91a7bf74347 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Sat, 26 Jan 2008 14:11:12 +0100 Subject: [S390] Load disabled wait psw instead of stopping cpu on halt. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index d73aff6..e3b9c6c 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1096,8 +1096,12 @@ static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR, static void stop_run(struct shutdown_trigger *trigger) { - signal_processor(smp_processor_id(), sigp_stop_and_store_status); - for (;;); + if (strcmp(trigger->name, ON_PANIC_STR) == 0) + disabled_wait((unsigned long) __builtin_return_address(0)); + else { + signal_processor(smp_processor_id(), sigp_stop); + for (;;); + } } static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR, -- cgit v0.10.2 From c11ca97ee9d2ed593ab7b5523def7787b46f398f Mon Sep 17 00:00:00 2001 From: Denis Cheng Date: Sat, 26 Jan 2008 14:11:13 +0100 Subject: [S390] use LIST_HEAD instead of LIST_HEAD_INIT single list_head variable initialized with LIST_HEAD_INIT could almost always can be replaced with LIST_HEAD declaration, this shrinks the code and looks better. Signed-off-by: Denis Cheng Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c index 394980b..880b0eb 100644 --- a/arch/s390/mm/extmem.c +++ b/arch/s390/mm/extmem.c @@ -83,7 +83,7 @@ struct dcss_segment { }; static DEFINE_MUTEX(dcss_lock); -static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); +static LIST_HEAD(dcss_list); static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", "EW/EN-MIXED" }; diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 15a5789..7779bfc 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -82,7 +82,7 @@ struct dcssblk_dev_info { struct request_queue *dcssblk_queue; }; -static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices); +static LIST_HEAD(dcssblk_devices); static struct rw_semaphore dcssblk_devices_sem; /* diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 8d1c64a..0d98f1f 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -66,7 +66,7 @@ struct raw3270 { static DEFINE_MUTEX(raw3270_mutex); /* List of 3270 devices. */ -static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices); +static LIST_HEAD(raw3270_devices); /* * Flag to indicate if the driver has been registered. Some operations @@ -1210,7 +1210,7 @@ struct raw3270_notifier { void (*notifier)(int, int); }; -static struct list_head raw3270_notifier = LIST_HEAD_INIT(raw3270_notifier); +static LIST_HEAD(raw3270_notifier); int raw3270_register_notifier(void (*notifier)(int, int)) { diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 2fae633..7ad8cf1 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -37,7 +37,7 @@ static void tape_long_busy_timeout(unsigned long data); * we can assign the devices to minor numbers of the same major * The list is protected by the rwlock */ -static struct list_head tape_device_list = LIST_HEAD_INIT(tape_device_list); +static LIST_HEAD(tape_device_list); static DEFINE_RWLOCK(tape_device_lock); /* diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index d6e93f1..f3d893c 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -198,8 +198,7 @@ struct iucv_connection { /** * Linked list of all connection structs. */ -static struct list_head iucv_connection_list = - LIST_HEAD_INIT(iucv_connection_list); +static LIST_HEAD(iucv_connection_list); static DEFINE_RWLOCK(iucv_connection_rwlock); /** diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 47bb47b..8735a41 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -42,7 +42,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver"); static struct iucv_path *smsg_path; static DEFINE_SPINLOCK(smsg_list_lock); -static struct list_head smsg_list = LIST_HEAD_INIT(smsg_list); +static LIST_HEAD(smsg_list); static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]); static void smsg_message_pending(struct iucv_path *, struct iucv_message *); -- cgit v0.10.2 From 1cb6bb4bbdfd7b6bbdd148c4a34c02066339806d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 26 Jan 2008 14:11:14 +0100 Subject: [S390] Allocate and free cpu lowcores and stacks when needed/possible. No need to preallocate the per cpu lowcores and stacks. Savings are 28-32k per offline cpu. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index d300a7f..040406d 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -589,8 +589,72 @@ static void __init smp_create_idle(unsigned int cpu) spin_lock_init(&(&per_cpu(s390_idle, cpu))->lock); } +static int __cpuinit smp_alloc_lowcore(int cpu) +{ + unsigned long async_stack, panic_stack; + struct _lowcore *lowcore; + int lc_order; + + lc_order = sizeof(long) == 8 ? 1 : 0; + lowcore = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, lc_order); + if (!lowcore) + return -ENOMEM; + async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER); + if (!async_stack) + goto out_async_stack; + panic_stack = __get_free_page(GFP_KERNEL); + if (!panic_stack) + goto out_panic_stack; + + *lowcore = S390_lowcore; + lowcore->async_stack = async_stack + ASYNC_SIZE; + lowcore->panic_stack = panic_stack + PAGE_SIZE; + +#ifndef CONFIG_64BIT + if (MACHINE_HAS_IEEE) { + unsigned long save_area; + + save_area = get_zeroed_page(GFP_KERNEL); + if (!save_area) + goto out_save_area; + lowcore->extended_save_area_addr = (u32) save_area; + } +#endif + lowcore_ptr[cpu] = lowcore; + return 0; + +#ifndef CONFIG_64BIT +out_save_area: + free_page(panic_stack); +#endif +out_panic_stack: + free_pages(async_stack, ASYNC_ORDER); +out_async_stack: + free_pages((unsigned long) lowcore, lc_order); + return -ENOMEM; +} + +#ifdef CONFIG_HOTPLUG_CPU +static void smp_free_lowcore(int cpu) +{ + struct _lowcore *lowcore; + int lc_order; + + lc_order = sizeof(long) == 8 ? 1 : 0; + lowcore = lowcore_ptr[cpu]; +#ifndef CONFIG_64BIT + if (MACHINE_HAS_IEEE) + free_page((unsigned long) lowcore->extended_save_area_addr); +#endif + free_page(lowcore->panic_stack - PAGE_SIZE); + free_pages(lowcore->async_stack - ASYNC_SIZE, ASYNC_ORDER); + free_pages((unsigned long) lowcore, lc_order); + lowcore_ptr[cpu] = NULL; +} +#endif /* CONFIG_HOTPLUG_CPU */ + /* Upping and downing of CPUs */ -int __cpu_up(unsigned int cpu) +int __cpuinit __cpu_up(unsigned int cpu) { struct task_struct *idle; struct _lowcore *cpu_lowcore; @@ -599,6 +663,8 @@ int __cpu_up(unsigned int cpu) if (smp_cpu_state[cpu] != CPU_STATE_CONFIGURED) return -EIO; + if (smp_alloc_lowcore(cpu)) + return -ENOMEM; ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]), cpu, sigp_set_prefix); @@ -613,6 +679,7 @@ int __cpu_up(unsigned int cpu) cpu_lowcore = lowcore_ptr[cpu]; cpu_lowcore->kernel_stack = (unsigned long) task_stack_page(idle) + THREAD_SIZE; + cpu_lowcore->thread_info = (unsigned long) task_thread_info(idle); sf = (struct stack_frame *) (cpu_lowcore->kernel_stack - sizeof(struct pt_regs) - sizeof(struct stack_frame)); @@ -626,6 +693,8 @@ int __cpu_up(unsigned int cpu) cpu_lowcore->percpu_offset = __per_cpu_offset[cpu]; cpu_lowcore->current_task = (unsigned long) idle; cpu_lowcore->cpu_data.cpu_nr = cpu; + cpu_lowcore->softirq_pending = 0; + cpu_lowcore->ext_call_fast = 0; eieio(); while (signal_processor(cpu, sigp_restart) == sigp_busy) @@ -686,6 +755,7 @@ void __cpu_die(unsigned int cpu) /* Wait until target cpu is down */ while (!smp_cpu_not_running(cpu)) cpu_relax(); + smp_free_lowcore(cpu); printk(KERN_INFO "Processor %d spun down\n", cpu); } @@ -699,15 +769,9 @@ void cpu_die(void) #endif /* CONFIG_HOTPLUG_CPU */ -/* - * Cycle through the processors and setup structures. - */ - void __init smp_prepare_cpus(unsigned int max_cpus) { - unsigned long stack; unsigned int cpu; - int i; smp_detect_cpus(); @@ -715,35 +779,9 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0) panic("Couldn't request external interrupt 0x1201"); memset(lowcore_ptr, 0, sizeof(lowcore_ptr)); - /* - * Initialize prefix pages and stacks for all possible cpus - */ print_cpu_info(&S390_lowcore.cpu_data); + smp_alloc_lowcore(smp_processor_id()); - for_each_possible_cpu(i) { - lowcore_ptr[i] = (struct _lowcore *) - __get_free_pages(GFP_KERNEL | GFP_DMA, - sizeof(void*) == 8 ? 1 : 0); - stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER); - if (!lowcore_ptr[i] || !stack) - panic("smp_boot_cpus failed to allocate memory\n"); - - *(lowcore_ptr[i]) = S390_lowcore; - lowcore_ptr[i]->async_stack = stack + ASYNC_SIZE; - stack = __get_free_pages(GFP_KERNEL, 0); - if (!stack) - panic("smp_boot_cpus failed to allocate memory\n"); - lowcore_ptr[i]->panic_stack = stack + PAGE_SIZE; -#ifndef CONFIG_64BIT - if (MACHINE_HAS_IEEE) { - lowcore_ptr[i]->extended_save_area_addr = - (__u32) __get_free_pages(GFP_KERNEL, 0); - if (!lowcore_ptr[i]->extended_save_area_addr) - panic("smp_boot_cpus failed to " - "allocate memory\n"); - } -#endif - } #ifndef CONFIG_64BIT if (MACHINE_HAS_IEEE) ctl_set_bit(14, 29); /* enable extended save area */ -- cgit v0.10.2 From d09293aee9fd0f9f291fa3a0e4ca5012f514d083 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Sat, 26 Jan 2008 14:11:15 +0100 Subject: [S390] Initialize sclp_ipl_info The sclp ipl information has not been initialized. Therefore the ipl loadparm and the "has_dump" flag have not been set correctly. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index e3b9c6c..14bdde9 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1287,6 +1287,7 @@ static void __init shutdown_actions_init(void) static int __init s390_ipl_init(void) { reipl_probe(); + sclp_get_ipl_info(&sclp_ipl_info); shutdown_actions_init(); shutdown_triggers_init(); return 0; -- cgit v0.10.2 From a2fd64d6aaf498756f700eb1d07818efee046733 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Sat, 26 Jan 2008 14:11:16 +0100 Subject: [S390] vmemmap: allocate struct pages before 1:1 mapping We have seen an oops in an OOM situation, where show_mem tried to access the struct page of a dcss segment. The vmemmap code has already created the 1:1 mapping but failed allocating the struct pages. In the OOM case, show_mem now walks the memory. It uses pfn_valid to detect if it may access the struct page. In the case described above, the mapping was established and pfn_valid returned true. As the struct pages were not allocated, the kernel oopsed. We have to ensure that we have created the struct pages, before we add a mapping pointing to the pages. Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 1fc9989..79d13a1 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -236,10 +236,10 @@ static int vmem_add_mem(unsigned long start, unsigned long size) { int ret; - ret = vmem_add_range(start, size); + ret = vmem_add_mem_map(start, size); if (ret) return ret; - return vmem_add_mem_map(start, size); + return vmem_add_range(start, size); } /* -- cgit v0.10.2 From 48657d223d403af676696d313b421368f5e2208a Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Sat, 26 Jan 2008 14:11:17 +0100 Subject: [S390] Use diag308 subcodes 3 and 6 for reboot and dump when possible. This patch fixes a problem with the following scenario: 1. Linux booted from DASD "A" 2. Reboot from DASD "B" using "/sys/firmware/reipl/ccw/device" 3. Reboot DASD "B" Without this patch in step 3 on newer s390 systems under LPAR instead of DASD "B", DASD "A" will be booted. The reason is that in step 2 we use CCW reipl and in step 3 we use DIAG308 (subcode 3) reipl. DIAG308 does not notice the CCW reipl and still thinks that it has to reboot DASD "A". Before applying this fix, ensure to have MCF RJ9967101E or z9 GA3 base driver installed. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 14bdde9..db28cca 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -595,7 +595,9 @@ static int reipl_set_type(enum ipl_type type) switch(type) { case IPL_TYPE_CCW: - if (MACHINE_IS_VM) + if (diag308_set_works) + reipl_method = REIPL_METHOD_CCW_DIAG; + else if (MACHINE_IS_VM) reipl_method = REIPL_METHOD_CCW_VM; else reipl_method = REIPL_METHOD_CCW_CIO; @@ -659,8 +661,6 @@ void reipl_run(struct shutdown_trigger *trigger) switch (reipl_method) { case REIPL_METHOD_CCW_CIO: devid.devno = reipl_block_ccw->ipl_info.ccw.devno; - if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno) - diag308(DIAG308_IPL, NULL); devid.ssid = 0; reipl_ccw_dev(&devid); break; @@ -745,6 +745,7 @@ static int __init reipl_ccw_init(void) reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; + reipl_block_ccw->hdr.flags = DIAG308_FLAGS_LP_VALID; /* check if read scp info worked and set loadparm */ if (sclp_ipl_info.is_valid) memcpy(reipl_block_ccw->ipl_info.ccw.load_param, @@ -753,8 +754,7 @@ static int __init reipl_ccw_init(void) /* read scp info failed: set empty loadparm (EBCDIC blanks) */ memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, LOADPARM_LEN); - /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ - if (!MACHINE_IS_VM) + if (!MACHINE_IS_VM && !diag308_set_works) sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; if (ipl_info.type == IPL_TYPE_CCW) reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; @@ -876,7 +876,9 @@ static int dump_set_type(enum dump_type type) return -EINVAL; switch (type) { case DUMP_TYPE_CCW: - if (MACHINE_IS_VM) + if (diag308_set_works) + dump_method = DUMP_METHOD_CCW_DIAG; + else if (MACHINE_IS_VM) dump_method = DUMP_METHOD_CCW_VM; else dump_method = DUMP_METHOD_CCW_CIO; diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h index d0dcc9c..c1b2e50 100644 --- a/include/asm-s390/ipl.h +++ b/include/asm-s390/ipl.h @@ -143,6 +143,10 @@ enum diag308_opt { DIAG308_IPL_OPT_DUMP = 0x20, }; +enum diag308_flags { + DIAG308_FLAGS_LP_VALID = 0x80, +}; + enum diag308_rc { DIAG308_RC_OK = 1, }; -- cgit v0.10.2 From c2e3bbac72d5a954bf60c5fabeb7c82ec310035e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 26 Jan 2008 14:11:18 +0100 Subject: [S390] arch/s390/: Spelling fixes Signed-off-by: Joe Perches Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c index 8eb3a1a..23ed094 100644 --- a/arch/s390/crypto/prng.c +++ b/arch/s390/crypto/prng.c @@ -90,7 +90,7 @@ static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes, int ret = 0; int tmp; - /* nbytes can be arbitrary long, we spilt it into chunks */ + /* nbytes can be arbitrary length, we split it into chunks */ while (nbytes) { /* same as in extract_entropy_user in random.c */ if (need_resched()) { -- cgit v0.10.2 From 5800266a7811afeba205e771dcad79023331011b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 26 Jan 2008 14:11:19 +0100 Subject: [S390] include/asm-s390/: Spelling fixes Signed-off-by: Joe Perches Signed-off-by: Martin Schwidefsky diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h index 2f08c16..123b557 100644 --- a/include/asm-s390/cio.h +++ b/include/asm-s390/cio.h @@ -24,8 +24,8 @@ * @fmt: format * @pfch: prefetch * @isic: initial-status interruption control - * @alcc: adress-limit checking control - * @ssi: supress-suspended interruption + * @alcc: address-limit checking control + * @ssi: suppress-suspended interruption * @zcc: zero condition code * @ectl: extended control * @pno: path not operational diff --git a/include/asm-s390/dasd.h b/include/asm-s390/dasd.h index 604f68f..3f002e1 100644 --- a/include/asm-s390/dasd.h +++ b/include/asm-s390/dasd.h @@ -105,7 +105,7 @@ typedef struct dasd_information_t { } dasd_information_t; /* - * Read Subsystem Data - Perfomance Statistics + * Read Subsystem Data - Performance Statistics */ typedef struct dasd_rssd_perf_stats_t { unsigned char invalid:1; diff --git a/include/asm-s390/qdio.h b/include/asm-s390/qdio.h index 74db1dc..4b8ff55 100644 --- a/include/asm-s390/qdio.h +++ b/include/asm-s390/qdio.h @@ -184,7 +184,7 @@ struct qdr { #endif /* QDIO_32_BIT */ unsigned long qiba; /* queue-information-block address */ unsigned int res8; /* reserved */ - unsigned int qkey : 4; /* queue-informatio-block key */ + unsigned int qkey : 4; /* queue-information-block key */ unsigned int res9 : 28; /* reserved */ /* union _qd {*/ /* why this? */ struct qdesfmt0 qdf0[126]; diff --git a/include/asm-s390/zcrypt.h b/include/asm-s390/zcrypt.h index a5dada6..f228f1b 100644 --- a/include/asm-s390/zcrypt.h +++ b/include/asm-s390/zcrypt.h @@ -117,7 +117,7 @@ struct CPRBX { unsigned char padx004[16 - sizeof (char *)]; unsigned char * req_extb; /* request extension block 'addr'*/ unsigned char padx005[16 - sizeof (char *)]; - unsigned char * rpl_extb; /* reply extension block 'addres'*/ + unsigned char * rpl_extb; /* reply extension block 'address'*/ unsigned short ccp_rtcode; /* server return code */ unsigned short ccp_rscode; /* server reason code */ unsigned int mac_data_len; /* Mac Data Length */ -- cgit v0.10.2 From 5d67d164e6e2e7310cf4b682c418d70d59295eaf Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 26 Jan 2008 14:11:20 +0100 Subject: [S390] drivers/s390/: Spelling fixes Signed-off-by: Joe Perches Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 5b7385e..c02f960 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -2620,7 +2620,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, * DASD_3990_ERP_ACTION * * DESCRIPTION - * controll routine for 3990 erp actions. + * control routine for 3990 erp actions. * Has to be called with the queue lock (namely the s390_irq_lock) acquired. * * PARAMETER diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 44adf84..6038d91 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1542,7 +1542,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) prssdp = (struct dasd_psf_prssd_data *) cqr->data; memset(prssdp, 0, sizeof (struct dasd_psf_prssd_data)); prssdp->order = PSF_ORDER_PRSSD; - prssdp->suborder = 0x01; /* Perfomance Statistics */ + prssdp->suborder = 0x01; /* Performance Statistics */ prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */ ccw = cqr->cpaddr; diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index d6b06ab..ad7195d 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -76,7 +76,7 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab) } /* - * Return a pointer to the orignal page that has been used to create + * Return a pointer to the original page that has been used to create * the buffer. */ void * diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index da25f8e..8246ef3 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -1495,7 +1495,7 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request, device->cdev->dev.bus_id); return tape_3590_erp_basic(device, request, irb, -EPERM); case 0x8013: - PRINT_WARN("(%s): Another host has priviliged access to the " + PRINT_WARN("(%s): Another host has privileged access to the " "tape device\n", device->cdev->dev.bus_id); PRINT_WARN("(%s): To solve the problem unload the current " "cartridge!\n", device->cdev->dev.bus_id); diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 4f86c0e..2dc8110 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1286,7 +1286,7 @@ zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action) * note: no lock in subsequent strategy routines * (this allows these routine to call schedule, e.g. * kmalloc with such flags or qdio_initialize & friends) - * Note: in case of timeout, the seperate strategies will fail + * Note: in case of timeout, the separate strategies will fail * anyhow. No need for a special action. Even worse, a nameserver * failure would not wake up waiting ports without the call. */ diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 51d92b1..22fdc17 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -529,7 +529,7 @@ zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req) /** - * zfcp_qdio_sbale_fill - set address and lenght in current SBALE + * zfcp_qdio_sbale_fill - set address and length in current SBALE * on request_queue */ static void -- cgit v0.10.2 From ea29ee16e6ea908c830e70516d440828fd9129a8 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Sat, 26 Jan 2008 14:11:21 +0100 Subject: [S390] Move NOTES and BUG_TABLE. Move the NOTES and BUG_TABLE section in the linker script to the read-only sections right after the text section. Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 849120e..9361591 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -17,6 +17,12 @@ ENTRY(_start) jiffies = jiffies_64; #endif +PHDRS { + text PT_LOAD FLAGS(5); /* R_E */ + data PT_LOAD FLAGS(7); /* RWE */ + note PT_NOTE FLAGS(0); /* ___ */ +} + SECTIONS { . = 0x00000000; @@ -33,6 +39,9 @@ SECTIONS _etext = .; /* End of text section */ + NOTES :text :note + BUG_TABLE :text + RODATA #ifdef CONFIG_SHARED_KERNEL @@ -49,9 +58,6 @@ SECTIONS __stop___ex_table = .; } - NOTES - BUG_TABLE - .data : { /* Data */ DATA_DATA CONSTRUCTORS -- cgit v0.10.2 From 0ac30be461084f30ad6e22c6b91347e880ed41aa Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 26 Jan 2008 14:11:22 +0100 Subject: [S390] single-step cleanup Signed-off-by: Roland McGrath Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 1d81bf9..6e036ba 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -86,13 +86,13 @@ FixPerRegisters(struct task_struct *task) per_info->control_regs.bits.storage_alt_space_ctl = 0; } -static void set_single_step(struct task_struct *task) +void user_enable_single_step(struct task_struct *task) { task->thread.per_info.single_step = 1; FixPerRegisters(task); } -static void clear_single_step(struct task_struct *task) +void user_disable_single_step(struct task_struct *task) { task->thread.per_info.single_step = 0; FixPerRegisters(task); @@ -107,7 +107,7 @@ void ptrace_disable(struct task_struct *child) { /* make sure the single step bit is not set. */ - clear_single_step(child); + user_disable_single_step(child); } #ifndef CONFIG_64BIT @@ -651,7 +651,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data) clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; /* make sure the single step bit is not set. */ - clear_single_step(child); + user_disable_single_step(child); wake_up_process(child); return 0; @@ -665,7 +665,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data) return 0; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ - clear_single_step(child); + user_disable_single_step(child); wake_up_process(child); return 0; @@ -675,10 +675,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data) return -EIO; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; - if (data) - set_tsk_thread_flag(child, TIF_SINGLE_STEP); - else - set_single_step(child); + user_enable_single_step(child); /* give it a chance to run. */ wake_up_process(child); return 0; diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index d264671..4449bf3 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -471,6 +471,7 @@ void do_signal(struct pt_regs *regs) if (signr > 0) { /* Whee! Actually deliver the signal. */ + int ret; #ifdef CONFIG_COMPAT if (test_thread_flag(TIF_31BIT)) { extern int handle_signal32(unsigned long sig, @@ -478,15 +479,12 @@ void do_signal(struct pt_regs *regs) siginfo_t *info, sigset_t *oldset, struct pt_regs *regs); - if (handle_signal32( - signr, &ka, &info, oldset, regs) == 0) { - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - clear_thread_flag(TIF_RESTORE_SIGMASK); - } - return; + ret = handle_signal32(signr, &ka, &info, oldset, regs); } + else #endif - if (handle_signal(signr, &ka, &info, oldset, regs) == 0) { + ret = handle_signal(signr, &ka, &info, oldset, regs); + if (!ret) { /* * A signal was successfully delivered; the saved * sigmask will have been stored in the signal frame, @@ -495,6 +493,14 @@ void do_signal(struct pt_regs *regs) */ if (test_thread_flag(TIF_RESTORE_SIGMASK)) clear_thread_flag(TIF_RESTORE_SIGMASK); + + /* + * If we would have taken a single-step trap + * for a normal instruction, act like we took + * one for the handler setup. + */ + if (current->thread.per_info.single_step) + set_thread_flag(TIF_SINGLE_STEP); } return; } diff --git a/include/asm-s390/ptrace.h b/include/asm-s390/ptrace.h index 332ee73..61f6952 100644 --- a/include/asm-s390/ptrace.h +++ b/include/asm-s390/ptrace.h @@ -465,6 +465,14 @@ struct user_regs_struct #ifdef __KERNEL__ #define __ARCH_SYS_PTRACE 1 +/* + * These are defined as per linux/ptrace.h, which see. + */ +#define arch_has_single_step() (1) +struct task_struct; +extern void user_enable_single_step(struct task_struct *); +extern void user_disable_single_step(struct task_struct *); + #define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0) #define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN) #define regs_return_value(regs)((regs)->gprs[2]) -- cgit v0.10.2 From 8e09f21574ea3028d5629e5de759e0b196c690c5 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Sat, 26 Jan 2008 14:11:23 +0100 Subject: [S390] dasd: add hyper PAV support to DASD device driver, part 1 Parallel access volumes (PAV) is a storage server feature, that allows to start multiple channel programs on the same DASD in parallel. It defines alias devices which can be used as alternative paths to the same disk. With the old base PAV support we only needed rudimentary functionality in the DASD device driver. As the mapping between base and alias devices was static, we just had to export an identifier (uid) and could leave the combining of devices to external layers like a device mapper multipath. Now hyper PAV removes the requirement to dedicate alias devices to specific base devices. Instead each alias devices can be combined with multiple base device on a per request basis. This requires full support by the DASD device driver as now each channel program itself has to identify the target base device. The changes to the dasd device driver and the ECKD discipline are: - Separate subchannel device representation (dasd_device) from block device representation (dasd_block). Only base devices are block devices. - Gather information about base and alias devices and possible combinations. - For each request decide which dasd_device should be used (base or alias) and build specific channel program. - Support summary unit checks, which allow the storage server to upgrade / downgrade between base and hyper PAV at runtime (support is mandatory). Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile index be9f22d..0a89e08 100644 --- a/drivers/s390/block/Makefile +++ b/drivers/s390/block/Makefile @@ -2,8 +2,8 @@ # S/390 block devices # -dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o -dasd_fba_mod-objs := dasd_fba.o dasd_3370_erp.o dasd_9336_erp.o +dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o +dasd_fba_mod-objs := dasd_fba.o dasd_diag_mod-objs := dasd_diag.o dasd_mod-objs := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \ dasd_genhd.o dasd_erp.o diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index da4fe1e..db9193d 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -48,13 +48,15 @@ MODULE_LICENSE("GPL"); /* * SECTION: prototypes for static functions of dasd.c */ -static int dasd_alloc_queue(struct dasd_device * device); -static void dasd_setup_queue(struct dasd_device * device); -static void dasd_free_queue(struct dasd_device * device); -static void dasd_flush_request_queue(struct dasd_device *); -static int dasd_flush_ccw_queue(struct dasd_device *, int); -static void dasd_tasklet(struct dasd_device *); +static int dasd_alloc_queue(struct dasd_block *); +static void dasd_setup_queue(struct dasd_block *); +static void dasd_free_queue(struct dasd_block *); +static void dasd_flush_request_queue(struct dasd_block *); +static int dasd_flush_block_queue(struct dasd_block *); +static void dasd_device_tasklet(struct dasd_device *); +static void dasd_block_tasklet(struct dasd_block *); static void do_kick_device(struct work_struct *); +static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); /* * SECTION: Operations on the device structure. @@ -65,26 +67,23 @@ static wait_queue_head_t dasd_flush_wq; /* * Allocate memory for a new device structure. */ -struct dasd_device * -dasd_alloc_device(void) +struct dasd_device *dasd_alloc_device(void) { struct dasd_device *device; - device = kzalloc(sizeof (struct dasd_device), GFP_ATOMIC); - if (device == NULL) + device = kzalloc(sizeof(struct dasd_device), GFP_ATOMIC); + if (!device) return ERR_PTR(-ENOMEM); - /* open_count = 0 means device online but not in use */ - atomic_set(&device->open_count, -1); /* Get two pages for normal block device operations. */ device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1); - if (device->ccw_mem == NULL) { + if (!device->ccw_mem) { kfree(device); return ERR_PTR(-ENOMEM); } /* Get one page for error recovery. */ device->erp_mem = (void *) get_zeroed_page(GFP_ATOMIC | GFP_DMA); - if (device->erp_mem == NULL) { + if (!device->erp_mem) { free_pages((unsigned long) device->ccw_mem, 1); kfree(device); return ERR_PTR(-ENOMEM); @@ -93,10 +92,9 @@ dasd_alloc_device(void) dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2); dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE); spin_lock_init(&device->mem_lock); - spin_lock_init(&device->request_queue_lock); - atomic_set (&device->tasklet_scheduled, 0); + atomic_set(&device->tasklet_scheduled, 0); tasklet_init(&device->tasklet, - (void (*)(unsigned long)) dasd_tasklet, + (void (*)(unsigned long)) dasd_device_tasklet, (unsigned long) device); INIT_LIST_HEAD(&device->ccw_queue); init_timer(&device->timer); @@ -110,8 +108,7 @@ dasd_alloc_device(void) /* * Free memory of a device structure. */ -void -dasd_free_device(struct dasd_device *device) +void dasd_free_device(struct dasd_device *device) { kfree(device->private); free_page((unsigned long) device->erp_mem); @@ -120,10 +117,42 @@ dasd_free_device(struct dasd_device *device) } /* + * Allocate memory for a new device structure. + */ +struct dasd_block *dasd_alloc_block(void) +{ + struct dasd_block *block; + + block = kzalloc(sizeof(*block), GFP_ATOMIC); + if (!block) + return ERR_PTR(-ENOMEM); + /* open_count = 0 means device online but not in use */ + atomic_set(&block->open_count, -1); + + spin_lock_init(&block->request_queue_lock); + atomic_set(&block->tasklet_scheduled, 0); + tasklet_init(&block->tasklet, + (void (*)(unsigned long)) dasd_block_tasklet, + (unsigned long) block); + INIT_LIST_HEAD(&block->ccw_queue); + spin_lock_init(&block->queue_lock); + init_timer(&block->timer); + + return block; +} + +/* + * Free memory of a device structure. + */ +void dasd_free_block(struct dasd_block *block) +{ + kfree(block); +} + +/* * Make a new device known to the system. */ -static int -dasd_state_new_to_known(struct dasd_device *device) +static int dasd_state_new_to_known(struct dasd_device *device) { int rc; @@ -133,12 +162,13 @@ dasd_state_new_to_known(struct dasd_device *device) */ dasd_get_device(device); - rc = dasd_alloc_queue(device); - if (rc) { - dasd_put_device(device); - return rc; + if (device->block) { + rc = dasd_alloc_queue(device->block); + if (rc) { + dasd_put_device(device); + return rc; + } } - device->state = DASD_STATE_KNOWN; return 0; } @@ -146,21 +176,24 @@ dasd_state_new_to_known(struct dasd_device *device) /* * Let the system forget about a device. */ -static int -dasd_state_known_to_new(struct dasd_device * device) +static int dasd_state_known_to_new(struct dasd_device *device) { /* Disable extended error reporting for this device. */ dasd_eer_disable(device); /* Forget the discipline information. */ - if (device->discipline) + if (device->discipline) { + if (device->discipline->uncheck_device) + device->discipline->uncheck_device(device); module_put(device->discipline->owner); + } device->discipline = NULL; if (device->base_discipline) module_put(device->base_discipline->owner); device->base_discipline = NULL; device->state = DASD_STATE_NEW; - dasd_free_queue(device); + if (device->block) + dasd_free_queue(device->block); /* Give up reference we took in dasd_state_new_to_known. */ dasd_put_device(device); @@ -170,19 +203,19 @@ dasd_state_known_to_new(struct dasd_device * device) /* * Request the irq line for the device. */ -static int -dasd_state_known_to_basic(struct dasd_device * device) +static int dasd_state_known_to_basic(struct dasd_device *device) { int rc; /* Allocate and register gendisk structure. */ - rc = dasd_gendisk_alloc(device); - if (rc) - return rc; - + if (device->block) { + rc = dasd_gendisk_alloc(device->block); + if (rc) + return rc; + } /* register 'device' debug area, used for all DBF_DEV_XXX calls */ device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2, - 8 * sizeof (long)); + 8 * sizeof(long)); debug_register_view(device->debug_area, &debug_sprintf_view); debug_set_level(device->debug_area, DBF_WARNING); DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); @@ -194,16 +227,17 @@ dasd_state_known_to_basic(struct dasd_device * device) /* * Release the irq line for the device. Terminate any running i/o. */ -static int -dasd_state_basic_to_known(struct dasd_device * device) +static int dasd_state_basic_to_known(struct dasd_device *device) { int rc; - - dasd_gendisk_free(device); - rc = dasd_flush_ccw_queue(device, 1); + if (device->block) { + dasd_gendisk_free(device->block); + dasd_block_clear_timer(device->block); + } + rc = dasd_flush_device_queue(device); if (rc) return rc; - dasd_clear_timer(device); + dasd_device_clear_timer(device); DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device); if (device->debug_area != NULL) { @@ -228,26 +262,32 @@ dasd_state_basic_to_known(struct dasd_device * device) * In case the analysis returns an error, the device setup is stopped * (a fake disk was already added to allow formatting). */ -static int -dasd_state_basic_to_ready(struct dasd_device * device) +static int dasd_state_basic_to_ready(struct dasd_device *device) { int rc; + struct dasd_block *block; rc = 0; - if (device->discipline->do_analysis != NULL) - rc = device->discipline->do_analysis(device); - if (rc) { - if (rc != -EAGAIN) - device->state = DASD_STATE_UNFMT; - return rc; - } + block = device->block; /* make disk known with correct capacity */ - dasd_setup_queue(device); - set_capacity(device->gdp, device->blocks << device->s2b_shift); - device->state = DASD_STATE_READY; - rc = dasd_scan_partitions(device); - if (rc) - device->state = DASD_STATE_BASIC; + if (block) { + if (block->base->discipline->do_analysis != NULL) + rc = block->base->discipline->do_analysis(block); + if (rc) { + if (rc != -EAGAIN) + device->state = DASD_STATE_UNFMT; + return rc; + } + dasd_setup_queue(block); + set_capacity(block->gdp, + block->blocks << block->s2b_shift); + device->state = DASD_STATE_READY; + rc = dasd_scan_partitions(block); + if (rc) + device->state = DASD_STATE_BASIC; + } else { + device->state = DASD_STATE_READY; + } return rc; } @@ -256,28 +296,31 @@ dasd_state_basic_to_ready(struct dasd_device * device) * Forget format information. Check if the target level is basic * and if it is create fake disk for formatting. */ -static int -dasd_state_ready_to_basic(struct dasd_device * device) +static int dasd_state_ready_to_basic(struct dasd_device *device) { int rc; - rc = dasd_flush_ccw_queue(device, 0); - if (rc) - return rc; - dasd_destroy_partitions(device); - dasd_flush_request_queue(device); - device->blocks = 0; - device->bp_block = 0; - device->s2b_shift = 0; device->state = DASD_STATE_BASIC; + if (device->block) { + struct dasd_block *block = device->block; + rc = dasd_flush_block_queue(block); + if (rc) { + device->state = DASD_STATE_READY; + return rc; + } + dasd_destroy_partitions(block); + dasd_flush_request_queue(block); + block->blocks = 0; + block->bp_block = 0; + block->s2b_shift = 0; + } return 0; } /* * Back to basic. */ -static int -dasd_state_unfmt_to_basic(struct dasd_device * device) +static int dasd_state_unfmt_to_basic(struct dasd_device *device) { device->state = DASD_STATE_BASIC; return 0; @@ -291,17 +334,31 @@ dasd_state_unfmt_to_basic(struct dasd_device * device) static int dasd_state_ready_to_online(struct dasd_device * device) { + int rc; + + if (device->discipline->ready_to_online) { + rc = device->discipline->ready_to_online(device); + if (rc) + return rc; + } device->state = DASD_STATE_ONLINE; - dasd_schedule_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); return 0; } /* * Stop the requeueing of requests again. */ -static int -dasd_state_online_to_ready(struct dasd_device * device) +static int dasd_state_online_to_ready(struct dasd_device *device) { + int rc; + + if (device->discipline->online_to_ready) { + rc = device->discipline->online_to_ready(device); + if (rc) + return rc; + } device->state = DASD_STATE_READY; return 0; } @@ -309,8 +366,7 @@ dasd_state_online_to_ready(struct dasd_device * device) /* * Device startup state changes. */ -static int -dasd_increase_state(struct dasd_device *device) +static int dasd_increase_state(struct dasd_device *device) { int rc; @@ -345,8 +401,7 @@ dasd_increase_state(struct dasd_device *device) /* * Device shutdown state changes. */ -static int -dasd_decrease_state(struct dasd_device *device) +static int dasd_decrease_state(struct dasd_device *device) { int rc; @@ -381,8 +436,7 @@ dasd_decrease_state(struct dasd_device *device) /* * This is the main startup/shutdown routine. */ -static void -dasd_change_state(struct dasd_device *device) +static void dasd_change_state(struct dasd_device *device) { int rc; @@ -409,17 +463,15 @@ dasd_change_state(struct dasd_device *device) * dasd_kick_device will schedule a call do do_kick_device to the kernel * event daemon. */ -static void -do_kick_device(struct work_struct *work) +static void do_kick_device(struct work_struct *work) { struct dasd_device *device = container_of(work, struct dasd_device, kick_work); dasd_change_state(device); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); dasd_put_device(device); } -void -dasd_kick_device(struct dasd_device *device) +void dasd_kick_device(struct dasd_device *device) { dasd_get_device(device); /* queue call to dasd_kick_device to the kernel event daemon. */ @@ -429,8 +481,7 @@ dasd_kick_device(struct dasd_device *device) /* * Set the target state for a device and starts the state change. */ -void -dasd_set_target_state(struct dasd_device *device, int target) +void dasd_set_target_state(struct dasd_device *device, int target) { /* If we are in probeonly mode stop at DASD_STATE_READY. */ if (dasd_probeonly && target > DASD_STATE_READY) @@ -447,14 +498,12 @@ dasd_set_target_state(struct dasd_device *device, int target) /* * Enable devices with device numbers in [from..to]. */ -static inline int -_wait_for_device(struct dasd_device *device) +static inline int _wait_for_device(struct dasd_device *device) { return (device->state == device->target); } -void -dasd_enable_device(struct dasd_device *device) +void dasd_enable_device(struct dasd_device *device) { dasd_set_target_state(device, DASD_STATE_ONLINE); if (device->state <= DASD_STATE_KNOWN) @@ -475,20 +524,20 @@ unsigned int dasd_profile_level = DASD_PROFILE_OFF; /* * Increments counter in global and local profiling structures. */ -#define dasd_profile_counter(value, counter, device) \ +#define dasd_profile_counter(value, counter, block) \ { \ int index; \ for (index = 0; index < 31 && value >> (2+index); index++); \ dasd_global_profile.counter[index]++; \ - device->profile.counter[index]++; \ + block->profile.counter[index]++; \ } /* * Add profiling information for cqr before execution. */ -static void -dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr, - struct request *req) +static void dasd_profile_start(struct dasd_block *block, + struct dasd_ccw_req *cqr, + struct request *req) { struct list_head *l; unsigned int counter; @@ -498,19 +547,19 @@ dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr, /* count the length of the chanq for statistics */ counter = 0; - list_for_each(l, &device->ccw_queue) + list_for_each(l, &block->ccw_queue) if (++counter >= 31) break; dasd_global_profile.dasd_io_nr_req[counter]++; - device->profile.dasd_io_nr_req[counter]++; + block->profile.dasd_io_nr_req[counter]++; } /* * Add profiling information for cqr after execution. */ -static void -dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, - struct request *req) +static void dasd_profile_end(struct dasd_block *block, + struct dasd_ccw_req *cqr, + struct request *req) { long strtime, irqtime, endtime, tottime; /* in microseconds */ long tottimeps, sectors; @@ -532,27 +581,27 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, if (!dasd_global_profile.dasd_io_reqs) memset(&dasd_global_profile, 0, - sizeof (struct dasd_profile_info_t)); + sizeof(struct dasd_profile_info_t)); dasd_global_profile.dasd_io_reqs++; dasd_global_profile.dasd_io_sects += sectors; - if (!device->profile.dasd_io_reqs) - memset(&device->profile, 0, - sizeof (struct dasd_profile_info_t)); - device->profile.dasd_io_reqs++; - device->profile.dasd_io_sects += sectors; + if (!block->profile.dasd_io_reqs) + memset(&block->profile, 0, + sizeof(struct dasd_profile_info_t)); + block->profile.dasd_io_reqs++; + block->profile.dasd_io_sects += sectors; - dasd_profile_counter(sectors, dasd_io_secs, device); - dasd_profile_counter(tottime, dasd_io_times, device); - dasd_profile_counter(tottimeps, dasd_io_timps, device); - dasd_profile_counter(strtime, dasd_io_time1, device); - dasd_profile_counter(irqtime, dasd_io_time2, device); - dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, device); - dasd_profile_counter(endtime, dasd_io_time3, device); + dasd_profile_counter(sectors, dasd_io_secs, block); + dasd_profile_counter(tottime, dasd_io_times, block); + dasd_profile_counter(tottimeps, dasd_io_timps, block); + dasd_profile_counter(strtime, dasd_io_time1, block); + dasd_profile_counter(irqtime, dasd_io_time2, block); + dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, block); + dasd_profile_counter(endtime, dasd_io_time3, block); } #else -#define dasd_profile_start(device, cqr, req) do {} while (0) -#define dasd_profile_end(device, cqr, req) do {} while (0) +#define dasd_profile_start(block, cqr, req) do {} while (0) +#define dasd_profile_end(block, cqr, req) do {} while (0) #endif /* CONFIG_DASD_PROFILE */ /* @@ -562,9 +611,9 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, * memory and 2) dasd_smalloc_request uses the static ccw memory * that gets allocated for each device. */ -struct dasd_ccw_req * -dasd_kmalloc_request(char *magic, int cplength, int datasize, - struct dasd_device * device) +struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength, + int datasize, + struct dasd_device *device) { struct dasd_ccw_req *cqr; @@ -600,9 +649,9 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize, return cqr; } -struct dasd_ccw_req * -dasd_smalloc_request(char *magic, int cplength, int datasize, - struct dasd_device * device) +struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength, + int datasize, + struct dasd_device *device) { unsigned long flags; struct dasd_ccw_req *cqr; @@ -649,8 +698,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize, * idal lists that might have been created by dasd_set_cda and the * struct dasd_ccw_req itself. */ -void -dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) +void dasd_kfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) { #ifdef CONFIG_64BIT struct ccw1 *ccw; @@ -667,8 +715,7 @@ dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) dasd_put_device(device); } -void -dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) +void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) { unsigned long flags; @@ -681,14 +728,13 @@ dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) /* * Check discipline magic in cqr. */ -static inline int -dasd_check_cqr(struct dasd_ccw_req *cqr) +static inline int dasd_check_cqr(struct dasd_ccw_req *cqr) { struct dasd_device *device; if (cqr == NULL) return -EINVAL; - device = cqr->device; + device = cqr->startdev; if (strncmp((char *) &cqr->magic, device->discipline->ebcname, 4)) { DEV_MESSAGE(KERN_WARNING, device, " dasd_ccw_req 0x%08x magic doesn't match" @@ -706,8 +752,7 @@ dasd_check_cqr(struct dasd_ccw_req *cqr) * ccw_device_clear can fail if the i/o subsystem * is in a bad mood. */ -int -dasd_term_IO(struct dasd_ccw_req * cqr) +int dasd_term_IO(struct dasd_ccw_req *cqr) { struct dasd_device *device; int retries, rc; @@ -717,13 +762,13 @@ dasd_term_IO(struct dasd_ccw_req * cqr) if (rc) return rc; retries = 0; - device = (struct dasd_device *) cqr->device; + device = (struct dasd_device *) cqr->startdev; while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) { rc = ccw_device_clear(device->cdev, (long) cqr); switch (rc) { case 0: /* termination successful */ cqr->retries--; - cqr->status = DASD_CQR_CLEAR; + cqr->status = DASD_CQR_CLEAR_PENDING; cqr->stopclk = get_clock(); cqr->starttime = 0; DBF_DEV_EVENT(DBF_DEBUG, device, @@ -753,7 +798,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) } retries++; } - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); return rc; } @@ -761,8 +806,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) * Start the i/o. This start_IO can fail if the channel is really busy. * In that case set up a timer to start the request later. */ -int -dasd_start_IO(struct dasd_ccw_req * cqr) +int dasd_start_IO(struct dasd_ccw_req *cqr) { struct dasd_device *device; int rc; @@ -771,12 +815,12 @@ dasd_start_IO(struct dasd_ccw_req * cqr) rc = dasd_check_cqr(cqr); if (rc) return rc; - device = (struct dasd_device *) cqr->device; + device = (struct dasd_device *) cqr->startdev; if (cqr->retries < 0) { DEV_MESSAGE(KERN_DEBUG, device, "start_IO: request %p (%02x/%i) - no retry left.", cqr, cqr->status, cqr->retries); - cqr->status = DASD_CQR_FAILED; + cqr->status = DASD_CQR_ERROR; return -EIO; } cqr->startclk = get_clock(); @@ -833,8 +877,7 @@ dasd_start_IO(struct dasd_ccw_req * cqr) * The head of the ccw queue will have status DASD_CQR_IN_IO for 1), * DASD_CQR_QUEUED for 2) and 3). */ -static void -dasd_timeout_device(unsigned long ptr) +static void dasd_device_timeout(unsigned long ptr) { unsigned long flags; struct dasd_device *device; @@ -844,14 +887,13 @@ dasd_timeout_device(unsigned long ptr) /* re-activate request queue */ device->stopped &= ~DASD_STOPPED_PENDING; spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); } /* * Setup timeout for a device in jiffies. */ -void -dasd_set_timer(struct dasd_device *device, int expires) +void dasd_device_set_timer(struct dasd_device *device, int expires) { if (expires == 0) { if (timer_pending(&device->timer)) @@ -862,7 +904,7 @@ dasd_set_timer(struct dasd_device *device, int expires) if (mod_timer(&device->timer, jiffies + expires)) return; } - device->timer.function = dasd_timeout_device; + device->timer.function = dasd_device_timeout; device->timer.data = (unsigned long) device; device->timer.expires = jiffies + expires; add_timer(&device->timer); @@ -871,15 +913,14 @@ dasd_set_timer(struct dasd_device *device, int expires) /* * Clear timeout for a device. */ -void -dasd_clear_timer(struct dasd_device *device) +void dasd_device_clear_timer(struct dasd_device *device) { if (timer_pending(&device->timer)) del_timer(&device->timer); } -static void -dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) +static void dasd_handle_killed_request(struct ccw_device *cdev, + unsigned long intparm) { struct dasd_ccw_req *cqr; struct dasd_device *device; @@ -893,7 +934,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) return; } - device = (struct dasd_device *) cqr->device; + device = (struct dasd_device *) cqr->startdev; if (device == NULL || device != dasd_device_from_cdev_locked(cdev) || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { @@ -905,46 +946,32 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) /* Schedule request to be retried. */ cqr->status = DASD_CQR_QUEUED; - dasd_clear_timer(device); - dasd_schedule_bh(device); + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); dasd_put_device(device); } -static void -dasd_handle_state_change_pending(struct dasd_device *device) +void dasd_generic_handle_state_change(struct dasd_device *device) { - struct dasd_ccw_req *cqr; - struct list_head *l, *n; - /* First of all start sense subsystem status request. */ dasd_eer_snss(device); device->stopped &= ~DASD_STOPPED_PENDING; - - /* restart all 'running' IO on queue */ - list_for_each_safe(l, n, &device->ccw_queue) { - cqr = list_entry(l, struct dasd_ccw_req, list); - if (cqr->status == DASD_CQR_IN_IO) { - cqr->status = DASD_CQR_QUEUED; - } - } - dasd_clear_timer(device); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); } /* * Interrupt handler for "normal" ssch-io based dasd devices. */ -void -dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, - struct irb *irb) +void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, + struct irb *irb) { struct dasd_ccw_req *cqr, *next; struct dasd_device *device; unsigned long long now; int expires; - dasd_era_t era; - char mask; if (IS_ERR(irb)) { switch (PTR_ERR(irb)) { @@ -969,29 +996,25 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat), (unsigned int) intparm); - /* first of all check for state change pending interrupt */ - mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; - if ((irb->scsw.dstat & mask) == mask) { + /* check for unsolicited interrupts */ + cqr = (struct dasd_ccw_req *) intparm; + if (!cqr || ((irb->scsw.cc == 1) && + (irb->scsw.fctl & SCSW_FCTL_START_FUNC) && + (irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) ) { + if (cqr && cqr->status == DASD_CQR_IN_IO) + cqr->status = DASD_CQR_QUEUED; device = dasd_device_from_cdev_locked(cdev); if (!IS_ERR(device)) { - dasd_handle_state_change_pending(device); + dasd_device_clear_timer(device); + device->discipline->handle_unsolicited_interrupt(device, + irb); dasd_put_device(device); } return; } - cqr = (struct dasd_ccw_req *) intparm; - - /* check for unsolicited interrupts */ - if (cqr == NULL) { - MESSAGE(KERN_DEBUG, - "unsolicited interrupt received: bus_id %s", - cdev->dev.bus_id); - return; - } - - device = (struct dasd_device *) cqr->device; - if (device == NULL || + device = (struct dasd_device *) cqr->startdev; + if (!device || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", cdev->dev.bus_id); @@ -999,12 +1022,12 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } /* Check for clear pending */ - if (cqr->status == DASD_CQR_CLEAR && + if (cqr->status == DASD_CQR_CLEAR_PENDING && irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) { - cqr->status = DASD_CQR_QUEUED; - dasd_clear_timer(device); + cqr->status = DASD_CQR_CLEARED; + dasd_device_clear_timer(device); wake_up(&dasd_flush_wq); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); return; } @@ -1017,272 +1040,164 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p", ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr); - - /* Find out the appropriate era_action. */ - if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) - era = dasd_era_fatal; - else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && - irb->scsw.cstat == 0 && - !irb->esw.esw0.erw.cons) - era = dasd_era_none; - else if (irb->esw.esw0.erw.cons) - era = device->discipline->examine_error(cqr, irb); - else - era = dasd_era_recover; - - DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era); + next = NULL; expires = 0; - if (era == dasd_era_none) { - cqr->status = DASD_CQR_DONE; + if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && + irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) { + /* request was completed successfully */ + cqr->status = DASD_CQR_SUCCESS; cqr->stopclk = now; /* Start first request on queue if possible -> fast_io. */ - if (cqr->list.next != &device->ccw_queue) { - next = list_entry(cqr->list.next, - struct dasd_ccw_req, list); - if ((next->status == DASD_CQR_QUEUED) && - (!device->stopped)) { - if (device->discipline->start_IO(next) == 0) - expires = next->expires; - else - DEV_MESSAGE(KERN_DEBUG, device, "%s", - "Interrupt fastpath " - "failed!"); - } + if (cqr->devlist.next != &device->ccw_queue) { + next = list_entry(cqr->devlist.next, + struct dasd_ccw_req, devlist); } - } else { /* error */ - memcpy(&cqr->irb, irb, sizeof (struct irb)); + } else { /* error */ + memcpy(&cqr->irb, irb, sizeof(struct irb)); if (device->features & DASD_FEATURE_ERPLOG) { - /* dump sense data */ dasd_log_sense(cqr, irb); } - switch (era) { - case dasd_era_fatal: - cqr->status = DASD_CQR_FAILED; - cqr->stopclk = now; - break; - case dasd_era_recover: + /* If we have no sense data, or we just don't want complex ERP + * for this request, but if we have retries left, then just + * reset this request and retry it in the fastpath + */ + if (!(cqr->irb.esw.esw0.erw.cons && + test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) && + cqr->retries > 0) { + DEV_MESSAGE(KERN_DEBUG, device, + "default ERP in fastpath (%i retries left)", + cqr->retries); + cqr->lpm = LPM_ANYPATH; + cqr->status = DASD_CQR_QUEUED; + next = cqr; + } else cqr->status = DASD_CQR_ERROR; - break; - default: - BUG(); - } + } + if (next && (next->status == DASD_CQR_QUEUED) && + (!device->stopped)) { + if (device->discipline->start_IO(next) == 0) + expires = next->expires; + else + DEV_MESSAGE(KERN_DEBUG, device, "%s", + "Interrupt fastpath " + "failed!"); } if (expires != 0) - dasd_set_timer(device, expires); + dasd_device_set_timer(device, expires); else - dasd_clear_timer(device); - dasd_schedule_bh(device); + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); } /* - * posts the buffer_cache about a finalized request + * If we have an error on a dasd_block layer request then we cancel + * and return all further requests from the same dasd_block as well. */ -static inline void -dasd_end_request(struct request *req, int uptodate) +static void __dasd_device_recovery(struct dasd_device *device, + struct dasd_ccw_req *ref_cqr) { - if (end_that_request_first(req, uptodate, req->hard_nr_sectors)) - BUG(); - add_disk_randomness(req->rq_disk); - end_that_request_last(req, uptodate); -} + struct list_head *l, *n; + struct dasd_ccw_req *cqr; -/* - * Process finished error recovery ccw. - */ -static inline void -__dasd_process_erp(struct dasd_device *device, struct dasd_ccw_req *cqr) -{ - dasd_erp_fn_t erp_fn; + /* + * only requeue request that came from the dasd_block layer + */ + if (!ref_cqr->block) + return; - if (cqr->status == DASD_CQR_DONE) - DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful"); - else - DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful"); - erp_fn = device->discipline->erp_postaction(cqr); - erp_fn(cqr); -} + list_for_each_safe(l, n, &device->ccw_queue) { + cqr = list_entry(l, struct dasd_ccw_req, devlist); + if (cqr->status == DASD_CQR_QUEUED && + ref_cqr->block == cqr->block) { + cqr->status = DASD_CQR_CLEARED; + } + } +}; /* - * Process ccw request queue. + * Remove those ccw requests from the queue that need to be returned + * to the upper layer. */ -static void -__dasd_process_ccw_queue(struct dasd_device * device, - struct list_head *final_queue) +static void __dasd_device_process_ccw_queue(struct dasd_device *device, + struct list_head *final_queue) { struct list_head *l, *n; struct dasd_ccw_req *cqr; - dasd_erp_fn_t erp_fn; -restart: /* Process request with final status. */ list_for_each_safe(l, n, &device->ccw_queue) { - cqr = list_entry(l, struct dasd_ccw_req, list); + cqr = list_entry(l, struct dasd_ccw_req, devlist); + /* Stop list processing at the first non-final request. */ - if (cqr->status != DASD_CQR_DONE && - cqr->status != DASD_CQR_FAILED && - cqr->status != DASD_CQR_ERROR) + if (cqr->status == DASD_CQR_QUEUED || + cqr->status == DASD_CQR_IN_IO || + cqr->status == DASD_CQR_CLEAR_PENDING) break; - /* Process requests with DASD_CQR_ERROR */ if (cqr->status == DASD_CQR_ERROR) { - if (cqr->irb.scsw.fctl & SCSW_FCTL_HALT_FUNC) { - cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock(); - } else { - if (cqr->irb.esw.esw0.erw.cons && - test_bit(DASD_CQR_FLAGS_USE_ERP, - &cqr->flags)) { - erp_fn = device->discipline-> - erp_action(cqr); - erp_fn(cqr); - } else - dasd_default_erp_action(cqr); - } - goto restart; - } - - /* First of all call extended error reporting. */ - if (dasd_eer_enabled(device) && - cqr->status == DASD_CQR_FAILED) { - dasd_eer_write(device, cqr, DASD_EER_FATALERROR); - - /* restart request */ - cqr->status = DASD_CQR_QUEUED; - cqr->retries = 255; - device->stopped |= DASD_STOPPED_QUIESCE; - goto restart; + __dasd_device_recovery(device, cqr); } - - /* Process finished ERP request. */ - if (cqr->refers) { - __dasd_process_erp(device, cqr); - goto restart; - } - /* Rechain finished requests to final queue */ - cqr->endclk = get_clock(); - list_move_tail(&cqr->list, final_queue); + list_move_tail(&cqr->devlist, final_queue); } } -static void -dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data) -{ - struct request *req; - struct dasd_device *device; - int status; - - req = (struct request *) data; - device = cqr->device; - dasd_profile_end(device, cqr, req); - status = cqr->device->discipline->free_cp(cqr,req); - spin_lock_irq(&device->request_queue_lock); - dasd_end_request(req, status); - spin_unlock_irq(&device->request_queue_lock); -} - - /* - * Fetch requests from the block device queue. + * the cqrs from the final queue are returned to the upper layer + * by setting a dasd_block state and calling the callback function */ -static void -__dasd_process_blk_queue(struct dasd_device * device) +static void __dasd_device_process_final_queue(struct dasd_device *device, + struct list_head *final_queue) { - struct request_queue *queue; - struct request *req; + struct list_head *l, *n; struct dasd_ccw_req *cqr; - int nr_queued; - - queue = device->request_queue; - /* No queue ? Then there is nothing to do. */ - if (queue == NULL) - return; - - /* - * We requeue request from the block device queue to the ccw - * queue only in two states. In state DASD_STATE_READY the - * partition detection is done and we need to requeue requests - * for that. State DASD_STATE_ONLINE is normal block device - * operation. - */ - if (device->state != DASD_STATE_READY && - device->state != DASD_STATE_ONLINE) - return; - nr_queued = 0; - /* Now we try to fetch requests from the request queue */ - list_for_each_entry(cqr, &device->ccw_queue, list) - if (cqr->status == DASD_CQR_QUEUED) - nr_queued++; - while (!blk_queue_plugged(queue) && - elv_next_request(queue) && - nr_queued < DASD_CHANQ_MAX_SIZE) { - req = elv_next_request(queue); - if (device->features & DASD_FEATURE_READONLY && - rq_data_dir(req) == WRITE) { - DBF_DEV_EVENT(DBF_ERR, device, - "Rejecting write request %p", - req); - blkdev_dequeue_request(req); - dasd_end_request(req, 0); - continue; - } - if (device->stopped & DASD_STOPPED_DC_EIO) { - blkdev_dequeue_request(req); - dasd_end_request(req, 0); - continue; - } - cqr = device->discipline->build_cp(device, req); - if (IS_ERR(cqr)) { - if (PTR_ERR(cqr) == -ENOMEM) - break; /* terminate request queue loop */ - if (PTR_ERR(cqr) == -EAGAIN) { - /* - * The current request cannot be build right - * now, we have to try later. If this request - * is the head-of-queue we stop the device - * for 1/2 second. - */ - if (!list_empty(&device->ccw_queue)) - break; - device->stopped |= DASD_STOPPED_PENDING; - dasd_set_timer(device, HZ/2); - break; - } - DBF_DEV_EVENT(DBF_ERR, device, - "CCW creation failed (rc=%ld) " - "on request %p", - PTR_ERR(cqr), req); - blkdev_dequeue_request(req); - dasd_end_request(req, 0); - continue; + list_for_each_safe(l, n, final_queue) { + cqr = list_entry(l, struct dasd_ccw_req, devlist); + list_del_init(&cqr->devlist); + if (cqr->block) + spin_lock_bh(&cqr->block->queue_lock); + switch (cqr->status) { + case DASD_CQR_SUCCESS: + cqr->status = DASD_CQR_DONE; + break; + case DASD_CQR_ERROR: + cqr->status = DASD_CQR_NEED_ERP; + break; + case DASD_CQR_CLEARED: + cqr->status = DASD_CQR_TERMINATED; + break; + default: + DEV_MESSAGE(KERN_ERR, device, + "wrong cqr status in __dasd_process_final_queue " + "for cqr %p, status %x", + cqr, cqr->status); + BUG(); } - cqr->callback = dasd_end_request_cb; - cqr->callback_data = (void *) req; - cqr->status = DASD_CQR_QUEUED; - blkdev_dequeue_request(req); - list_add_tail(&cqr->list, &device->ccw_queue); - dasd_profile_start(device, cqr, req); - nr_queued++; + if (cqr->block) + spin_unlock_bh(&cqr->block->queue_lock); + if (cqr->callback != NULL) + (cqr->callback)(cqr, cqr->callback_data); } } + + /* * Take a look at the first request on the ccw queue and check * if it reached its expire time. If so, terminate the IO. */ -static void -__dasd_check_expire(struct dasd_device * device) +static void __dasd_device_check_expire(struct dasd_device *device) { struct dasd_ccw_req *cqr; if (list_empty(&device->ccw_queue)) return; - cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) && (time_after_eq(jiffies, cqr->expires + cqr->starttime))) { if (device->discipline->term_IO(cqr) != 0) { /* Hmpf, try again in 5 sec */ - dasd_set_timer(device, 5*HZ); + dasd_device_set_timer(device, 5*HZ); DEV_MESSAGE(KERN_ERR, device, "internal error - timeout (%is) expired " "for cqr %p, termination failed, " @@ -1301,77 +1216,53 @@ __dasd_check_expire(struct dasd_device * device) * Take a look at the first request on the ccw queue and check * if it needs to be started. */ -static void -__dasd_start_head(struct dasd_device * device) +static void __dasd_device_start_head(struct dasd_device *device) { struct dasd_ccw_req *cqr; int rc; if (list_empty(&device->ccw_queue)) return; - cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); if (cqr->status != DASD_CQR_QUEUED) return; - /* Non-temporary stop condition will trigger fail fast */ - if (device->stopped & ~DASD_STOPPED_PENDING && - test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && - (!dasd_eer_enabled(device))) { - cqr->status = DASD_CQR_FAILED; - dasd_schedule_bh(device); + /* when device is stopped, return request to previous layer */ + if (device->stopped) { + cqr->status = DASD_CQR_CLEARED; + dasd_schedule_device_bh(device); return; } - /* Don't try to start requests if device is stopped */ - if (device->stopped) - return; rc = device->discipline->start_IO(cqr); if (rc == 0) - dasd_set_timer(device, cqr->expires); + dasd_device_set_timer(device, cqr->expires); else if (rc == -EACCES) { - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); } else /* Hmpf, try again in 1/2 sec */ - dasd_set_timer(device, 50); -} - -static inline int -_wait_for_clear(struct dasd_ccw_req *cqr) -{ - return (cqr->status == DASD_CQR_QUEUED); + dasd_device_set_timer(device, 50); } /* - * Remove all requests from the ccw queue (all = '1') or only block device - * requests in case all = '0'. - * Take care of the erp-chain (chained via cqr->refers) and remove either - * the whole erp-chain or none of the erp-requests. - * If a request is currently running, term_IO is called and the request - * is re-queued. Prior to removing the terminated request we need to wait - * for the clear-interrupt. - * In case termination is not possible we stop processing and just finishing - * the already moved requests. + * Go through all request on the dasd_device request queue, + * terminate them on the cdev if necessary, and return them to the + * submitting layer via callback. + * Note: + * Make sure that all 'submitting layers' still exist when + * this function is called!. In other words, when 'device' is a base + * device then all block layer requests must have been removed before + * via dasd_flush_block_queue. */ -static int -dasd_flush_ccw_queue(struct dasd_device * device, int all) +int dasd_flush_device_queue(struct dasd_device *device) { - struct dasd_ccw_req *cqr, *orig, *n; - int rc, i; - + struct dasd_ccw_req *cqr, *n; + int rc; struct list_head flush_queue; INIT_LIST_HEAD(&flush_queue); spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = 0; -restart: - list_for_each_entry_safe(cqr, n, &device->ccw_queue, list) { - /* get original request of erp request-chain */ - for (orig = cqr; orig->refers != NULL; orig = orig->refers); - - /* Flush all request or only block device requests? */ - if (all == 0 && cqr->callback != dasd_end_request_cb && - orig->callback != dasd_end_request_cb) { - continue; - } + list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) { /* Check status and move request to flush_queue */ switch (cqr->status) { case DASD_CQR_IN_IO: @@ -1387,90 +1278,60 @@ restart: } break; case DASD_CQR_QUEUED: - case DASD_CQR_ERROR: - /* set request to FAILED */ cqr->stopclk = get_clock(); - cqr->status = DASD_CQR_FAILED; + cqr->status = DASD_CQR_CLEARED; break; - default: /* do not touch the others */ + default: /* no need to modify the others */ break; } - /* Rechain request (including erp chain) */ - for (i = 0; cqr != NULL; cqr = cqr->refers, i++) { - cqr->endclk = get_clock(); - list_move_tail(&cqr->list, &flush_queue); - } - if (i > 1) - /* moved more than one request - need to restart */ - goto restart; + list_move_tail(&cqr->devlist, &flush_queue); } - finished: spin_unlock_irq(get_ccwdev_lock(device->cdev)); - /* Now call the callback function of flushed requests */ -restart_cb: - list_for_each_entry_safe(cqr, n, &flush_queue, list) { - if (cqr->status == DASD_CQR_CLEAR) { - /* wait for clear interrupt! */ - wait_event(dasd_flush_wq, _wait_for_clear(cqr)); - cqr->status = DASD_CQR_FAILED; - } - /* Process finished ERP request. */ - if (cqr->refers) { - __dasd_process_erp(device, cqr); - /* restart list_for_xx loop since dasd_process_erp - * might remove multiple elements */ - goto restart_cb; - } - /* call the callback function */ - cqr->endclk = get_clock(); - if (cqr->callback != NULL) - (cqr->callback)(cqr, cqr->callback_data); - } + /* + * After this point all requests must be in state CLEAR_PENDING, + * CLEARED, SUCCESS or ERROR. Now wait for CLEAR_PENDING to become + * one of the others. + */ + list_for_each_entry_safe(cqr, n, &flush_queue, devlist) + wait_event(dasd_flush_wq, + (cqr->status != DASD_CQR_CLEAR_PENDING)); + /* + * Now set each request back to TERMINATED, DONE or NEED_ERP + * and call the callback function of flushed requests + */ + __dasd_device_process_final_queue(device, &flush_queue); return rc; } /* * Acquire the device lock and process queues for the device. */ -static void -dasd_tasklet(struct dasd_device * device) +static void dasd_device_tasklet(struct dasd_device *device) { struct list_head final_queue; - struct list_head *l, *n; - struct dasd_ccw_req *cqr; atomic_set (&device->tasklet_scheduled, 0); INIT_LIST_HEAD(&final_queue); spin_lock_irq(get_ccwdev_lock(device->cdev)); /* Check expire time of first request on the ccw queue. */ - __dasd_check_expire(device); - /* Finish off requests on ccw queue */ - __dasd_process_ccw_queue(device, &final_queue); + __dasd_device_check_expire(device); + /* find final requests on ccw queue */ + __dasd_device_process_ccw_queue(device, &final_queue); spin_unlock_irq(get_ccwdev_lock(device->cdev)); /* Now call the callback function of requests with final status */ - list_for_each_safe(l, n, &final_queue) { - cqr = list_entry(l, struct dasd_ccw_req, list); - list_del_init(&cqr->list); - if (cqr->callback != NULL) - (cqr->callback)(cqr, cqr->callback_data); - } - spin_lock_irq(&device->request_queue_lock); - spin_lock(get_ccwdev_lock(device->cdev)); - /* Get new request from the block device request queue */ - __dasd_process_blk_queue(device); + __dasd_device_process_final_queue(device, &final_queue); + spin_lock_irq(get_ccwdev_lock(device->cdev)); /* Now check if the head of the ccw queue needs to be started. */ - __dasd_start_head(device); - spin_unlock(get_ccwdev_lock(device->cdev)); - spin_unlock_irq(&device->request_queue_lock); + __dasd_device_start_head(device); + spin_unlock_irq(get_ccwdev_lock(device->cdev)); dasd_put_device(device); } /* * Schedules a call to dasd_tasklet over the device tasklet. */ -void -dasd_schedule_bh(struct dasd_device * device) +void dasd_schedule_device_bh(struct dasd_device *device) { /* Protect against rescheduling. */ if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0) @@ -1480,160 +1341,109 @@ dasd_schedule_bh(struct dasd_device * device) } /* - * Queue a request to the head of the ccw_queue. Start the I/O if - * possible. + * Queue a request to the head of the device ccw_queue. + * Start the I/O if possible. */ -void -dasd_add_request_head(struct dasd_ccw_req *req) +void dasd_add_request_head(struct dasd_ccw_req *cqr) { struct dasd_device *device; unsigned long flags; - device = req->device; + device = cqr->startdev; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - req->status = DASD_CQR_QUEUED; - req->device = device; - list_add(&req->list, &device->ccw_queue); + cqr->status = DASD_CQR_QUEUED; + list_add(&cqr->devlist, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } /* - * Queue a request to the tail of the ccw_queue. Start the I/O if - * possible. + * Queue a request to the tail of the device ccw_queue. + * Start the I/O if possible. */ -void -dasd_add_request_tail(struct dasd_ccw_req *req) +void dasd_add_request_tail(struct dasd_ccw_req *cqr) { struct dasd_device *device; unsigned long flags; - device = req->device; + device = cqr->startdev; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - req->status = DASD_CQR_QUEUED; - req->device = device; - list_add_tail(&req->list, &device->ccw_queue); + cqr->status = DASD_CQR_QUEUED; + list_add_tail(&cqr->devlist, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } /* - * Wakeup callback. + * Wakeup helper for the 'sleep_on' functions. */ -static void -dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data) +static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data) { wake_up((wait_queue_head_t *) data); } -static inline int -_wait_for_wakeup(struct dasd_ccw_req *cqr) +static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr) { struct dasd_device *device; int rc; - device = cqr->device; + device = cqr->startdev; spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = ((cqr->status == DASD_CQR_DONE || - cqr->status == DASD_CQR_FAILED) && - list_empty(&cqr->list)); + cqr->status == DASD_CQR_NEED_ERP || + cqr->status == DASD_CQR_TERMINATED) && + list_empty(&cqr->devlist)); spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; } /* - * Attempts to start a special ccw queue and waits for its completion. + * Queue a request to the tail of the device ccw_queue and wait for + * it's completion. */ -int -dasd_sleep_on(struct dasd_ccw_req * cqr) +int dasd_sleep_on(struct dasd_ccw_req *cqr) { wait_queue_head_t wait_q; struct dasd_device *device; int rc; - device = cqr->device; - spin_lock_irq(get_ccwdev_lock(device->cdev)); + device = cqr->startdev; init_waitqueue_head (&wait_q); cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; - cqr->status = DASD_CQR_QUEUED; - list_add_tail(&cqr->list, &device->ccw_queue); - - /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); - - spin_unlock_irq(get_ccwdev_lock(device->cdev)); - + dasd_add_request_tail(cqr); wait_event(wait_q, _wait_for_wakeup(cqr)); /* Request status is either done or failed. */ - rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; + rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; return rc; } /* - * Attempts to start a special ccw queue and wait interruptible - * for its completion. + * Queue a request to the tail of the device ccw_queue and wait + * interruptible for it's completion. */ -int -dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) +int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr) { wait_queue_head_t wait_q; struct dasd_device *device; - int rc, finished; - - device = cqr->device; - spin_lock_irq(get_ccwdev_lock(device->cdev)); + int rc; + device = cqr->startdev; init_waitqueue_head (&wait_q); cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; - cqr->status = DASD_CQR_QUEUED; - list_add_tail(&cqr->list, &device->ccw_queue); - - /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); - spin_unlock_irq(get_ccwdev_lock(device->cdev)); - - finished = 0; - while (!finished) { - rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); - if (rc != -ERESTARTSYS) { - /* Request is final (done or failed) */ - rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; - break; - } - spin_lock_irq(get_ccwdev_lock(device->cdev)); - switch (cqr->status) { - case DASD_CQR_IN_IO: - /* terminate runnig cqr */ - if (device->discipline->term_IO) { - cqr->retries = -1; - device->discipline->term_IO(cqr); - /* wait (non-interruptible) for final status - * because signal ist still pending */ - spin_unlock_irq(get_ccwdev_lock(device->cdev)); - wait_event(wait_q, _wait_for_wakeup(cqr)); - spin_lock_irq(get_ccwdev_lock(device->cdev)); - rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; - finished = 1; - } - break; - case DASD_CQR_QUEUED: - /* request */ - list_del_init(&cqr->list); - rc = -EIO; - finished = 1; - break; - default: - /* cqr with 'non-interruptable' status - just wait */ - break; - } - spin_unlock_irq(get_ccwdev_lock(device->cdev)); + dasd_add_request_tail(cqr); + rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); + if (rc == -ERESTARTSYS) { + dasd_cancel_req(cqr); + /* wait (non-interruptible) for final status */ + wait_event(wait_q, _wait_for_wakeup(cqr)); } + rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; return rc; } @@ -1643,25 +1453,23 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) * and be put back to status queued, before the special request is added * to the head of the queue. Then the special request is waited on normally. */ -static inline int -_dasd_term_running_cqr(struct dasd_device *device) +static inline int _dasd_term_running_cqr(struct dasd_device *device) { struct dasd_ccw_req *cqr; if (list_empty(&device->ccw_queue)) return 0; - cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); return device->discipline->term_IO(cqr); } -int -dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) +int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) { wait_queue_head_t wait_q; struct dasd_device *device; int rc; - device = cqr->device; + device = cqr->startdev; spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = _dasd_term_running_cqr(device); if (rc) { @@ -1673,17 +1481,17 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; cqr->status = DASD_CQR_QUEUED; - list_add(&cqr->list, &device->ccw_queue); + list_add(&cqr->devlist, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); wait_event(wait_q, _wait_for_wakeup(cqr)); /* Request status is either done or failed. */ - rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; + rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; return rc; } @@ -1692,11 +1500,14 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) * This is useful to timeout requests. The request will be * terminated if it is currently in i/o. * Returns 1 if the request has been terminated. + * 0 if there was no need to terminate the request (not started yet) + * negative error code if termination failed + * Cancellation of a request is an asynchronous operation! The calling + * function has to wait until the request is properly returned via callback. */ -int -dasd_cancel_req(struct dasd_ccw_req *cqr) +int dasd_cancel_req(struct dasd_ccw_req *cqr) { - struct dasd_device *device = cqr->device; + struct dasd_device *device = cqr->startdev; unsigned long flags; int rc; @@ -1704,74 +1515,453 @@ dasd_cancel_req(struct dasd_ccw_req *cqr) spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); switch (cqr->status) { case DASD_CQR_QUEUED: - /* request was not started - just set to failed */ - cqr->status = DASD_CQR_FAILED; + /* request was not started - just set to cleared */ + cqr->status = DASD_CQR_CLEARED; break; case DASD_CQR_IN_IO: /* request in IO - terminate IO and release again */ - if (device->discipline->term_IO(cqr) != 0) - /* what to do if unable to terminate ?????? - e.g. not _IN_IO */ - cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock(); - rc = 1; + rc = device->discipline->term_IO(cqr); + if (rc) { + DEV_MESSAGE(KERN_ERR, device, + "dasd_cancel_req is unable " + " to terminate request %p, rc = %d", + cqr, rc); + } else { + cqr->stopclk = get_clock(); + rc = 1; + } break; - case DASD_CQR_DONE: - case DASD_CQR_FAILED: - /* already finished - do nothing */ + default: /* already finished or clear pending - do nothing */ break; - default: - DEV_MESSAGE(KERN_ALERT, device, - "invalid status %02x in request", - cqr->status); + } + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + dasd_schedule_device_bh(device); + return rc; +} + + +/* + * SECTION: Operations of the dasd_block layer. + */ + +/* + * Timeout function for dasd_block. This is used when the block layer + * is waiting for something that may not come reliably, (e.g. a state + * change interrupt) + */ +static void dasd_block_timeout(unsigned long ptr) +{ + unsigned long flags; + struct dasd_block *block; + + block = (struct dasd_block *) ptr; + spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags); + /* re-activate request queue */ + block->base->stopped &= ~DASD_STOPPED_PENDING; + spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags); + dasd_schedule_block_bh(block); +} + +/* + * Setup timeout for a dasd_block in jiffies. + */ +void dasd_block_set_timer(struct dasd_block *block, int expires) +{ + if (expires == 0) { + if (timer_pending(&block->timer)) + del_timer(&block->timer); + return; + } + if (timer_pending(&block->timer)) { + if (mod_timer(&block->timer, jiffies + expires)) + return; + } + block->timer.function = dasd_block_timeout; + block->timer.data = (unsigned long) block; + block->timer.expires = jiffies + expires; + add_timer(&block->timer); +} + +/* + * Clear timeout for a dasd_block. + */ +void dasd_block_clear_timer(struct dasd_block *block) +{ + if (timer_pending(&block->timer)) + del_timer(&block->timer); +} + +/* + * posts the buffer_cache about a finalized request + */ +static inline void dasd_end_request(struct request *req, int uptodate) +{ + if (end_that_request_first(req, uptodate, req->hard_nr_sectors)) BUG(); + add_disk_randomness(req->rq_disk); + end_that_request_last(req, uptodate); +} + +/* + * Process finished error recovery ccw. + */ +static inline void __dasd_block_process_erp(struct dasd_block *block, + struct dasd_ccw_req *cqr) +{ + dasd_erp_fn_t erp_fn; + struct dasd_device *device = block->base; + + if (cqr->status == DASD_CQR_DONE) + DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful"); + else + DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful"); + erp_fn = device->discipline->erp_postaction(cqr); + erp_fn(cqr); +} +/* + * Fetch requests from the block device queue. + */ +static void __dasd_process_request_queue(struct dasd_block *block) +{ + struct request_queue *queue; + struct request *req; + struct dasd_ccw_req *cqr; + struct dasd_device *basedev; + unsigned long flags; + queue = block->request_queue; + basedev = block->base; + /* No queue ? Then there is nothing to do. */ + if (queue == NULL) + return; + + /* + * We requeue request from the block device queue to the ccw + * queue only in two states. In state DASD_STATE_READY the + * partition detection is done and we need to requeue requests + * for that. State DASD_STATE_ONLINE is normal block device + * operation. + */ + if (basedev->state < DASD_STATE_READY) + return; + /* Now we try to fetch requests from the request queue */ + while (!blk_queue_plugged(queue) && + elv_next_request(queue)) { + + req = elv_next_request(queue); + + if (basedev->features & DASD_FEATURE_READONLY && + rq_data_dir(req) == WRITE) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "Rejecting write request %p", + req); + blkdev_dequeue_request(req); + dasd_end_request(req, 0); + continue; + } + cqr = basedev->discipline->build_cp(basedev, block, req); + if (IS_ERR(cqr)) { + if (PTR_ERR(cqr) == -EBUSY) + break; /* normal end condition */ + if (PTR_ERR(cqr) == -ENOMEM) + break; /* terminate request queue loop */ + if (PTR_ERR(cqr) == -EAGAIN) { + /* + * The current request cannot be build right + * now, we have to try later. If this request + * is the head-of-queue we stop the device + * for 1/2 second. + */ + if (!list_empty(&block->ccw_queue)) + break; + spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags); + basedev->stopped |= DASD_STOPPED_PENDING; + spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags); + dasd_block_set_timer(block, HZ/2); + break; + } + DBF_DEV_EVENT(DBF_ERR, basedev, + "CCW creation failed (rc=%ld) " + "on request %p", + PTR_ERR(cqr), req); + blkdev_dequeue_request(req); + dasd_end_request(req, 0); + continue; + } + /* + * Note: callback is set to dasd_return_cqr_cb in + * __dasd_block_start_head to cover erp requests as well + */ + cqr->callback_data = (void *) req; + cqr->status = DASD_CQR_FILLED; + blkdev_dequeue_request(req); + list_add_tail(&cqr->blocklist, &block->ccw_queue); + dasd_profile_start(block, cqr, req); + } +} + +static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr) +{ + struct request *req; + int status; + + req = (struct request *) cqr->callback_data; + dasd_profile_end(cqr->block, cqr, req); + status = cqr->memdev->discipline->free_cp(cqr, req); + dasd_end_request(req, status); +} + +/* + * Process ccw request queue. + */ +static void __dasd_process_block_ccw_queue(struct dasd_block *block, + struct list_head *final_queue) +{ + struct list_head *l, *n; + struct dasd_ccw_req *cqr; + dasd_erp_fn_t erp_fn; + unsigned long flags; + struct dasd_device *base = block->base; + +restart: + /* Process request with final status. */ + list_for_each_safe(l, n, &block->ccw_queue) { + cqr = list_entry(l, struct dasd_ccw_req, blocklist); + if (cqr->status != DASD_CQR_DONE && + cqr->status != DASD_CQR_FAILED && + cqr->status != DASD_CQR_NEED_ERP && + cqr->status != DASD_CQR_TERMINATED) + continue; + + if (cqr->status == DASD_CQR_TERMINATED) { + base->discipline->handle_terminated_request(cqr); + goto restart; + } + + /* Process requests that may be recovered */ + if (cqr->status == DASD_CQR_NEED_ERP) { + if (cqr->irb.esw.esw0.erw.cons && + test_bit(DASD_CQR_FLAGS_USE_ERP, + &cqr->flags)) { + erp_fn = base->discipline->erp_action(cqr); + erp_fn(cqr); + } + goto restart; + } + + /* First of all call extended error reporting. */ + if (dasd_eer_enabled(base) && + cqr->status == DASD_CQR_FAILED) { + dasd_eer_write(base, cqr, DASD_EER_FATALERROR); + + /* restart request */ + cqr->status = DASD_CQR_FILLED; + cqr->retries = 255; + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); + base->stopped |= DASD_STOPPED_QUIESCE; + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), + flags); + goto restart; + } + + /* Process finished ERP request. */ + if (cqr->refers) { + __dasd_block_process_erp(block, cqr); + goto restart; + } + + /* Rechain finished requests to final queue */ + cqr->endclk = get_clock(); + list_move_tail(&cqr->blocklist, final_queue); + } +} + +static void dasd_return_cqr_cb(struct dasd_ccw_req *cqr, void *data) +{ + dasd_schedule_block_bh(cqr->block); +} + +static void __dasd_block_start_head(struct dasd_block *block) +{ + struct dasd_ccw_req *cqr; + + if (list_empty(&block->ccw_queue)) + return; + /* We allways begin with the first requests on the queue, as some + * of previously started requests have to be enqueued on a + * dasd_device again for error recovery. + */ + list_for_each_entry(cqr, &block->ccw_queue, blocklist) { + if (cqr->status != DASD_CQR_FILLED) + continue; + /* Non-temporary stop condition will trigger fail fast */ + if (block->base->stopped & ~DASD_STOPPED_PENDING && + test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + (!dasd_eer_enabled(block->base))) { + cqr->status = DASD_CQR_FAILED; + dasd_schedule_block_bh(block); + continue; + } + /* Don't try to start requests if device is stopped */ + if (block->base->stopped) + return; + + /* just a fail safe check, should not happen */ + if (!cqr->startdev) + cqr->startdev = block->base; + + /* make sure that the requests we submit find their way back */ + cqr->callback = dasd_return_cqr_cb; + + dasd_add_request_tail(cqr); + } +} + +/* + * Central dasd_block layer routine. Takes requests from the generic + * block layer request queue, creates ccw requests, enqueues them on + * a dasd_device and processes ccw requests that have been returned. + */ +static void dasd_block_tasklet(struct dasd_block *block) +{ + struct list_head final_queue; + struct list_head *l, *n; + struct dasd_ccw_req *cqr; + + atomic_set(&block->tasklet_scheduled, 0); + INIT_LIST_HEAD(&final_queue); + spin_lock(&block->queue_lock); + /* Finish off requests on ccw queue */ + __dasd_process_block_ccw_queue(block, &final_queue); + spin_unlock(&block->queue_lock); + /* Now call the callback function of requests with final status */ + spin_lock_irq(&block->request_queue_lock); + list_for_each_safe(l, n, &final_queue) { + cqr = list_entry(l, struct dasd_ccw_req, blocklist); + list_del_init(&cqr->blocklist); + __dasd_cleanup_cqr(cqr); + } + spin_lock(&block->queue_lock); + /* Get new request from the block device request queue */ + __dasd_process_request_queue(block); + /* Now check if the head of the ccw queue needs to be started. */ + __dasd_block_start_head(block); + spin_unlock(&block->queue_lock); + spin_unlock_irq(&block->request_queue_lock); + dasd_put_device(block->base); +} + +static void _dasd_wake_block_flush_cb(struct dasd_ccw_req *cqr, void *data) +{ + wake_up(&dasd_flush_wq); +} + +/* + * Go through all request on the dasd_block request queue, cancel them + * on the respective dasd_device, and return them to the generic + * block layer. + */ +static int dasd_flush_block_queue(struct dasd_block *block) +{ + struct dasd_ccw_req *cqr, *n; + int rc, i; + struct list_head flush_queue; + + INIT_LIST_HEAD(&flush_queue); + spin_lock_bh(&block->queue_lock); + rc = 0; +restart: + list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) { + /* if this request currently owned by a dasd_device cancel it */ + if (cqr->status >= DASD_CQR_QUEUED) + rc = dasd_cancel_req(cqr); + if (rc < 0) + break; + /* Rechain request (including erp chain) so it won't be + * touched by the dasd_block_tasklet anymore. + * Replace the callback so we notice when the request + * is returned from the dasd_device layer. + */ + cqr->callback = _dasd_wake_block_flush_cb; + for (i = 0; cqr != NULL; cqr = cqr->refers, i++) + list_move_tail(&cqr->blocklist, &flush_queue); + if (i > 1) + /* moved more than one request - need to restart */ + goto restart; + } + spin_unlock_bh(&block->queue_lock); + /* Now call the callback function of flushed requests */ +restart_cb: + list_for_each_entry_safe(cqr, n, &flush_queue, blocklist) { + wait_event(dasd_flush_wq, (cqr->status < DASD_CQR_QUEUED)); + /* Process finished ERP request. */ + if (cqr->refers) { + __dasd_block_process_erp(block, cqr); + /* restart list_for_xx loop since dasd_process_erp + * might remove multiple elements */ + goto restart_cb; + } + /* call the callback function */ + cqr->endclk = get_clock(); + list_del_init(&cqr->blocklist); + __dasd_cleanup_cqr(cqr); } - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - dasd_schedule_bh(device); return rc; } /* - * SECTION: Block device operations (request queue, partitions, open, release). + * Schedules a call to dasd_tasklet over the device tasklet. + */ +void dasd_schedule_block_bh(struct dasd_block *block) +{ + /* Protect against rescheduling. */ + if (atomic_cmpxchg(&block->tasklet_scheduled, 0, 1) != 0) + return; + /* life cycle of block is bound to it's base device */ + dasd_get_device(block->base); + tasklet_hi_schedule(&block->tasklet); +} + + +/* + * SECTION: external block device operations + * (request queue handling, open, release, etc.) */ /* * Dasd request queue function. Called from ll_rw_blk.c */ -static void -do_dasd_request(struct request_queue * queue) +static void do_dasd_request(struct request_queue *queue) { - struct dasd_device *device; + struct dasd_block *block; - device = (struct dasd_device *) queue->queuedata; - spin_lock(get_ccwdev_lock(device->cdev)); + block = queue->queuedata; + spin_lock(&block->queue_lock); /* Get new request from the block device request queue */ - __dasd_process_blk_queue(device); + __dasd_process_request_queue(block); /* Now check if the head of the ccw queue needs to be started. */ - __dasd_start_head(device); - spin_unlock(get_ccwdev_lock(device->cdev)); + __dasd_block_start_head(block); + spin_unlock(&block->queue_lock); } /* * Allocate and initialize request queue and default I/O scheduler. */ -static int -dasd_alloc_queue(struct dasd_device * device) +static int dasd_alloc_queue(struct dasd_block *block) { int rc; - device->request_queue = blk_init_queue(do_dasd_request, - &device->request_queue_lock); - if (device->request_queue == NULL) + block->request_queue = blk_init_queue(do_dasd_request, + &block->request_queue_lock); + if (block->request_queue == NULL) return -ENOMEM; - device->request_queue->queuedata = device; + block->request_queue->queuedata = block; - elevator_exit(device->request_queue->elevator); - rc = elevator_init(device->request_queue, "deadline"); + elevator_exit(block->request_queue->elevator); + rc = elevator_init(block->request_queue, "deadline"); if (rc) { - blk_cleanup_queue(device->request_queue); + blk_cleanup_queue(block->request_queue); return rc; } return 0; @@ -1780,79 +1970,76 @@ dasd_alloc_queue(struct dasd_device * device) /* * Allocate and initialize request queue. */ -static void -dasd_setup_queue(struct dasd_device * device) +static void dasd_setup_queue(struct dasd_block *block) { int max; - blk_queue_hardsect_size(device->request_queue, device->bp_block); - max = device->discipline->max_blocks << device->s2b_shift; - blk_queue_max_sectors(device->request_queue, max); - blk_queue_max_phys_segments(device->request_queue, -1L); - blk_queue_max_hw_segments(device->request_queue, -1L); - blk_queue_max_segment_size(device->request_queue, -1L); - blk_queue_segment_boundary(device->request_queue, -1L); - blk_queue_ordered(device->request_queue, QUEUE_ORDERED_TAG, NULL); + blk_queue_hardsect_size(block->request_queue, block->bp_block); + max = block->base->discipline->max_blocks << block->s2b_shift; + blk_queue_max_sectors(block->request_queue, max); + blk_queue_max_phys_segments(block->request_queue, -1L); + blk_queue_max_hw_segments(block->request_queue, -1L); + blk_queue_max_segment_size(block->request_queue, -1L); + blk_queue_segment_boundary(block->request_queue, -1L); + blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN, NULL); } /* * Deactivate and free request queue. */ -static void -dasd_free_queue(struct dasd_device * device) +static void dasd_free_queue(struct dasd_block *block) { - if (device->request_queue) { - blk_cleanup_queue(device->request_queue); - device->request_queue = NULL; + if (block->request_queue) { + blk_cleanup_queue(block->request_queue); + block->request_queue = NULL; } } /* * Flush request on the request queue. */ -static void -dasd_flush_request_queue(struct dasd_device * device) +static void dasd_flush_request_queue(struct dasd_block *block) { struct request *req; - if (!device->request_queue) + if (!block->request_queue) return; - spin_lock_irq(&device->request_queue_lock); - while ((req = elv_next_request(device->request_queue))) { + spin_lock_irq(&block->request_queue_lock); + while ((req = elv_next_request(block->request_queue))) { blkdev_dequeue_request(req); dasd_end_request(req, 0); } - spin_unlock_irq(&device->request_queue_lock); + spin_unlock_irq(&block->request_queue_lock); } -static int -dasd_open(struct inode *inp, struct file *filp) +static int dasd_open(struct inode *inp, struct file *filp) { struct gendisk *disk = inp->i_bdev->bd_disk; - struct dasd_device *device = disk->private_data; + struct dasd_block *block = disk->private_data; + struct dasd_device *base = block->base; int rc; - atomic_inc(&device->open_count); - if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) { + atomic_inc(&block->open_count); + if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) { rc = -ENODEV; goto unlock; } - if (!try_module_get(device->discipline->owner)) { + if (!try_module_get(base->discipline->owner)) { rc = -EINVAL; goto unlock; } if (dasd_probeonly) { - DEV_MESSAGE(KERN_INFO, device, "%s", + DEV_MESSAGE(KERN_INFO, base, "%s", "No access to device due to probeonly mode"); rc = -EPERM; goto out; } - if (device->state <= DASD_STATE_BASIC) { - DBF_DEV_EVENT(DBF_ERR, device, " %s", + if (base->state <= DASD_STATE_BASIC) { + DBF_DEV_EVENT(DBF_ERR, base, " %s", " Cannot open unrecognized device"); rc = -ENODEV; goto out; @@ -1861,41 +2048,41 @@ dasd_open(struct inode *inp, struct file *filp) return 0; out: - module_put(device->discipline->owner); + module_put(base->discipline->owner); unlock: - atomic_dec(&device->open_count); + atomic_dec(&block->open_count); return rc; } -static int -dasd_release(struct inode *inp, struct file *filp) +static int dasd_release(struct inode *inp, struct file *filp) { struct gendisk *disk = inp->i_bdev->bd_disk; - struct dasd_device *device = disk->private_data; + struct dasd_block *block = disk->private_data; - atomic_dec(&device->open_count); - module_put(device->discipline->owner); + atomic_dec(&block->open_count); + module_put(block->base->discipline->owner); return 0; } /* * Return disk geometry. */ -static int -dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) +static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) { - struct dasd_device *device; + struct dasd_block *block; + struct dasd_device *base; - device = bdev->bd_disk->private_data; - if (!device) + block = bdev->bd_disk->private_data; + base = block->base; + if (!block) return -ENODEV; - if (!device->discipline || - !device->discipline->fill_geometry) + if (!base->discipline || + !base->discipline->fill_geometry) return -EINVAL; - device->discipline->fill_geometry(device, geo); - geo->start = get_start_sect(bdev) >> device->s2b_shift; + base->discipline->fill_geometry(block, geo); + geo->start = get_start_sect(bdev) >> block->s2b_shift; return 0; } @@ -1909,6 +2096,9 @@ dasd_device_operations = { .getgeo = dasd_getgeo, }; +/******************************************************************************* + * end of block device operations + */ static void dasd_exit(void) @@ -1937,9 +2127,8 @@ dasd_exit(void) * Initial attempt at a probe function. this can be simplified once * the other detection code is gone. */ -int -dasd_generic_probe (struct ccw_device *cdev, - struct dasd_discipline *discipline) +int dasd_generic_probe(struct ccw_device *cdev, + struct dasd_discipline *discipline) { int ret; @@ -1979,10 +2168,10 @@ dasd_generic_probe (struct ccw_device *cdev, * This will one day be called from a global not_oper handler. * It is also used by driver_unregister during module unload. */ -void -dasd_generic_remove (struct ccw_device *cdev) +void dasd_generic_remove(struct ccw_device *cdev) { struct dasd_device *device; + struct dasd_block *block; cdev->handler = NULL; @@ -2002,7 +2191,15 @@ dasd_generic_remove (struct ccw_device *cdev) */ dasd_set_target_state(device, DASD_STATE_NEW); /* dasd_delete_device destroys the device reference. */ + block = device->block; + device->block = NULL; dasd_delete_device(device); + /* + * life cycle of block is bound to device, so delete it after + * device was safely removed + */ + if (block) + dasd_free_block(block); } /* @@ -2010,10 +2207,8 @@ dasd_generic_remove (struct ccw_device *cdev) * the device is detected for the first time and is supposed to be used * or the user has started activation through sysfs. */ -int -dasd_generic_set_online (struct ccw_device *cdev, - struct dasd_discipline *base_discipline) - +int dasd_generic_set_online(struct ccw_device *cdev, + struct dasd_discipline *base_discipline) { struct dasd_discipline *discipline; struct dasd_device *device; @@ -2049,6 +2244,7 @@ dasd_generic_set_online (struct ccw_device *cdev, device->base_discipline = base_discipline; device->discipline = discipline; + /* check_device will allocate block device if necessary */ rc = discipline->check_device(device); if (rc) { printk (KERN_WARNING @@ -2068,6 +2264,8 @@ dasd_generic_set_online (struct ccw_device *cdev, cdev->dev.bus_id); rc = -ENODEV; dasd_set_target_state(device, DASD_STATE_NEW); + if (device->block) + dasd_free_block(device->block); dasd_delete_device(device); } else pr_debug("dasd_generic device %s found\n", @@ -2082,10 +2280,10 @@ dasd_generic_set_online (struct ccw_device *cdev, return rc; } -int -dasd_generic_set_offline (struct ccw_device *cdev) +int dasd_generic_set_offline(struct ccw_device *cdev) { struct dasd_device *device; + struct dasd_block *block; int max_count, open_count; device = dasd_device_from_cdev(cdev); @@ -2102,30 +2300,39 @@ dasd_generic_set_offline (struct ccw_device *cdev) * the blkdev_get in dasd_scan_partitions. We are only interested * in the other openers. */ - max_count = device->bdev ? 0 : -1; - open_count = (int) atomic_read(&device->open_count); - if (open_count > max_count) { - if (open_count > 0) - printk (KERN_WARNING "Can't offline dasd device with " - "open count = %i.\n", - open_count); - else - printk (KERN_WARNING "%s", - "Can't offline dasd device due to internal " - "use\n"); - clear_bit(DASD_FLAG_OFFLINE, &device->flags); - dasd_put_device(device); - return -EBUSY; + if (device->block) { + struct dasd_block *block = device->block; + max_count = block->bdev ? 0 : -1; + open_count = (int) atomic_read(&block->open_count); + if (open_count > max_count) { + if (open_count > 0) + printk(KERN_WARNING "Can't offline dasd " + "device with open count = %i.\n", + open_count); + else + printk(KERN_WARNING "%s", + "Can't offline dasd device due " + "to internal use\n"); + clear_bit(DASD_FLAG_OFFLINE, &device->flags); + dasd_put_device(device); + return -EBUSY; + } } dasd_set_target_state(device, DASD_STATE_NEW); /* dasd_delete_device destroys the device reference. */ + block = device->block; + device->block = NULL; dasd_delete_device(device); - + /* + * life cycle of block is bound to device, so delete it after + * device was safely removed + */ + if (block) + dasd_free_block(block); return 0; } -int -dasd_generic_notify(struct ccw_device *cdev, int event) +int dasd_generic_notify(struct ccw_device *cdev, int event) { struct dasd_device *device; struct dasd_ccw_req *cqr; @@ -2146,27 +2353,22 @@ dasd_generic_notify(struct ccw_device *cdev, int event) if (device->state < DASD_STATE_BASIC) break; /* Device is active. We want to keep it. */ - if (test_bit(DASD_FLAG_DSC_ERROR, &device->flags)) { - list_for_each_entry(cqr, &device->ccw_queue, list) - if (cqr->status == DASD_CQR_IN_IO) - cqr->status = DASD_CQR_FAILED; - device->stopped |= DASD_STOPPED_DC_EIO; - } else { - list_for_each_entry(cqr, &device->ccw_queue, list) - if (cqr->status == DASD_CQR_IN_IO) { - cqr->status = DASD_CQR_QUEUED; - cqr->retries++; - } - device->stopped |= DASD_STOPPED_DC_WAIT; - dasd_set_timer(device, 0); - } - dasd_schedule_bh(device); + list_for_each_entry(cqr, &device->ccw_queue, devlist) + if (cqr->status == DASD_CQR_IN_IO) { + cqr->status = DASD_CQR_QUEUED; + cqr->retries++; + } + device->stopped |= DASD_STOPPED_DC_WAIT; + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); ret = 1; break; case CIO_OPER: /* FIXME: add a sanity check. */ - device->stopped &= ~(DASD_STOPPED_DC_WAIT|DASD_STOPPED_DC_EIO); - dasd_schedule_bh(device); + device->stopped &= ~DASD_STOPPED_DC_WAIT; + dasd_schedule_device_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); ret = 1; break; } @@ -2196,7 +2398,8 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, ccw->cda = (__u32)(addr_t)rdc_buffer; ccw->count = rdc_buffer_size; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; cqr->expires = 10*HZ; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); cqr->retries = 2; @@ -2218,13 +2421,12 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic, return PTR_ERR(cqr); ret = dasd_sleep_on(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return ret; } EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars); -static int __init -dasd_init(void) +static int __init dasd_init(void) { int rc; @@ -2232,7 +2434,7 @@ dasd_init(void) init_waitqueue_head(&dasd_flush_wq); /* register 'common' DASD debug area, used for all DBF_XXX calls */ - dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof (long)); + dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof(long)); if (dasd_debug_area == NULL) { rc = -ENOMEM; goto failed; @@ -2278,15 +2480,18 @@ EXPORT_SYMBOL(dasd_diag_discipline_pointer); EXPORT_SYMBOL(dasd_add_request_head); EXPORT_SYMBOL(dasd_add_request_tail); EXPORT_SYMBOL(dasd_cancel_req); -EXPORT_SYMBOL(dasd_clear_timer); +EXPORT_SYMBOL(dasd_device_clear_timer); +EXPORT_SYMBOL(dasd_block_clear_timer); EXPORT_SYMBOL(dasd_enable_device); EXPORT_SYMBOL(dasd_int_handler); EXPORT_SYMBOL(dasd_kfree_request); EXPORT_SYMBOL(dasd_kick_device); EXPORT_SYMBOL(dasd_kmalloc_request); -EXPORT_SYMBOL(dasd_schedule_bh); +EXPORT_SYMBOL(dasd_schedule_device_bh); +EXPORT_SYMBOL(dasd_schedule_block_bh); EXPORT_SYMBOL(dasd_set_target_state); -EXPORT_SYMBOL(dasd_set_timer); +EXPORT_SYMBOL(dasd_device_set_timer); +EXPORT_SYMBOL(dasd_block_set_timer); EXPORT_SYMBOL(dasd_sfree_request); EXPORT_SYMBOL(dasd_sleep_on); EXPORT_SYMBOL(dasd_sleep_on_immediatly); @@ -2300,4 +2505,7 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove); EXPORT_SYMBOL_GPL(dasd_generic_notify); EXPORT_SYMBOL_GPL(dasd_generic_set_online); EXPORT_SYMBOL_GPL(dasd_generic_set_offline); - +EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change); +EXPORT_SYMBOL_GPL(dasd_flush_device_queue); +EXPORT_SYMBOL_GPL(dasd_alloc_block); +EXPORT_SYMBOL_GPL(dasd_free_block); diff --git a/drivers/s390/block/dasd_3370_erp.c b/drivers/s390/block/dasd_3370_erp.c deleted file mode 100644 index 1ddab89..0000000 --- a/drivers/s390/block/dasd_3370_erp.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * File...........: linux/drivers/s390/block/dasd_3370_erp.c - * Author(s)......: Holger Smolinski - * Bugreports.to..: - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000 - * - */ - -#define PRINTK_HEADER "dasd_erp(3370)" - -#include "dasd_int.h" - - -/* - * DASD_3370_ERP_EXAMINE - * - * DESCRIPTION - * Checks only for fatal/no/recover error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * The logic is based on the 'IBM 3880 Storage Control Reference' manual - * 'Chapter 7. 3370 Sense Data'. - * - * RETURN VALUES - * dasd_era_none no error - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for all others. - */ -dasd_era_t -dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) -{ - char *sense = irb->ecw; - - /* check for successful execution first */ - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - if (sense[0] & 0x80) { /* CMD reject */ - return dasd_era_fatal; - } - if (sense[0] & 0x40) { /* Drive offline */ - return dasd_era_recover; - } - if (sense[0] & 0x20) { /* Bus out parity */ - return dasd_era_recover; - } - if (sense[0] & 0x10) { /* equipment check */ - if (sense[1] & 0x80) { - return dasd_era_fatal; - } - return dasd_era_recover; - } - if (sense[0] & 0x08) { /* data check */ - if (sense[1] & 0x80) { - return dasd_era_fatal; - } - return dasd_era_recover; - } - if (sense[0] & 0x04) { /* overrun */ - if (sense[1] & 0x80) { - return dasd_era_fatal; - } - return dasd_era_recover; - } - if (sense[1] & 0x40) { /* invalid blocksize */ - return dasd_era_fatal; - } - if (sense[1] & 0x04) { /* file protected */ - return dasd_era_recover; - } - if (sense[1] & 0x01) { /* operation incomplete */ - return dasd_era_recover; - } - if (sense[2] & 0x80) { /* check data erroor */ - return dasd_era_recover; - } - if (sense[2] & 0x10) { /* Env. data present */ - return dasd_era_recover; - } - /* examine the 24 byte sense data */ - return dasd_era_recover; - -} /* END dasd_3370_erp_examine */ diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index c02f960..c361ab6 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -26,158 +26,6 @@ struct DCTL_data { /* ***************************************************************************** - * SECTION ERP EXAMINATION - ***************************************************************************** - */ - -/* - * DASD_3990_ERP_EXAMINE_24 - * - * DESCRIPTION - * Checks only for fatal (unrecoverable) error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * Each bit configuration leading to an action code 2 (Exit with - * programming error or unusual condition indication) - * are handled as fatal errors. - * - * All other configurations are handled as recoverable errors. - * - * RETURN VALUES - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for all others. - */ -static dasd_era_t -dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense) -{ - - struct dasd_device *device = cqr->device; - - /* check for 'Command Reject' */ - if ((sense[0] & SNS0_CMD_REJECT) && - (!(sense[2] & SNS2_ENV_DATA_PRESENT))) { - - DEV_MESSAGE(KERN_ERR, device, "%s", - "EXAMINE 24: Command Reject detected - " - "fatal error"); - - return dasd_era_fatal; - } - - /* check for 'Invalid Track Format' */ - if ((sense[1] & SNS1_INV_TRACK_FORMAT) && - (!(sense[2] & SNS2_ENV_DATA_PRESENT))) { - - DEV_MESSAGE(KERN_ERR, device, "%s", - "EXAMINE 24: Invalid Track Format detected " - "- fatal error"); - - return dasd_era_fatal; - } - - /* check for 'No Record Found' */ - if (sense[1] & SNS1_NO_REC_FOUND) { - - /* FIXME: fatal error ?!? */ - DEV_MESSAGE(KERN_ERR, device, - "EXAMINE 24: No Record Found detected %s", - device->state <= DASD_STATE_BASIC ? - " " : "- fatal error"); - - return dasd_era_fatal; - } - - /* return recoverable for all others */ - return dasd_era_recover; -} /* END dasd_3990_erp_examine_24 */ - -/* - * DASD_3990_ERP_EXAMINE_32 - * - * DESCRIPTION - * Checks only for fatal/no/recoverable error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * RETURN VALUES - * dasd_era_none no error - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for recoverable others. - */ -static dasd_era_t -dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense) -{ - - struct dasd_device *device = cqr->device; - - switch (sense[25]) { - case 0x00: - return dasd_era_none; - - case 0x01: - DEV_MESSAGE(KERN_ERR, device, "%s", "EXAMINE 32: fatal error"); - - return dasd_era_fatal; - - default: - - return dasd_era_recover; - } - -} /* end dasd_3990_erp_examine_32 */ - -/* - * DASD_3990_ERP_EXAMINE - * - * DESCRIPTION - * Checks only for fatal/no/recover error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * The logic is based on the 'IBM 3990 Storage Control Reference' manual - * 'Chapter 7. Error Recovery Procedures'. - * - * RETURN VALUES - * dasd_era_none no error - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for all others. - */ -dasd_era_t -dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) -{ - - char *sense = irb->ecw; - dasd_era_t era = dasd_era_recover; - struct dasd_device *device = cqr->device; - - /* check for successful execution first */ - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - /* distinguish between 24 and 32 byte sense data */ - if (sense[27] & DASD_SENSE_BIT_0) { - - era = dasd_3990_erp_examine_24(cqr, sense); - - } else { - - era = dasd_3990_erp_examine_32(cqr, sense); - - } - - /* log the erp chain if fatal error occurred */ - if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY)) { - dasd_log_sense(cqr, irb); - } - - return era; - -} /* END dasd_3990_erp_examine */ - -/* - ***************************************************************************** * SECTION ERP HANDLING ***************************************************************************** */ @@ -206,7 +54,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) { struct dasd_ccw_req *cqr = erp->refers; - dasd_free_erp_request(erp, erp->device); + dasd_free_erp_request(erp, erp->memdev); cqr->status = final_status; return cqr; @@ -224,15 +72,17 @@ static void dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; + unsigned long flags; DEV_MESSAGE(KERN_INFO, device, "blocking request queue for %is", expires/HZ); + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); device->stopped |= DASD_STOPPED_PENDING; - erp->status = DASD_CQR_QUEUED; - - dasd_set_timer(device, expires); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + erp->status = DASD_CQR_FILLED; + dasd_block_set_timer(device->block, expires); } /* @@ -251,7 +101,7 @@ static struct dasd_ccw_req * dasd_3990_erp_int_req(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; /* first time set initial retry counter and erp_function */ /* and retry once without blocking queue */ @@ -292,11 +142,14 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp) static void dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; __u8 opm; + unsigned long flags; /* try alternate valid path */ + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); opm = ccw_device_get_path_mask(device->cdev); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); //FIXME: start with get_opm ? if (erp->lpm == 0) erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum); @@ -309,9 +162,8 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) "try alternate lpm=%x (lpum=%x / opm=%x)", erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm); - /* reset status to queued to handle the request again... */ - if (erp->status > DASD_CQR_QUEUED) - erp->status = DASD_CQR_QUEUED; + /* reset status to submit the request again... */ + erp->status = DASD_CQR_FILLED; erp->retries = 1; } else { DEV_MESSAGE(KERN_ERR, device, @@ -320,8 +172,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) erp->irb.esw.esw0.sublog.lpum, opm); /* post request with permanent error */ - if (erp->status > DASD_CQR_QUEUED) - erp->status = DASD_CQR_FAILED; + erp->status = DASD_CQR_FAILED; } } /* end dasd_3990_erp_alternate_path */ @@ -344,14 +195,14 @@ static struct dasd_ccw_req * dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; struct DCTL_data *DCTL_data; struct ccw1 *ccw; struct dasd_ccw_req *dctl_cqr; dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1, - sizeof (struct DCTL_data), - erp->device); + sizeof(struct DCTL_data), + device); if (IS_ERR(dctl_cqr)) { DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate DCTL-CQR"); @@ -365,13 +216,14 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) DCTL_data->modifier = modifier; ccw = dctl_cqr->cpaddr; - memset(ccw, 0, sizeof (struct ccw1)); + memset(ccw, 0, sizeof(struct ccw1)); ccw->cmd_code = CCW_CMD_DCTL; ccw->count = 4; ccw->cda = (__u32)(addr_t) DCTL_data; dctl_cqr->function = dasd_3990_erp_DCTL; dctl_cqr->refers = erp; - dctl_cqr->device = erp->device; + dctl_cqr->startdev = device; + dctl_cqr->memdev = device; dctl_cqr->magic = erp->magic; dctl_cqr->expires = 5 * 60 * HZ; dctl_cqr->retries = 2; @@ -435,7 +287,7 @@ static struct dasd_ccw_req * dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; /* first time set initial retry counter and erp_function */ /* and retry once without waiting for state change pending */ @@ -472,7 +324,7 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) "redriving request immediately, " "%d retries left", erp->retries); - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; } } @@ -530,7 +382,7 @@ static void dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; char msg_format = (sense[7] & 0xF0); char msg_no = (sense[7] & 0x0F); @@ -1157,7 +1009,7 @@ static struct dasd_ccw_req * dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_com_rej; @@ -1198,7 +1050,7 @@ static struct dasd_ccw_req * dasd_3990_erp_bus_out(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; /* first time set initial retry counter and erp_function */ /* and retry once without blocking queue */ @@ -1237,7 +1089,7 @@ static struct dasd_ccw_req * dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_equip_check; @@ -1279,7 +1131,6 @@ dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) erp = dasd_3990_erp_action_5(erp); } - return erp; } /* end dasd_3990_erp_equip_check */ @@ -1299,7 +1150,7 @@ static struct dasd_ccw_req * dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_data_check; @@ -1358,7 +1209,7 @@ static struct dasd_ccw_req * dasd_3990_erp_overrun(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_overrun; @@ -1387,7 +1238,7 @@ static struct dasd_ccw_req * dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_inv_format; @@ -1403,8 +1254,7 @@ dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) } else { DEV_MESSAGE(KERN_ERR, device, "%s", - "Invalid Track Format - Fatal error should have " - "been handled within the interrupt handler"); + "Invalid Track Format - Fatal error"); erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); } @@ -1428,7 +1278,7 @@ static struct dasd_ccw_req * dasd_3990_erp_EOC(struct dasd_ccw_req * default_erp, char *sense) { - struct dasd_device *device = default_erp->device; + struct dasd_device *device = default_erp->startdev; DEV_MESSAGE(KERN_ERR, device, "%s", "End-of-Cylinder - must never happen"); @@ -1453,7 +1303,7 @@ static struct dasd_ccw_req * dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_env_data; @@ -1463,11 +1313,9 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) /* don't retry on disabled interface */ if (sense[7] != 0x0F) { - erp = dasd_3990_erp_action_4(erp, sense); } else { - - erp = dasd_3990_erp_cleanup(erp, DASD_CQR_IN_IO); + erp->status = DASD_CQR_FILLED; } return erp; @@ -1490,11 +1338,10 @@ static struct dasd_ccw_req * dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense) { - struct dasd_device *device = default_erp->device; + struct dasd_device *device = default_erp->startdev; DEV_MESSAGE(KERN_ERR, device, "%s", - "No Record Found - Fatal error should " - "have been handled within the interrupt handler"); + "No Record Found - Fatal error "); return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); @@ -1517,7 +1364,7 @@ static struct dasd_ccw_req * dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; DEV_MESSAGE(KERN_ERR, device, "%s", "File Protected"); @@ -1526,6 +1373,43 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) } /* end dasd_3990_erp_file_prot */ /* + * DASD_3990_ERP_INSPECT_ALIAS + * + * DESCRIPTION + * Checks if the original request was started on an alias device. + * If yes, it modifies the original and the erp request so that + * the erp request can be started on a base device. + * + * PARAMETER + * erp pointer to the currently created default ERP + * + * RETURN VALUES + * erp pointer to the modified ERP, or NULL + */ + +static struct dasd_ccw_req *dasd_3990_erp_inspect_alias( + struct dasd_ccw_req *erp) +{ + struct dasd_ccw_req *cqr = erp->refers; + + if (cqr->block && + (cqr->block->base != cqr->startdev)) { + if (cqr->startdev->features & DASD_FEATURE_ERPLOG) { + DEV_MESSAGE(KERN_ERR, cqr->startdev, + "ERP on alias device for request %p," + " recover on base device %s", cqr, + cqr->block->base->cdev->dev.bus_id); + } + dasd_eckd_reset_ccw_to_base_io(cqr); + erp->startdev = cqr->block->base; + erp->function = dasd_3990_erp_inspect_alias; + return erp; + } else + return NULL; +} + + +/* * DASD_3990_ERP_INSPECT_24 * * DESCRIPTION @@ -1623,7 +1507,7 @@ static struct dasd_ccw_req * dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->retries = 256; erp->function = dasd_3990_erp_action_10_32; @@ -1657,13 +1541,14 @@ static struct dasd_ccw_req * dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) { - struct dasd_device *device = default_erp->device; + struct dasd_device *device = default_erp->startdev; __u32 cpa = 0; struct dasd_ccw_req *cqr; struct dasd_ccw_req *erp; struct DE_eckd_data *DE_data; + struct PFX_eckd_data *PFX_data; char *LO_data; /* LO_eckd_data_t */ - struct ccw1 *ccw; + struct ccw1 *ccw, *oldccw; DEV_MESSAGE(KERN_DEBUG, device, "%s", "Write not finished because of unexpected condition"); @@ -1702,8 +1587,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* Build new ERP request including DE/LO */ erp = dasd_alloc_erp_request((char *) &cqr->magic, 2 + 1,/* DE/LO + TIC */ - sizeof (struct DE_eckd_data) + - sizeof (struct LO_eckd_data), device); + sizeof(struct DE_eckd_data) + + sizeof(struct LO_eckd_data), device); if (IS_ERR(erp)) { DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate ERP"); @@ -1712,10 +1597,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* use original DE */ DE_data = erp->data; - memcpy(DE_data, cqr->data, sizeof (struct DE_eckd_data)); + oldccw = cqr->cpaddr; + if (oldccw->cmd_code == DASD_ECKD_CCW_PFX) { + PFX_data = cqr->data; + memcpy(DE_data, &PFX_data->define_extend, + sizeof(struct DE_eckd_data)); + } else + memcpy(DE_data, cqr->data, sizeof(struct DE_eckd_data)); /* create LO */ - LO_data = erp->data + sizeof (struct DE_eckd_data); + LO_data = erp->data + sizeof(struct DE_eckd_data); if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) { @@ -1748,7 +1639,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* create DE ccw */ ccw = erp->cpaddr; - memset(ccw, 0, sizeof (struct ccw1)); + memset(ccw, 0, sizeof(struct ccw1)); ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; ccw->flags = CCW_FLAG_CC; ccw->count = 16; @@ -1756,7 +1647,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* create LO ccw */ ccw++; - memset(ccw, 0, sizeof (struct ccw1)); + memset(ccw, 0, sizeof(struct ccw1)); ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; ccw->flags = CCW_FLAG_CC; ccw->count = 16; @@ -1770,7 +1661,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* fill erp related fields */ erp->function = dasd_3990_erp_action_1B_32; erp->refers = default_erp->refers; - erp->device = device; + erp->startdev = device; + erp->memdev = device; erp->magic = default_erp->magic; erp->expires = 0; erp->retries = 256; @@ -1803,7 +1695,7 @@ static struct dasd_ccw_req * dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) { - struct dasd_device *device = previous_erp->device; + struct dasd_device *device = previous_erp->startdev; __u32 cpa = 0; struct dasd_ccw_req *cqr; struct dasd_ccw_req *erp; @@ -1827,7 +1719,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) DEV_MESSAGE(KERN_DEBUG, device, "%s", "Imprecise ending is set - just retry"); - previous_erp->status = DASD_CQR_QUEUED; + previous_erp->status = DASD_CQR_FILLED; return previous_erp; } @@ -1850,7 +1742,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) erp = previous_erp; /* update the LO with the new returned sense data */ - LO_data = erp->data + sizeof (struct DE_eckd_data); + LO_data = erp->data + sizeof(struct DE_eckd_data); if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) { @@ -1889,7 +1781,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) ccw++; /* addr of TIC ccw */ ccw->cda = cpa; - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; return erp; @@ -1968,9 +1860,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) * try further actions. */ erp->lpm = 0; - - erp->status = DASD_CQR_ERROR; - + erp->status = DASD_CQR_NEED_ERP; } } @@ -2047,7 +1937,7 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense) if ((sense[25] & DASD_SENSE_BIT_1) && (sense[26] & DASD_SENSE_BIT_2)) { /* set to suspended duplex state then restart */ - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; DEV_MESSAGE(KERN_ERR, device, "%s", "Set device to suspended duplex state should be " @@ -2081,28 +1971,26 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense) { if ((erp->function == dasd_3990_erp_compound_retry) && - (erp->status == DASD_CQR_ERROR)) { + (erp->status == DASD_CQR_NEED_ERP)) { dasd_3990_erp_compound_path(erp, sense); } if ((erp->function == dasd_3990_erp_compound_path) && - (erp->status == DASD_CQR_ERROR)) { + (erp->status == DASD_CQR_NEED_ERP)) { erp = dasd_3990_erp_compound_code(erp, sense); } if ((erp->function == dasd_3990_erp_compound_code) && - (erp->status == DASD_CQR_ERROR)) { + (erp->status == DASD_CQR_NEED_ERP)) { dasd_3990_erp_compound_config(erp, sense); } /* if no compound action ERP specified, the request failed */ - if (erp->status == DASD_CQR_ERROR) { - + if (erp->status == DASD_CQR_NEED_ERP) erp->status = DASD_CQR_FAILED; - } return erp; @@ -2127,7 +2015,7 @@ static struct dasd_ccw_req * dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; erp->function = dasd_3990_erp_inspect_32; @@ -2149,8 +2037,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) case 0x01: /* fatal error */ DEV_MESSAGE(KERN_ERR, device, "%s", - "Fatal error should have been " - "handled within the interrupt handler"); + "Retry not recommended - Fatal error"); erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); break; @@ -2253,6 +2140,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) /* already set up new ERP ! */ char *sense = erp->refers->irb.ecw; + /* if this problem occured on an alias retry on base */ + erp_new = dasd_3990_erp_inspect_alias(erp); + if (erp_new) + return erp_new; + /* distinguish between 24 and 32 byte sense data */ if (sense[27] & DASD_SENSE_BIT_0) { @@ -2287,13 +2179,13 @@ static struct dasd_ccw_req * dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) { - struct dasd_device *device = cqr->device; + struct dasd_device *device = cqr->startdev; struct ccw1 *ccw; /* allocate additional request block */ struct dasd_ccw_req *erp; - erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, cqr->device); + erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, device); if (IS_ERR(erp)) { if (cqr->retries <= 0) { DEV_MESSAGE(KERN_ERR, device, "%s", @@ -2305,7 +2197,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) "Unable to allocate ERP request " "(%i retries left)", cqr->retries); - dasd_set_timer(device, (HZ << 3)); + dasd_block_set_timer(device->block, (HZ << 3)); } return cqr; } @@ -2319,7 +2211,9 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) ccw->cda = (long)(cqr->cpaddr); erp->function = dasd_3990_erp_add_erp; erp->refers = cqr; - erp->device = cqr->device; + erp->startdev = device; + erp->memdev = device; + erp->block = cqr->block; erp->magic = cqr->magic; erp->expires = 0; erp->retries = 256; @@ -2466,7 +2360,7 @@ static struct dasd_ccw_req * dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) { - struct dasd_device *device = erp->device; + struct dasd_device *device = erp->startdev; char *sense = erp->irb.ecw; /* check for 24 byte sense ERP */ @@ -2557,7 +2451,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, struct dasd_ccw_req *erp) { - struct dasd_device *device = erp_head->device; + struct dasd_device *device = erp_head->startdev; struct dasd_ccw_req *erp_done = erp_head; /* finished req */ struct dasd_ccw_req *erp_free = NULL; /* req to be freed */ @@ -2569,13 +2463,13 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, "original request was lost\n"); /* remove the request from the device queue */ - list_del(&erp_done->list); + list_del(&erp_done->blocklist); erp_free = erp_done; erp_done = erp_done->refers; /* free the finished erp request */ - dasd_free_erp_request(erp_free, erp_free->device); + dasd_free_erp_request(erp_free, erp_free->memdev); } /* end while */ @@ -2603,7 +2497,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, erp->retries, erp); /* handle the request again... */ - erp->status = DASD_CQR_QUEUED; + erp->status = DASD_CQR_FILLED; } } else { @@ -2636,9 +2530,8 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, struct dasd_ccw_req * dasd_3990_erp_action(struct dasd_ccw_req * cqr) { - struct dasd_ccw_req *erp = NULL; - struct dasd_device *device = cqr->device; + struct dasd_device *device = cqr->startdev; struct dasd_ccw_req *temp_erp = NULL; if (device->features & DASD_FEATURE_ERPLOG) { @@ -2704,10 +2597,11 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) } } - /* enqueue added ERP request */ - if (erp->status == DASD_CQR_FILLED) { - erp->status = DASD_CQR_QUEUED; - list_add(&erp->list, &device->ccw_queue); + /* enqueue ERP request if it's a new one */ + if (list_empty(&erp->blocklist)) { + cqr->status = DASD_CQR_IN_ERP; + /* add erp request before the cqr */ + list_add_tail(&erp->blocklist, &cqr->blocklist); } return erp; diff --git a/drivers/s390/block/dasd_9336_erp.c b/drivers/s390/block/dasd_9336_erp.c deleted file mode 100644 index 6e08268..0000000 --- a/drivers/s390/block/dasd_9336_erp.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * File...........: linux/drivers/s390/block/dasd_9336_erp.c - * Author(s)......: Holger Smolinski - * Bugreports.to..: - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000 - * - */ - -#define PRINTK_HEADER "dasd_erp(9336)" - -#include "dasd_int.h" - - -/* - * DASD_9336_ERP_EXAMINE - * - * DESCRIPTION - * Checks only for fatal/no/recover error. - * A detailed examination of the sense data is done later outside - * the interrupt handler. - * - * The logic is based on the 'IBM 3880 Storage Control Reference' manual - * 'Chapter 7. 9336 Sense Data'. - * - * RETURN VALUES - * dasd_era_none no error - * dasd_era_fatal for all fatal (unrecoverable errors) - * dasd_era_recover for all others. - */ -dasd_era_t -dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) -{ - /* check for successful execution first */ - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - /* examine the 24 byte sense data */ - return dasd_era_recover; - -} /* END dasd_9336_erp_examine */ diff --git a/drivers/s390/block/dasd_9343_erp.c b/drivers/s390/block/dasd_9343_erp.c deleted file mode 100644 index ddecb98..0000000 --- a/drivers/s390/block/dasd_9343_erp.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * File...........: linux/drivers/s390/block/dasd_9345_erp.c - * Author(s)......: Holger Smolinski - * Bugreports.to..: - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000 - * - */ - -#define PRINTK_HEADER "dasd_erp(9343)" - -#include "dasd_int.h" - -dasd_era_t -dasd_9343_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) -{ - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - return dasd_era_recover; -} diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c new file mode 100644 index 0000000..3a40bee --- /dev/null +++ b/drivers/s390/block/dasd_alias.c @@ -0,0 +1,903 @@ +/* + * PAV alias management for the DASD ECKD discipline + * + * Copyright IBM Corporation, 2007 + * Author(s): Stefan Weinhuber + */ + +#include +#include +#include "dasd_int.h" +#include "dasd_eckd.h" + +#ifdef PRINTK_HEADER +#undef PRINTK_HEADER +#endif /* PRINTK_HEADER */ +#define PRINTK_HEADER "dasd(eckd):" + + +/* + * General concept of alias management: + * - PAV and DASD alias management is specific to the eckd discipline. + * - A device is connected to an lcu as long as the device exists. + * dasd_alias_make_device_known_to_lcu will be called wenn the + * device is checked by the eckd discipline and + * dasd_alias_disconnect_device_from_lcu will be called + * before the device is deleted. + * - The dasd_alias_add_device / dasd_alias_remove_device + * functions mark the point when a device is 'ready for service'. + * - A summary unit check is a rare occasion, but it is mandatory to + * support it. It requires some complex recovery actions before the + * devices can be used again (see dasd_alias_handle_summary_unit_check). + * - dasd_alias_get_start_dev will find an alias device that can be used + * instead of the base device and does some (very simple) load balancing. + * This is the function that gets called for each I/O, so when improving + * something, this function should get faster or better, the rest has just + * to be correct. + */ + + +static void summary_unit_check_handling_work(struct work_struct *); +static void lcu_update_work(struct work_struct *); +static int _schedule_lcu_update(struct alias_lcu *, struct dasd_device *); + +static struct alias_root aliastree = { + .serverlist = LIST_HEAD_INIT(aliastree.serverlist), + .lock = __SPIN_LOCK_UNLOCKED(aliastree.lock), +}; + +static struct alias_server *_find_server(struct dasd_uid *uid) +{ + struct alias_server *pos; + list_for_each_entry(pos, &aliastree.serverlist, server) { + if (!strncmp(pos->uid.vendor, uid->vendor, + sizeof(uid->vendor)) + && !strncmp(pos->uid.serial, uid->serial, + sizeof(uid->serial))) + return pos; + }; + return NULL; +} + +static struct alias_lcu *_find_lcu(struct alias_server *server, + struct dasd_uid *uid) +{ + struct alias_lcu *pos; + list_for_each_entry(pos, &server->lculist, lcu) { + if (pos->uid.ssid == uid->ssid) + return pos; + }; + return NULL; +} + +static struct alias_pav_group *_find_group(struct alias_lcu *lcu, + struct dasd_uid *uid) +{ + struct alias_pav_group *pos; + __u8 search_unit_addr; + + /* for hyper pav there is only one group */ + if (lcu->pav == HYPER_PAV) { + if (list_empty(&lcu->grouplist)) + return NULL; + else + return list_first_entry(&lcu->grouplist, + struct alias_pav_group, group); + } + + /* for base pav we have to find the group that matches the base */ + if (uid->type == UA_BASE_DEVICE) + search_unit_addr = uid->real_unit_addr; + else + search_unit_addr = uid->base_unit_addr; + list_for_each_entry(pos, &lcu->grouplist, group) { + if (pos->uid.base_unit_addr == search_unit_addr) + return pos; + }; + return NULL; +} + +static struct alias_server *_allocate_server(struct dasd_uid *uid) +{ + struct alias_server *server; + + server = kzalloc(sizeof(*server), GFP_KERNEL); + if (!server) + return ERR_PTR(-ENOMEM); + memcpy(server->uid.vendor, uid->vendor, sizeof(uid->vendor)); + memcpy(server->uid.serial, uid->serial, sizeof(uid->serial)); + INIT_LIST_HEAD(&server->server); + INIT_LIST_HEAD(&server->lculist); + return server; +} + +static void _free_server(struct alias_server *server) +{ + kfree(server); +} + +static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid) +{ + struct alias_lcu *lcu; + + lcu = kzalloc(sizeof(*lcu), GFP_KERNEL); + if (!lcu) + return ERR_PTR(-ENOMEM); + lcu->uac = kzalloc(sizeof(*(lcu->uac)), GFP_KERNEL | GFP_DMA); + if (!lcu->uac) + goto out_err1; + lcu->rsu_cqr = kzalloc(sizeof(*lcu->rsu_cqr), GFP_KERNEL | GFP_DMA); + if (!lcu->rsu_cqr) + goto out_err2; + lcu->rsu_cqr->cpaddr = kzalloc(sizeof(struct ccw1), + GFP_KERNEL | GFP_DMA); + if (!lcu->rsu_cqr->cpaddr) + goto out_err3; + lcu->rsu_cqr->data = kzalloc(16, GFP_KERNEL | GFP_DMA); + if (!lcu->rsu_cqr->data) + goto out_err4; + + memcpy(lcu->uid.vendor, uid->vendor, sizeof(uid->vendor)); + memcpy(lcu->uid.serial, uid->serial, sizeof(uid->serial)); + lcu->uid.ssid = uid->ssid; + lcu->pav = NO_PAV; + lcu->flags = NEED_UAC_UPDATE | UPDATE_PENDING; + INIT_LIST_HEAD(&lcu->lcu); + INIT_LIST_HEAD(&lcu->inactive_devices); + INIT_LIST_HEAD(&lcu->active_devices); + INIT_LIST_HEAD(&lcu->grouplist); + INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work); + INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work); + spin_lock_init(&lcu->lock); + return lcu; + +out_err4: + kfree(lcu->rsu_cqr->cpaddr); +out_err3: + kfree(lcu->rsu_cqr); +out_err2: + kfree(lcu->uac); +out_err1: + kfree(lcu); + return ERR_PTR(-ENOMEM); +} + +static void _free_lcu(struct alias_lcu *lcu) +{ + kfree(lcu->rsu_cqr->data); + kfree(lcu->rsu_cqr->cpaddr); + kfree(lcu->rsu_cqr); + kfree(lcu->uac); + kfree(lcu); +} + +/* + * This is the function that will allocate all the server and lcu data, + * so this function must be called first for a new device. + * If the return value is 1, the lcu was already known before, if it + * is 0, this is a new lcu. + * Negative return code indicates that something went wrong (e.g. -ENOMEM) + */ +int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + unsigned long flags; + struct alias_server *server, *newserver; + struct alias_lcu *lcu, *newlcu; + int is_lcu_known; + struct dasd_uid *uid; + + private = (struct dasd_eckd_private *) device->private; + uid = &private->uid; + spin_lock_irqsave(&aliastree.lock, flags); + is_lcu_known = 1; + server = _find_server(uid); + if (!server) { + spin_unlock_irqrestore(&aliastree.lock, flags); + newserver = _allocate_server(uid); + if (IS_ERR(newserver)) + return PTR_ERR(newserver); + spin_lock_irqsave(&aliastree.lock, flags); + server = _find_server(uid); + if (!server) { + list_add(&newserver->server, &aliastree.serverlist); + server = newserver; + is_lcu_known = 0; + } else { + /* someone was faster */ + _free_server(newserver); + } + } + + lcu = _find_lcu(server, uid); + if (!lcu) { + spin_unlock_irqrestore(&aliastree.lock, flags); + newlcu = _allocate_lcu(uid); + if (IS_ERR(newlcu)) + return PTR_ERR(lcu); + spin_lock_irqsave(&aliastree.lock, flags); + lcu = _find_lcu(server, uid); + if (!lcu) { + list_add(&newlcu->lcu, &server->lculist); + lcu = newlcu; + is_lcu_known = 0; + } else { + /* someone was faster */ + _free_lcu(newlcu); + } + is_lcu_known = 0; + } + spin_lock(&lcu->lock); + list_add(&device->alias_list, &lcu->inactive_devices); + private->lcu = lcu; + spin_unlock(&lcu->lock); + spin_unlock_irqrestore(&aliastree.lock, flags); + + return is_lcu_known; +} + +/* + * This function removes a device from the scope of alias management. + * The complicated part is to make sure that it is not in use by + * any of the workers. If necessary cancel the work. + */ +void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + unsigned long flags; + struct alias_lcu *lcu; + struct alias_server *server; + int was_pending; + + private = (struct dasd_eckd_private *) device->private; + lcu = private->lcu; + spin_lock_irqsave(&lcu->lock, flags); + list_del_init(&device->alias_list); + /* make sure that the workers don't use this device */ + if (device == lcu->suc_data.device) { + spin_unlock_irqrestore(&lcu->lock, flags); + cancel_work_sync(&lcu->suc_data.worker); + spin_lock_irqsave(&lcu->lock, flags); + if (device == lcu->suc_data.device) + lcu->suc_data.device = NULL; + } + was_pending = 0; + if (device == lcu->ruac_data.device) { + spin_unlock_irqrestore(&lcu->lock, flags); + was_pending = 1; + cancel_delayed_work_sync(&lcu->ruac_data.dwork); + spin_lock_irqsave(&lcu->lock, flags); + if (device == lcu->ruac_data.device) + lcu->ruac_data.device = NULL; + } + private->lcu = NULL; + spin_unlock_irqrestore(&lcu->lock, flags); + + spin_lock_irqsave(&aliastree.lock, flags); + spin_lock(&lcu->lock); + if (list_empty(&lcu->grouplist) && + list_empty(&lcu->active_devices) && + list_empty(&lcu->inactive_devices)) { + list_del(&lcu->lcu); + spin_unlock(&lcu->lock); + _free_lcu(lcu); + lcu = NULL; + } else { + if (was_pending) + _schedule_lcu_update(lcu, NULL); + spin_unlock(&lcu->lock); + } + server = _find_server(&private->uid); + if (server && list_empty(&server->lculist)) { + list_del(&server->server); + _free_server(server); + } + spin_unlock_irqrestore(&aliastree.lock, flags); +} + +/* + * This function assumes that the unit address configuration stored + * in the lcu is up to date and will update the device uid before + * adding it to a pav group. + */ +static int _add_device_to_lcu(struct alias_lcu *lcu, + struct dasd_device *device) +{ + + struct dasd_eckd_private *private; + struct alias_pav_group *group; + struct dasd_uid *uid; + + private = (struct dasd_eckd_private *) device->private; + uid = &private->uid; + uid->type = lcu->uac->unit[uid->real_unit_addr].ua_type; + uid->base_unit_addr = lcu->uac->unit[uid->real_unit_addr].base_ua; + dasd_set_uid(device->cdev, &private->uid); + + /* if we have no PAV anyway, we don't need to bother with PAV groups */ + if (lcu->pav == NO_PAV) { + list_move(&device->alias_list, &lcu->active_devices); + return 0; + } + + group = _find_group(lcu, uid); + if (!group) { + group = kzalloc(sizeof(*group), GFP_ATOMIC); + if (!group) + return -ENOMEM; + memcpy(group->uid.vendor, uid->vendor, sizeof(uid->vendor)); + memcpy(group->uid.serial, uid->serial, sizeof(uid->serial)); + group->uid.ssid = uid->ssid; + if (uid->type == UA_BASE_DEVICE) + group->uid.base_unit_addr = uid->real_unit_addr; + else + group->uid.base_unit_addr = uid->base_unit_addr; + INIT_LIST_HEAD(&group->group); + INIT_LIST_HEAD(&group->baselist); + INIT_LIST_HEAD(&group->aliaslist); + list_add(&group->group, &lcu->grouplist); + } + if (uid->type == UA_BASE_DEVICE) + list_move(&device->alias_list, &group->baselist); + else + list_move(&device->alias_list, &group->aliaslist); + private->pavgroup = group; + return 0; +}; + +static void _remove_device_from_lcu(struct alias_lcu *lcu, + struct dasd_device *device) +{ + struct dasd_eckd_private *private; + struct alias_pav_group *group; + + private = (struct dasd_eckd_private *) device->private; + list_move(&device->alias_list, &lcu->inactive_devices); + group = private->pavgroup; + if (!group) + return; + private->pavgroup = NULL; + if (list_empty(&group->baselist) && list_empty(&group->aliaslist)) { + list_del(&group->group); + kfree(group); + return; + } + if (group->next == device) + group->next = NULL; +}; + +static int read_unit_address_configuration(struct dasd_device *device, + struct alias_lcu *lcu) +{ + struct dasd_psf_prssd_data *prssdp; + struct dasd_ccw_req *cqr; + struct ccw1 *ccw; + int rc; + unsigned long flags; + + cqr = dasd_kmalloc_request("ECKD", + 1 /* PSF */ + 1 /* RSSD */ , + (sizeof(struct dasd_psf_prssd_data)), + device); + if (IS_ERR(cqr)) + return PTR_ERR(cqr); + cqr->startdev = device; + cqr->memdev = device; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + cqr->retries = 10; + cqr->expires = 20 * HZ; + + /* Prepare for Read Subsystem Data */ + prssdp = (struct dasd_psf_prssd_data *) cqr->data; + memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); + prssdp->order = PSF_ORDER_PRSSD; + prssdp->suborder = 0x0e; /* Read unit address configuration */ + /* all other bytes of prssdp must be zero */ + + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->count = sizeof(struct dasd_psf_prssd_data); + ccw->flags |= CCW_FLAG_CC; + ccw->cda = (__u32)(addr_t) prssdp; + + /* Read Subsystem Data - feature codes */ + memset(lcu->uac, 0, sizeof(*(lcu->uac))); + + ccw++; + ccw->cmd_code = DASD_ECKD_CCW_RSSD; + ccw->count = sizeof(*(lcu->uac)); + ccw->cda = (__u32)(addr_t) lcu->uac; + + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + + /* need to unset flag here to detect race with summary unit check */ + spin_lock_irqsave(&lcu->lock, flags); + lcu->flags &= ~NEED_UAC_UPDATE; + spin_unlock_irqrestore(&lcu->lock, flags); + + do { + rc = dasd_sleep_on(cqr); + } while (rc && (cqr->retries > 0)); + if (rc) { + spin_lock_irqsave(&lcu->lock, flags); + lcu->flags |= NEED_UAC_UPDATE; + spin_unlock_irqrestore(&lcu->lock, flags); + } + dasd_kfree_request(cqr, cqr->memdev); + return rc; +} + +static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu) +{ + unsigned long flags; + struct alias_pav_group *pavgroup, *tempgroup; + struct dasd_device *device, *tempdev; + int i, rc; + struct dasd_eckd_private *private; + + spin_lock_irqsave(&lcu->lock, flags); + list_for_each_entry_safe(pavgroup, tempgroup, &lcu->grouplist, group) { + list_for_each_entry_safe(device, tempdev, &pavgroup->baselist, + alias_list) { + list_move(&device->alias_list, &lcu->active_devices); + private = (struct dasd_eckd_private *) device->private; + private->pavgroup = NULL; + } + list_for_each_entry_safe(device, tempdev, &pavgroup->aliaslist, + alias_list) { + list_move(&device->alias_list, &lcu->active_devices); + private = (struct dasd_eckd_private *) device->private; + private->pavgroup = NULL; + } + list_del(&pavgroup->group); + kfree(pavgroup); + } + spin_unlock_irqrestore(&lcu->lock, flags); + + rc = read_unit_address_configuration(refdev, lcu); + if (rc) + return rc; + + spin_lock_irqsave(&lcu->lock, flags); + lcu->pav = NO_PAV; + for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) { + switch (lcu->uac->unit[i].ua_type) { + case UA_BASE_PAV_ALIAS: + lcu->pav = BASE_PAV; + break; + case UA_HYPER_PAV_ALIAS: + lcu->pav = HYPER_PAV; + break; + } + if (lcu->pav != NO_PAV) + break; + } + + list_for_each_entry_safe(device, tempdev, &lcu->active_devices, + alias_list) { + _add_device_to_lcu(lcu, device); + } + spin_unlock_irqrestore(&lcu->lock, flags); + return 0; +} + +static void lcu_update_work(struct work_struct *work) +{ + struct alias_lcu *lcu; + struct read_uac_work_data *ruac_data; + struct dasd_device *device; + unsigned long flags; + int rc; + + ruac_data = container_of(work, struct read_uac_work_data, dwork.work); + lcu = container_of(ruac_data, struct alias_lcu, ruac_data); + device = ruac_data->device; + rc = _lcu_update(device, lcu); + /* + * Need to check flags again, as there could have been another + * prepare_update or a new device a new device while we were still + * processing the data + */ + spin_lock_irqsave(&lcu->lock, flags); + if (rc || (lcu->flags & NEED_UAC_UPDATE)) { + DEV_MESSAGE(KERN_WARNING, device, "could not update" + " alias data in lcu (rc = %d), retry later", rc); + schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ); + } else { + lcu->ruac_data.device = NULL; + lcu->flags &= ~UPDATE_PENDING; + } + spin_unlock_irqrestore(&lcu->lock, flags); +} + +static int _schedule_lcu_update(struct alias_lcu *lcu, + struct dasd_device *device) +{ + struct dasd_device *usedev = NULL; + struct alias_pav_group *group; + + lcu->flags |= NEED_UAC_UPDATE; + if (lcu->ruac_data.device) { + /* already scheduled or running */ + return 0; + } + if (device && !list_empty(&device->alias_list)) + usedev = device; + + if (!usedev && !list_empty(&lcu->grouplist)) { + group = list_first_entry(&lcu->grouplist, + struct alias_pav_group, group); + if (!list_empty(&group->baselist)) + usedev = list_first_entry(&group->baselist, + struct dasd_device, + alias_list); + else if (!list_empty(&group->aliaslist)) + usedev = list_first_entry(&group->aliaslist, + struct dasd_device, + alias_list); + } + if (!usedev && !list_empty(&lcu->active_devices)) { + usedev = list_first_entry(&lcu->active_devices, + struct dasd_device, alias_list); + } + /* + * if we haven't found a proper device yet, give up for now, the next + * device that will be set active will trigger an lcu update + */ + if (!usedev) + return -EINVAL; + lcu->ruac_data.device = usedev; + schedule_delayed_work(&lcu->ruac_data.dwork, 0); + return 0; +} + +int dasd_alias_add_device(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + struct alias_lcu *lcu; + unsigned long flags; + int rc; + + private = (struct dasd_eckd_private *) device->private; + lcu = private->lcu; + rc = 0; + spin_lock_irqsave(&lcu->lock, flags); + if (!(lcu->flags & UPDATE_PENDING)) { + rc = _add_device_to_lcu(lcu, device); + if (rc) + lcu->flags |= UPDATE_PENDING; + } + if (lcu->flags & UPDATE_PENDING) { + list_move(&device->alias_list, &lcu->active_devices); + _schedule_lcu_update(lcu, device); + } + spin_unlock_irqrestore(&lcu->lock, flags); + return rc; +} + +int dasd_alias_remove_device(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + struct alias_lcu *lcu; + unsigned long flags; + + private = (struct dasd_eckd_private *) device->private; + lcu = private->lcu; + spin_lock_irqsave(&lcu->lock, flags); + _remove_device_from_lcu(lcu, device); + spin_unlock_irqrestore(&lcu->lock, flags); + return 0; +} + +struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device) +{ + + struct dasd_device *alias_device; + struct alias_pav_group *group; + struct alias_lcu *lcu; + struct dasd_eckd_private *private, *alias_priv; + unsigned long flags; + + private = (struct dasd_eckd_private *) base_device->private; + group = private->pavgroup; + lcu = private->lcu; + if (!group || !lcu) + return NULL; + if (lcu->pav == NO_PAV || + lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING)) + return NULL; + + spin_lock_irqsave(&lcu->lock, flags); + alias_device = group->next; + if (!alias_device) { + if (list_empty(&group->aliaslist)) { + spin_unlock_irqrestore(&lcu->lock, flags); + return NULL; + } else { + alias_device = list_first_entry(&group->aliaslist, + struct dasd_device, + alias_list); + } + } + if (list_is_last(&alias_device->alias_list, &group->aliaslist)) + group->next = list_first_entry(&group->aliaslist, + struct dasd_device, alias_list); + else + group->next = list_first_entry(&alias_device->alias_list, + struct dasd_device, alias_list); + spin_unlock_irqrestore(&lcu->lock, flags); + alias_priv = (struct dasd_eckd_private *) alias_device->private; + if ((alias_priv->count < private->count) && !alias_device->stopped) + return alias_device; + else + return NULL; +} + +/* + * Summary unit check handling depends on the way alias devices + * are handled so it is done here rather then in dasd_eckd.c + */ +static int reset_summary_unit_check(struct alias_lcu *lcu, + struct dasd_device *device, + char reason) +{ + struct dasd_ccw_req *cqr; + int rc = 0; + + cqr = lcu->rsu_cqr; + strncpy((char *) &cqr->magic, "ECKD", 4); + ASCEBC((char *) &cqr->magic, 4); + cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RSCK; + cqr->cpaddr->flags = 0 ; + cqr->cpaddr->count = 16; + cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; + ((char *)cqr->data)[0] = reason; + + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + cqr->retries = 255; /* set retry counter to enable basic ERP */ + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; + cqr->expires = 5 * HZ; + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + + rc = dasd_sleep_on_immediatly(cqr); + return rc; +} + +static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu) +{ + struct alias_pav_group *pavgroup; + struct dasd_device *device; + struct dasd_eckd_private *private; + + /* active and inactive list can contain alias as well as base devices */ + list_for_each_entry(device, &lcu->active_devices, alias_list) { + private = (struct dasd_eckd_private *) device->private; + if (private->uid.type != UA_BASE_DEVICE) + continue; + dasd_schedule_block_bh(device->block); + dasd_schedule_device_bh(device); + } + list_for_each_entry(device, &lcu->inactive_devices, alias_list) { + private = (struct dasd_eckd_private *) device->private; + if (private->uid.type != UA_BASE_DEVICE) + continue; + dasd_schedule_block_bh(device->block); + dasd_schedule_device_bh(device); + } + list_for_each_entry(pavgroup, &lcu->grouplist, group) { + list_for_each_entry(device, &pavgroup->baselist, alias_list) { + dasd_schedule_block_bh(device->block); + dasd_schedule_device_bh(device); + } + } +} + +static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu) +{ + struct alias_pav_group *pavgroup; + struct dasd_device *device, *temp; + struct dasd_eckd_private *private; + int rc; + unsigned long flags; + LIST_HEAD(active); + + /* + * Problem here ist that dasd_flush_device_queue may wait + * for termination of a request to complete. We can't keep + * the lcu lock during that time, so we must assume that + * the lists may have changed. + * Idea: first gather all active alias devices in a separate list, + * then flush the first element of this list unlocked, and afterwards + * check if it is still on the list before moving it to the + * active_devices list. + */ + + spin_lock_irqsave(&lcu->lock, flags); + list_for_each_entry_safe(device, temp, &lcu->active_devices, + alias_list) { + private = (struct dasd_eckd_private *) device->private; + if (private->uid.type == UA_BASE_DEVICE) + continue; + list_move(&device->alias_list, &active); + } + + list_for_each_entry(pavgroup, &lcu->grouplist, group) { + list_splice_init(&pavgroup->aliaslist, &active); + } + while (!list_empty(&active)) { + device = list_first_entry(&active, struct dasd_device, + alias_list); + spin_unlock_irqrestore(&lcu->lock, flags); + rc = dasd_flush_device_queue(device); + spin_lock_irqsave(&lcu->lock, flags); + /* + * only move device around if it wasn't moved away while we + * were waiting for the flush + */ + if (device == list_first_entry(&active, + struct dasd_device, alias_list)) + list_move(&device->alias_list, &lcu->active_devices); + } + spin_unlock_irqrestore(&lcu->lock, flags); +} + +/* + * This function is called in interrupt context, so the + * cdev lock for device is already locked! + */ +static void _stop_all_devices_on_lcu(struct alias_lcu *lcu, + struct dasd_device *device) +{ + struct alias_pav_group *pavgroup; + struct dasd_device *pos; + + list_for_each_entry(pos, &lcu->active_devices, alias_list) { + if (pos != device) + spin_lock(get_ccwdev_lock(pos->cdev)); + pos->stopped |= DASD_STOPPED_SU; + if (pos != device) + spin_unlock(get_ccwdev_lock(pos->cdev)); + } + list_for_each_entry(pos, &lcu->inactive_devices, alias_list) { + if (pos != device) + spin_lock(get_ccwdev_lock(pos->cdev)); + pos->stopped |= DASD_STOPPED_SU; + if (pos != device) + spin_unlock(get_ccwdev_lock(pos->cdev)); + } + list_for_each_entry(pavgroup, &lcu->grouplist, group) { + list_for_each_entry(pos, &pavgroup->baselist, alias_list) { + if (pos != device) + spin_lock(get_ccwdev_lock(pos->cdev)); + pos->stopped |= DASD_STOPPED_SU; + if (pos != device) + spin_unlock(get_ccwdev_lock(pos->cdev)); + } + list_for_each_entry(pos, &pavgroup->aliaslist, alias_list) { + if (pos != device) + spin_lock(get_ccwdev_lock(pos->cdev)); + pos->stopped |= DASD_STOPPED_SU; + if (pos != device) + spin_unlock(get_ccwdev_lock(pos->cdev)); + } + } +} + +static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu) +{ + struct alias_pav_group *pavgroup; + struct dasd_device *device; + unsigned long flags; + + list_for_each_entry(device, &lcu->active_devices, alias_list) { + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~DASD_STOPPED_SU; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + } + + list_for_each_entry(device, &lcu->inactive_devices, alias_list) { + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~DASD_STOPPED_SU; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + } + + list_for_each_entry(pavgroup, &lcu->grouplist, group) { + list_for_each_entry(device, &pavgroup->baselist, alias_list) { + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~DASD_STOPPED_SU; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), + flags); + } + list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~DASD_STOPPED_SU; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), + flags); + } + } +} + +static void summary_unit_check_handling_work(struct work_struct *work) +{ + struct alias_lcu *lcu; + struct summary_unit_check_work_data *suc_data; + unsigned long flags; + struct dasd_device *device; + + suc_data = container_of(work, struct summary_unit_check_work_data, + worker); + lcu = container_of(suc_data, struct alias_lcu, suc_data); + device = suc_data->device; + + /* 1. flush alias devices */ + flush_all_alias_devices_on_lcu(lcu); + + /* 2. reset summary unit check */ + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + reset_summary_unit_check(lcu, device, suc_data->reason); + + spin_lock_irqsave(&lcu->lock, flags); + _unstop_all_devices_on_lcu(lcu); + _restart_all_base_devices_on_lcu(lcu); + /* 3. read new alias configuration */ + _schedule_lcu_update(lcu, device); + lcu->suc_data.device = NULL; + spin_unlock_irqrestore(&lcu->lock, flags); +} + +/* + * note: this will be called from int handler context (cdev locked) + */ +void dasd_alias_handle_summary_unit_check(struct dasd_device *device, + struct irb *irb) +{ + struct alias_lcu *lcu; + char reason; + struct dasd_eckd_private *private; + + private = (struct dasd_eckd_private *) device->private; + + reason = irb->ecw[8]; + DEV_MESSAGE(KERN_WARNING, device, "%s %x", + "eckd handle summary unit check: reason", reason); + + lcu = private->lcu; + if (!lcu) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "device not ready to handle summary" + " unit check (no lcu structure)"); + return; + } + spin_lock(&lcu->lock); + _stop_all_devices_on_lcu(lcu, device); + /* prepare for lcu_update */ + private->lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING; + /* If this device is about to be removed just return and wait for + * the next interrupt on a different device + */ + if (list_empty(&device->alias_list)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "device is in offline processing," + " don't do summary unit check handling"); + spin_unlock(&lcu->lock); + return; + } + if (lcu->suc_data.device) { + /* already scheduled or running */ + DEV_MESSAGE(KERN_WARNING, device, "%s", + "previous instance of summary unit check worker" + " still pending"); + spin_unlock(&lcu->lock); + return ; + } + lcu->suc_data.reason = reason; + lcu->suc_data.device = device; + spin_unlock(&lcu->lock); + schedule_work(&lcu->suc_data.worker); +}; diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 0c67258f..f4fb402 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -49,22 +49,6 @@ struct dasd_devmap { }; /* - * dasd_server_ssid_map contains a globally unique storage server subsystem ID. - * dasd_server_ssid_list contains the list of all subsystem IDs accessed by - * the DASD device driver. - */ -struct dasd_server_ssid_map { - struct list_head list; - struct system_id { - char vendor[4]; - char serial[15]; - __u16 ssid; - } sid; -}; - -static struct list_head dasd_server_ssid_list; - -/* * Parameter parsing functions for dasd= parameter. The syntax is: * : (0x)?[0-9a-fA-F]+ * : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+ @@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr, devmap->features &= ~DASD_FEATURE_READONLY; if (devmap->device) devmap->device->features = devmap->features; - if (devmap->device && devmap->device->gdp) - set_disk_ro(devmap->device->gdp, val); + if (devmap->device && devmap->device->block + && devmap->device->block->gdp) + set_disk_ro(devmap->device->block->gdp, val); spin_unlock(&dasd_devmap_lock); return count; } @@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) devmap = dasd_find_busid(dev->bus_id); spin_lock(&dasd_devmap_lock); - if (!IS_ERR(devmap)) - alias = devmap->uid.alias; + if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) { + spin_unlock(&dasd_devmap_lock); + return sprintf(buf, "0\n"); + } + if (devmap->uid.type == UA_BASE_PAV_ALIAS || + devmap->uid.type == UA_HYPER_PAV_ALIAS) + alias = 1; else alias = 0; spin_unlock(&dasd_devmap_lock); - return sprintf(buf, alias ? "1\n" : "0\n"); } @@ -930,19 +919,36 @@ static ssize_t dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dasd_devmap *devmap; - char uid[UID_STRLEN]; + char uid_string[UID_STRLEN]; + char ua_string[3]; + struct dasd_uid *uid; devmap = dasd_find_busid(dev->bus_id); spin_lock(&dasd_devmap_lock); - if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0) - snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x", - devmap->uid.vendor, devmap->uid.serial, - devmap->uid.ssid, devmap->uid.unit_addr); - else - uid[0] = 0; + if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) { + spin_unlock(&dasd_devmap_lock); + return sprintf(buf, "\n"); + } + uid = &devmap->uid; + switch (uid->type) { + case UA_BASE_DEVICE: + sprintf(ua_string, "%02x", uid->real_unit_addr); + break; + case UA_BASE_PAV_ALIAS: + sprintf(ua_string, "%02x", uid->base_unit_addr); + break; + case UA_HYPER_PAV_ALIAS: + sprintf(ua_string, "xx"); + break; + default: + /* should not happen, treat like base device */ + sprintf(ua_string, "%02x", uid->real_unit_addr); + break; + } + snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s", + uid->vendor, uid->serial, uid->ssid, ua_string); spin_unlock(&dasd_devmap_lock); - - return snprintf(buf, PAGE_SIZE, "%s\n", uid); + return snprintf(buf, PAGE_SIZE, "%s\n", uid_string); } static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL); @@ -1040,39 +1046,16 @@ int dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid) { struct dasd_devmap *devmap; - struct dasd_server_ssid_map *srv, *tmp; devmap = dasd_find_busid(cdev->dev.bus_id); if (IS_ERR(devmap)) return PTR_ERR(devmap); - /* generate entry for server_ssid_map */ - srv = (struct dasd_server_ssid_map *) - kzalloc(sizeof(struct dasd_server_ssid_map), GFP_KERNEL); - if (!srv) - return -ENOMEM; - strncpy(srv->sid.vendor, uid->vendor, sizeof(srv->sid.vendor) - 1); - strncpy(srv->sid.serial, uid->serial, sizeof(srv->sid.serial) - 1); - srv->sid.ssid = uid->ssid; - - /* server is already contained ? */ spin_lock(&dasd_devmap_lock); devmap->uid = *uid; - list_for_each_entry(tmp, &dasd_server_ssid_list, list) { - if (!memcmp(&srv->sid, &tmp->sid, - sizeof(struct system_id))) { - kfree(srv); - srv = NULL; - break; - } - } - - /* add servermap to serverlist */ - if (srv) - list_add(&srv->list, &dasd_server_ssid_list); spin_unlock(&dasd_devmap_lock); - return (srv ? 1 : 0); + return 0; } EXPORT_SYMBOL_GPL(dasd_set_uid); @@ -1138,9 +1121,6 @@ dasd_devmap_init(void) dasd_max_devindex = 0; for (i = 0; i < 256; i++) INIT_LIST_HEAD(&dasd_hashlists[i]); - - /* Initialize servermap structure. */ - INIT_LIST_HEAD(&dasd_server_ssid_list); return 0; } diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 571320ab..d91df38 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device) int rc; mdsk_term_io(device); - rc = mdsk_init_io(device, device->bp_block, 0, NULL); + rc = mdsk_init_io(device, device->block->bp_block, 0, NULL); if (rc) DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, " "rc=%d", rc); @@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr) struct dasd_diag_req *dreq; int rc; - device = cqr->device; + device = cqr->startdev; if (cqr->retries < 0) { DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p " "- no retry left)", cqr); - cqr->status = DASD_CQR_FAILED; + cqr->status = DASD_CQR_ERROR; return -EIO; } private = (struct dasd_diag_private *) device->private; @@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr) switch (rc) { case 0: /* Synchronous I/O finished successfully */ cqr->stopclk = get_clock(); - cqr->status = DASD_CQR_DONE; + cqr->status = DASD_CQR_SUCCESS; /* Indicate to calling function that only a dasd_schedule_bh() and no timer is needed */ rc = -EACCES; @@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) { struct dasd_device *device; - device = cqr->device; + device = cqr->startdev; mdsk_term_io(device); - mdsk_init_io(device, device->bp_block, 0, NULL); - cqr->status = DASD_CQR_CLEAR; + mdsk_init_io(device, device->block->bp_block, 0, NULL); + cqr->status = DASD_CQR_CLEAR_PENDING; cqr->stopclk = get_clock(); - dasd_schedule_bh(device); + dasd_schedule_device_bh(device); return 0; } @@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code) return; } cqr = (struct dasd_ccw_req *) ip; - device = (struct dasd_device *) cqr->device; + device = (struct dasd_device *) cqr->startdev; if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { DEV_MESSAGE(KERN_WARNING, device, " magic number of dasd_ccw_req 0x%08X doesn't" @@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code) spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); /* Check for a pending clear operation */ - if (cqr->status == DASD_CQR_CLEAR) { - cqr->status = DASD_CQR_QUEUED; - dasd_clear_timer(device); - dasd_schedule_bh(device); + if (cqr->status == DASD_CQR_CLEAR_PENDING) { + cqr->status = DASD_CQR_CLEARED; + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); return; } @@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code) expires = 0; if (status == 0) { - cqr->status = DASD_CQR_DONE; + cqr->status = DASD_CQR_SUCCESS; /* Start first request on queue if possible -> fast_io. */ if (!list_empty(&device->ccw_queue)) { next = list_entry(device->ccw_queue.next, - struct dasd_ccw_req, list); + struct dasd_ccw_req, devlist); if (next->status == DASD_CQR_QUEUED) { rc = dasd_start_diag(next); if (rc == 0) @@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code) } if (expires != 0) - dasd_set_timer(device, expires); + dasd_device_set_timer(device, expires); else - dasd_clear_timer(device); - dasd_schedule_bh(device); + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } @@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code) static int dasd_diag_check_device(struct dasd_device *device) { + struct dasd_block *block; struct dasd_diag_private *private; struct dasd_diag_characteristics *rdc_data; struct dasd_diag_bio bio; @@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device) ccw_device_get_id(device->cdev, &private->dev_id); device->private = (void *) private; } + block = dasd_alloc_block(); + if (IS_ERR(block)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "could not allocate dasd block structure"); + kfree(device->private); + return PTR_ERR(block); + } + device->block = block; + block->base = device; + /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); rdc_data->dev_nr = private->dev_id.devno; @@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device) sizeof(DASD_DIAG_CMS1)) == 0) { /* get formatted blocksize from label block */ bsize = (unsigned int) label->block_size; - device->blocks = (unsigned long) label->block_count; + block->blocks = (unsigned long) label->block_count; } else - device->blocks = end_block; - device->bp_block = bsize; - device->s2b_shift = 0; /* bits to shift 512 to get a block */ + block->blocks = end_block; + block->bp_block = bsize; + block->s2b_shift = 0; /* bits to shift 512 to get a block */ for (sb = 512; sb < bsize; sb = sb << 1) - device->s2b_shift++; - rc = mdsk_init_io(device, device->bp_block, 0, NULL); + block->s2b_shift++; + rc = mdsk_init_io(device, block->bp_block, 0, NULL); if (rc) { DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization " "failed (rc=%d)", rc); @@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device) } else { DEV_MESSAGE(KERN_INFO, device, "(%ld B/blk): %ldkB", - (unsigned long) device->bp_block, - (unsigned long) (device->blocks << - device->s2b_shift) >> 1); + (unsigned long) block->bp_block, + (unsigned long) (block->blocks << + block->s2b_shift) >> 1); } out: free_page((long) label); @@ -436,22 +447,16 @@ out: /* Fill in virtual disk geometry for device. Return zero on success, non-zero * otherwise. */ static int -dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) +dasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) { - if (dasd_check_blocksize(device->bp_block) != 0) + if (dasd_check_blocksize(block->bp_block) != 0) return -EINVAL; - geo->cylinders = (device->blocks << device->s2b_shift) >> 10; + geo->cylinders = (block->blocks << block->s2b_shift) >> 10; geo->heads = 16; - geo->sectors = 128 >> device->s2b_shift; + geo->sectors = 128 >> block->s2b_shift; return 0; } -static dasd_era_t -dasd_diag_examine_error(struct dasd_ccw_req * cqr, struct irb * stat) -{ - return dasd_era_fatal; -} - static dasd_erp_fn_t dasd_diag_erp_action(struct dasd_ccw_req * cqr) { @@ -466,8 +471,9 @@ dasd_diag_erp_postaction(struct dasd_ccw_req * cqr) /* Create DASD request from block device request. Return pointer to new * request on success, ERR_PTR otherwise. */ -static struct dasd_ccw_req * -dasd_diag_build_cp(struct dasd_device * device, struct request *req) +static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, + struct dasd_block *block, + struct request *req) { struct dasd_ccw_req *cqr; struct dasd_diag_req *dreq; @@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) rw_cmd = MDSK_WRITE_REQ; else return ERR_PTR(-EINVAL); - blksize = device->bp_block; + blksize = block->bp_block; /* Calculate record id of first and last block. */ - first_rec = req->sector >> device->s2b_shift; - last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift; + first_rec = req->sector >> block->s2b_shift; + last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift; /* Check struct bio and count the number of blocks for the request. */ count = 0; rq_for_each_segment(bv, req, iter) { if (bv->bv_len & (blksize - 1)) /* Fba can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (device->s2b_shift + 9); + count += bv->bv_len >> (block->s2b_shift + 9); } /* Paranoia. */ if (count != last_rec - first_rec + 1) @@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) datasize = sizeof(struct dasd_diag_req) + count*sizeof(struct dasd_diag_bio); cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0, - datasize, device); + datasize, memdev); if (IS_ERR(cqr)) return cqr; @@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) cqr->buildclk = get_clock(); if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->device = device; + cqr->startdev = memdev; + cqr->memdev = memdev; + cqr->block = block; cqr->expires = DIAG_TIMEOUT; cqr->status = DASD_CQR_FILLED; return cqr; @@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) int status; status = cqr->status == DASD_CQR_DONE; - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return status; } +static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr) +{ + cqr->status = DASD_CQR_FILLED; +}; + /* Fill in IOCTL data for device. */ static int dasd_diag_fill_info(struct dasd_device * device, @@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = { .fill_geometry = dasd_diag_fill_geometry, .start_IO = dasd_start_diag, .term_IO = dasd_diag_term_IO, - .examine_error = dasd_diag_examine_error, + .handle_terminated_request = dasd_diag_handle_terminated_request, .erp_action = dasd_diag_erp_action, .erp_postaction = dasd_diag_erp_postaction, .build_cp = dasd_diag_build_cp, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 6038d91..61f1693 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -52,16 +52,6 @@ MODULE_LICENSE("GPL"); static struct dasd_discipline dasd_eckd_discipline; -struct dasd_eckd_private { - struct dasd_eckd_characteristics rdc_data; - struct dasd_eckd_confdata conf_data; - struct dasd_eckd_path path_data; - struct eckd_count count_area[5]; - int init_cqr_status; - int uses_cdl; - struct attrib_data_t attrib; /* e.g. cache operations */ -}; - /* The ccw bus type uses this table to find devices that it sends to * dasd_eckd_probe */ static struct ccw_device_id dasd_eckd_ids[] = { @@ -188,7 +178,7 @@ check_XRC (struct ccw1 *de_ccw, if (rc == -ENOSYS || rc == -EACCES) rc = 0; - de_ccw->count = sizeof (struct DE_eckd_data); + de_ccw->count = sizeof(struct DE_eckd_data); de_ccw->flags |= CCW_FLAG_SLI; return rc; } @@ -208,7 +198,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, ccw->count = 16; ccw->cda = (__u32) __pa(data); - memset(data, 0, sizeof (struct DE_eckd_data)); + memset(data, 0, sizeof(struct DE_eckd_data)); switch (cmd) { case DASD_ECKD_CCW_READ_HOME_ADDRESS: case DASD_ECKD_CCW_READ_RECORD_ZERO: @@ -280,6 +270,132 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, return rc; } +static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata, + struct dasd_device *device) +{ + struct dasd_eckd_private *private; + int rc; + + private = (struct dasd_eckd_private *) device->private; + if (!private->rdc_data.facilities.XRC_supported) + return 0; + + /* switch on System Time Stamp - needed for XRC Support */ + pfxdata->define_extend.ga_extended |= 0x08; /* 'Time Stamp Valid' */ + pfxdata->define_extend.ga_extended |= 0x02; /* 'Extended Parameter' */ + pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */ + + rc = get_sync_clock(&pfxdata->define_extend.ep_sys_time); + /* Ignore return code if sync clock is switched off. */ + if (rc == -ENOSYS || rc == -EACCES) + rc = 0; + return rc; +} + +static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk, + int totrk, int cmd, struct dasd_device *basedev, + struct dasd_device *startdev) +{ + struct dasd_eckd_private *basepriv, *startpriv; + struct DE_eckd_data *data; + struct ch_t geo, beg, end; + int rc = 0; + + basepriv = (struct dasd_eckd_private *) basedev->private; + startpriv = (struct dasd_eckd_private *) startdev->private; + data = &pfxdata->define_extend; + + ccw->cmd_code = DASD_ECKD_CCW_PFX; + ccw->flags = 0; + ccw->count = sizeof(*pfxdata); + ccw->cda = (__u32) __pa(pfxdata); + + memset(pfxdata, 0, sizeof(*pfxdata)); + /* prefix data */ + pfxdata->format = 0; + pfxdata->base_address = basepriv->conf_data.ned1.unit_addr; + pfxdata->base_lss = basepriv->conf_data.ned1.ID; + pfxdata->validity.define_extend = 1; + + /* private uid is kept up to date, conf_data may be outdated */ + if (startpriv->uid.type != UA_BASE_DEVICE) { + pfxdata->validity.verify_base = 1; + if (startpriv->uid.type == UA_HYPER_PAV_ALIAS) + pfxdata->validity.hyper_pav = 1; + } + + /* define extend data (mostly)*/ + switch (cmd) { + case DASD_ECKD_CCW_READ_HOME_ADDRESS: + case DASD_ECKD_CCW_READ_RECORD_ZERO: + case DASD_ECKD_CCW_READ: + case DASD_ECKD_CCW_READ_MT: + case DASD_ECKD_CCW_READ_CKD: + case DASD_ECKD_CCW_READ_CKD_MT: + case DASD_ECKD_CCW_READ_KD: + case DASD_ECKD_CCW_READ_KD_MT: + case DASD_ECKD_CCW_READ_COUNT: + data->mask.perm = 0x1; + data->attributes.operation = basepriv->attrib.operation; + break; + case DASD_ECKD_CCW_WRITE: + case DASD_ECKD_CCW_WRITE_MT: + case DASD_ECKD_CCW_WRITE_KD: + case DASD_ECKD_CCW_WRITE_KD_MT: + data->mask.perm = 0x02; + data->attributes.operation = basepriv->attrib.operation; + rc = check_XRC_on_prefix(pfxdata, basedev); + break; + case DASD_ECKD_CCW_WRITE_CKD: + case DASD_ECKD_CCW_WRITE_CKD_MT: + data->attributes.operation = DASD_BYPASS_CACHE; + rc = check_XRC_on_prefix(pfxdata, basedev); + break; + case DASD_ECKD_CCW_ERASE: + case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: + case DASD_ECKD_CCW_WRITE_RECORD_ZERO: + data->mask.perm = 0x3; + data->mask.auth = 0x1; + data->attributes.operation = DASD_BYPASS_CACHE; + rc = check_XRC_on_prefix(pfxdata, basedev); + break; + default: + DEV_MESSAGE(KERN_ERR, basedev, "unknown opcode 0x%x", cmd); + break; + } + + data->attributes.mode = 0x3; /* ECKD */ + + if ((basepriv->rdc_data.cu_type == 0x2105 || + basepriv->rdc_data.cu_type == 0x2107 || + basepriv->rdc_data.cu_type == 0x1750) + && !(basepriv->uses_cdl && trk < 2)) + data->ga_extended |= 0x40; /* Regular Data Format Mode */ + + geo.cyl = basepriv->rdc_data.no_cyl; + geo.head = basepriv->rdc_data.trk_per_cyl; + beg.cyl = trk / geo.head; + beg.head = trk % geo.head; + end.cyl = totrk / geo.head; + end.head = totrk % geo.head; + + /* check for sequential prestage - enhance cylinder range */ + if (data->attributes.operation == DASD_SEQ_PRESTAGE || + data->attributes.operation == DASD_SEQ_ACCESS) { + + if (end.cyl + basepriv->attrib.nr_cyl < geo.cyl) + end.cyl += basepriv->attrib.nr_cyl; + else + end.cyl = (geo.cyl - 1); + } + + data->beg_ext.cyl = beg.cyl; + data->beg_ext.head = beg.head; + data->end_ext.cyl = end.cyl; + data->end_ext.head = end.head; + return rc; +} + static void locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, int rec_on_trk, int no_rec, int cmd, @@ -300,7 +416,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, ccw->count = 16; ccw->cda = (__u32) __pa(data); - memset(data, 0, sizeof (struct LO_eckd_data)); + memset(data, 0, sizeof(struct LO_eckd_data)); sector = 0; if (rec_on_trk) { switch (private->rdc_data.dev_type) { @@ -441,12 +557,15 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) sizeof(uid->serial) - 1); EBCASC(uid->serial, sizeof(uid->serial) - 1); uid->ssid = confdata->neq.subsystemID; - if (confdata->ned2.sneq.flags == 0x40) { - uid->alias = 1; - uid->unit_addr = confdata->ned2.sneq.base_unit_addr; - } else - uid->unit_addr = confdata->ned1.unit_addr; - + uid->real_unit_addr = confdata->ned1.unit_addr; + if (confdata->ned2.sneq.flags == 0x40 && + confdata->ned2.sneq.format == 0x0001) { + uid->type = confdata->ned2.sneq.sua_flags; + if (uid->type == UA_BASE_PAV_ALIAS) + uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr; + } else { + uid->type = UA_BASE_DEVICE; + } return 0; } @@ -470,7 +589,9 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, ccw->cda = (__u32)(addr_t)rcd_buffer; ccw->count = ciw->count; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; cqr->expires = 10*HZ; cqr->lpm = lpm; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); @@ -511,7 +632,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, /* * on success we update the user input parms */ - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); if (ret) goto out_error; @@ -557,19 +678,19 @@ dasd_eckd_read_conf(struct dasd_device *device) "data retrieved"); continue; /* no error */ } - if (conf_len != sizeof (struct dasd_eckd_confdata)) { + if (conf_len != sizeof(struct dasd_eckd_confdata)) { MESSAGE(KERN_WARNING, "sizes of configuration data mismatch" "%d (read) vs %ld (expected)", conf_len, - sizeof (struct dasd_eckd_confdata)); + sizeof(struct dasd_eckd_confdata)); kfree(conf_data); continue; /* no error */ } /* save first valid configuration data */ if (!conf_data_saved){ memcpy(&private->conf_data, conf_data, - sizeof (struct dasd_eckd_confdata)); + sizeof(struct dasd_eckd_confdata)); conf_data_saved++; } switch (((char *)conf_data)[242] & 0x07){ @@ -586,39 +707,104 @@ dasd_eckd_read_conf(struct dasd_device *device) return 0; } +static int dasd_eckd_read_features(struct dasd_device *device) +{ + struct dasd_psf_prssd_data *prssdp; + struct dasd_rssd_features *features; + struct dasd_ccw_req *cqr; + struct ccw1 *ccw; + int rc; + struct dasd_eckd_private *private; + + private = (struct dasd_eckd_private *) device->private; + cqr = dasd_smalloc_request(dasd_eckd_discipline.name, + 1 /* PSF */ + 1 /* RSSD */ , + (sizeof(struct dasd_psf_prssd_data) + + sizeof(struct dasd_rssd_features)), + device); + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "Could not allocate initialization request"); + return PTR_ERR(cqr); + } + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + cqr->retries = 5; + cqr->expires = 10 * HZ; + + /* Prepare for Read Subsystem Data */ + prssdp = (struct dasd_psf_prssd_data *) cqr->data; + memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); + prssdp->order = PSF_ORDER_PRSSD; + prssdp->suborder = 0x41; /* Read Feature Codes */ + /* all other bytes of prssdp must be zero */ + + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->count = sizeof(struct dasd_psf_prssd_data); + ccw->flags |= CCW_FLAG_CC; + ccw->cda = (__u32)(addr_t) prssdp; + + /* Read Subsystem Data - feature codes */ + features = (struct dasd_rssd_features *) (prssdp + 1); + memset(features, 0, sizeof(struct dasd_rssd_features)); + + ccw++; + ccw->cmd_code = DASD_ECKD_CCW_RSSD; + ccw->count = sizeof(struct dasd_rssd_features); + ccw->cda = (__u32)(addr_t) features; + + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + rc = dasd_sleep_on(cqr); + if (rc == 0) { + prssdp = (struct dasd_psf_prssd_data *) cqr->data; + features = (struct dasd_rssd_features *) (prssdp + 1); + memcpy(&private->features, features, + sizeof(struct dasd_rssd_features)); + } + dasd_sfree_request(cqr, cqr->memdev); + return rc; +} + + /* * Build CP for Perform Subsystem Function - SSC. */ -static struct dasd_ccw_req * -dasd_eckd_build_psf_ssc(struct dasd_device *device) +static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device) { - struct dasd_ccw_req *cqr; - struct dasd_psf_ssc_data *psf_ssc_data; - struct ccw1 *ccw; + struct dasd_ccw_req *cqr; + struct dasd_psf_ssc_data *psf_ssc_data; + struct ccw1 *ccw; - cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , + cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , sizeof(struct dasd_psf_ssc_data), device); - if (IS_ERR(cqr)) { - DEV_MESSAGE(KERN_WARNING, device, "%s", + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", "Could not allocate PSF-SSC request"); - return cqr; - } - psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; - psf_ssc_data->order = PSF_ORDER_SSC; - psf_ssc_data->suborder = 0x08; - - ccw = cqr->cpaddr; - ccw->cmd_code = DASD_ECKD_CCW_PSF; - ccw->cda = (__u32)(addr_t)psf_ssc_data; - ccw->count = 66; - - cqr->device = device; - cqr->expires = 10*HZ; - cqr->buildclk = get_clock(); - cqr->status = DASD_CQR_FILLED; - return cqr; + return cqr; + } + psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; + psf_ssc_data->order = PSF_ORDER_SSC; + psf_ssc_data->suborder = 0x88; + psf_ssc_data->reserved[0] = 0x88; + + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->cda = (__u32)(addr_t)psf_ssc_data; + ccw->count = 66; + + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; + cqr->expires = 10*HZ; + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + return cqr; } /* @@ -629,28 +815,28 @@ dasd_eckd_build_psf_ssc(struct dasd_device *device) static int dasd_eckd_psf_ssc(struct dasd_device *device) { - struct dasd_ccw_req *cqr; - int rc; - - cqr = dasd_eckd_build_psf_ssc(device); - if (IS_ERR(cqr)) - return PTR_ERR(cqr); - - rc = dasd_sleep_on(cqr); - if (!rc) - /* trigger CIO to reprobe devices */ - css_schedule_reprobe(); - dasd_sfree_request(cqr, cqr->device); - return rc; + struct dasd_ccw_req *cqr; + int rc; + + cqr = dasd_eckd_build_psf_ssc(device); + if (IS_ERR(cqr)) + return PTR_ERR(cqr); + + rc = dasd_sleep_on(cqr); + if (!rc) + /* trigger CIO to reprobe devices */ + css_schedule_reprobe(); + dasd_sfree_request(cqr, cqr->memdev); + return rc; } /* * Valide storage server of current device. */ -static int -dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid) +static int dasd_eckd_validate_server(struct dasd_device *device) { int rc; + struct dasd_eckd_private *private; /* Currently PAV is the only reason to 'validate' server on LPAR */ if (dasd_nopav || MACHINE_IS_VM) @@ -659,9 +845,11 @@ dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid) rc = dasd_eckd_psf_ssc(device); /* may be requested feature is not available on server, * therefore just report error and go ahead */ + private = (struct dasd_eckd_private *) device->private; DEV_MESSAGE(KERN_INFO, device, "PSF-SSC on storage subsystem %s.%s.%04x returned rc=%d", - uid->vendor, uid->serial, uid->ssid, rc); + private->uid.vendor, private->uid.serial, + private->uid.ssid, rc); /* RE-Read Configuration Data */ return dasd_eckd_read_conf(device); } @@ -674,9 +862,9 @@ static int dasd_eckd_check_characteristics(struct dasd_device *device) { struct dasd_eckd_private *private; - struct dasd_uid uid; + struct dasd_block *block; void *rdc_data; - int rc; + int is_known, rc; private = (struct dasd_eckd_private *) device->private; if (private == NULL) { @@ -699,27 +887,54 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* Read Configuration Data */ rc = dasd_eckd_read_conf(device); if (rc) - return rc; + goto out_err1; /* Generate device unique id and register in devmap */ - rc = dasd_eckd_generate_uid(device, &uid); + rc = dasd_eckd_generate_uid(device, &private->uid); if (rc) - return rc; - rc = dasd_set_uid(device->cdev, &uid); - if (rc == 1) /* new server found */ - rc = dasd_eckd_validate_server(device, &uid); + goto out_err1; + dasd_set_uid(device->cdev, &private->uid); + + if (private->uid.type == UA_BASE_DEVICE) { + block = dasd_alloc_block(); + if (IS_ERR(block)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "could not allocate dasd block structure"); + rc = PTR_ERR(block); + goto out_err1; + } + device->block = block; + block->base = device; + } + + /* register lcu with alias handling, enable PAV if this is a new lcu */ + is_known = dasd_alias_make_device_known_to_lcu(device); + if (is_known < 0) { + rc = is_known; + goto out_err2; + } + if (!is_known) { + /* new lcu found */ + rc = dasd_eckd_validate_server(device); /* will switch pav on */ + if (rc) + goto out_err3; + } + + /* Read Feature Codes */ + rc = dasd_eckd_read_features(device); if (rc) - return rc; + goto out_err3; /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); memset(rdc_data, 0, sizeof(rdc_data)); rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64); - if (rc) + if (rc) { DEV_MESSAGE(KERN_WARNING, device, "Read device characteristics returned " "rc=%d", rc); - + goto out_err3; + } DEV_MESSAGE(KERN_INFO, device, "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", private->rdc_data.dev_type, @@ -729,9 +944,24 @@ dasd_eckd_check_characteristics(struct dasd_device *device) private->rdc_data.no_cyl, private->rdc_data.trk_per_cyl, private->rdc_data.sec_per_trk); + return 0; + +out_err3: + dasd_alias_disconnect_device_from_lcu(device); +out_err2: + dasd_free_block(device->block); + device->block = NULL; +out_err1: + kfree(device->private); + device->private = NULL; return rc; } +static void dasd_eckd_uncheck_device(struct dasd_device *device) +{ + dasd_alias_disconnect_device_from_lcu(device); +} + static struct dasd_ccw_req * dasd_eckd_analysis_ccw(struct dasd_device *device) { @@ -755,7 +985,7 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) /* Define extent for the first 3 tracks. */ define_extent(ccw++, cqr->data, 0, 2, DASD_ECKD_CCW_READ_COUNT, device); - LO_data = cqr->data + sizeof (struct DE_eckd_data); + LO_data = cqr->data + sizeof(struct DE_eckd_data); /* Locate record for the first 4 records on track 0. */ ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, LO_data++, 0, 0, 4, @@ -783,7 +1013,9 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) ccw->count = 8; ccw->cda = (__u32)(addr_t) count_data; - cqr->device = device; + cqr->block = NULL; + cqr->startdev = device; + cqr->memdev = device; cqr->retries = 0; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -803,7 +1035,7 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) struct dasd_eckd_private *private; struct dasd_device *device; - device = init_cqr->device; + device = init_cqr->startdev; private = (struct dasd_eckd_private *) device->private; private->init_cqr_status = init_cqr->status; dasd_sfree_request(init_cqr, device); @@ -811,13 +1043,13 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) } static int -dasd_eckd_start_analysis(struct dasd_device *device) +dasd_eckd_start_analysis(struct dasd_block *block) { struct dasd_eckd_private *private; struct dasd_ccw_req *init_cqr; - private = (struct dasd_eckd_private *) device->private; - init_cqr = dasd_eckd_analysis_ccw(device); + private = (struct dasd_eckd_private *) block->base->private; + init_cqr = dasd_eckd_analysis_ccw(block->base); if (IS_ERR(init_cqr)) return PTR_ERR(init_cqr); init_cqr->callback = dasd_eckd_analysis_callback; @@ -828,13 +1060,15 @@ dasd_eckd_start_analysis(struct dasd_device *device) } static int -dasd_eckd_end_analysis(struct dasd_device *device) +dasd_eckd_end_analysis(struct dasd_block *block) { + struct dasd_device *device; struct dasd_eckd_private *private; struct eckd_count *count_area; unsigned int sb, blk_per_trk; int status, i; + device = block->base; private = (struct dasd_eckd_private *) device->private; status = private->init_cqr_status; private->init_cqr_status = -1; @@ -846,7 +1080,7 @@ dasd_eckd_end_analysis(struct dasd_device *device) private->uses_cdl = 1; /* Calculate number of blocks/records per track. */ - blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block); + blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); /* Check Track 0 for Compatible Disk Layout */ count_area = NULL; for (i = 0; i < 3; i++) { @@ -876,56 +1110,65 @@ dasd_eckd_end_analysis(struct dasd_device *device) if (count_area != NULL && count_area->kl == 0) { /* we found notthing violating our disk layout */ if (dasd_check_blocksize(count_area->dl) == 0) - device->bp_block = count_area->dl; + block->bp_block = count_area->dl; } - if (device->bp_block == 0) { + if (block->bp_block == 0) { DEV_MESSAGE(KERN_WARNING, device, "%s", "Volume has incompatible disk layout"); return -EMEDIUMTYPE; } - device->s2b_shift = 0; /* bits to shift 512 to get a block */ - for (sb = 512; sb < device->bp_block; sb = sb << 1) - device->s2b_shift++; + block->s2b_shift = 0; /* bits to shift 512 to get a block */ + for (sb = 512; sb < block->bp_block; sb = sb << 1) + block->s2b_shift++; - blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block); - device->blocks = (private->rdc_data.no_cyl * + blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); + block->blocks = (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl * blk_per_trk); DEV_MESSAGE(KERN_INFO, device, "(%dkB blks): %dkB at %dkB/trk %s", - (device->bp_block >> 10), + (block->bp_block >> 10), ((private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl * - blk_per_trk * (device->bp_block >> 9)) >> 1), - ((blk_per_trk * device->bp_block) >> 10), + blk_per_trk * (block->bp_block >> 9)) >> 1), + ((blk_per_trk * block->bp_block) >> 10), private->uses_cdl ? "compatible disk layout" : "linux disk layout"); return 0; } -static int -dasd_eckd_do_analysis(struct dasd_device *device) +static int dasd_eckd_do_analysis(struct dasd_block *block) { struct dasd_eckd_private *private; - private = (struct dasd_eckd_private *) device->private; + private = (struct dasd_eckd_private *) block->base->private; if (private->init_cqr_status < 0) - return dasd_eckd_start_analysis(device); + return dasd_eckd_start_analysis(block); else - return dasd_eckd_end_analysis(device); + return dasd_eckd_end_analysis(block); } +static int dasd_eckd_ready_to_online(struct dasd_device *device) +{ + return dasd_alias_add_device(device); +}; + +static int dasd_eckd_online_to_ready(struct dasd_device *device) +{ + return dasd_alias_remove_device(device); +}; + static int -dasd_eckd_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) +dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) { struct dasd_eckd_private *private; - private = (struct dasd_eckd_private *) device->private; - if (dasd_check_blocksize(device->bp_block) == 0) { + private = (struct dasd_eckd_private *) block->base->private; + if (dasd_check_blocksize(block->bp_block) == 0) { geo->sectors = recs_per_track(&private->rdc_data, - 0, device->bp_block); + 0, block->bp_block); } geo->cylinders = private->rdc_data.no_cyl; geo->heads = private->rdc_data.trk_per_cyl; @@ -1037,7 +1280,7 @@ dasd_eckd_format_device(struct dasd_device * device, locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, rpt + 1, DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, - device->bp_block); + device->block->bp_block); data += sizeof(struct LO_eckd_data); break; case 0x04: /* Invalidate track. */ @@ -1110,43 +1353,28 @@ dasd_eckd_format_device(struct dasd_device * device, ccw++; } } - fcp->device = device; - fcp->retries = 2; /* set retry counter to enable ERP */ + fcp->startdev = device; + fcp->memdev = device; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags); + fcp->retries = 5; /* set retry counter to enable default ERP */ fcp->buildclk = get_clock(); fcp->status = DASD_CQR_FILLED; return fcp; } -static dasd_era_t -dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) +static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) { - struct dasd_device *device = (struct dasd_device *) cqr->device; - struct ccw_device *cdev = device->cdev; - - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - switch (cdev->id.cu_type) { - case 0x3990: - case 0x2105: - case 0x2107: - case 0x1750: - return dasd_3990_erp_examine(cqr, irb); - case 0x9343: - return dasd_9343_erp_examine(cqr, irb); - case 0x3880: - default: - DEV_MESSAGE(KERN_WARNING, device, "%s", - "default (unknown CU type) - RECOVERABLE return"); - return dasd_era_recover; + cqr->status = DASD_CQR_FILLED; + if (cqr->block && (cqr->startdev != cqr->block->base)) { + dasd_eckd_reset_ccw_to_base_io(cqr); + cqr->startdev = cqr->block->base; } -} +}; static dasd_erp_fn_t dasd_eckd_erp_action(struct dasd_ccw_req * cqr) { - struct dasd_device *device = (struct dasd_device *) cqr->device; + struct dasd_device *device = (struct dasd_device *) cqr->startdev; struct ccw_device *cdev = device->cdev; switch (cdev->id.cu_type) { @@ -1168,8 +1396,37 @@ dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) return dasd_default_erp_postaction; } -static struct dasd_ccw_req * -dasd_eckd_build_cp(struct dasd_device * device, struct request *req) + +static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, + struct irb *irb) +{ + char mask; + + /* first of all check for state change pending interrupt */ + mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; + if ((irb->scsw.dstat & mask) == mask) { + dasd_generic_handle_state_change(device); + return; + } + + /* summary unit check */ + if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && irb->ecw[7] == 0x0D) { + dasd_alias_handle_summary_unit_check(device, irb); + return; + } + + /* just report other unsolicited interrupts */ + DEV_MESSAGE(KERN_DEBUG, device, "%s", + "unsolicited interrupt received"); + device->discipline->dump_sense(device, NULL, irb); + dasd_schedule_device_bh(device); + + return; +}; + +static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, + struct dasd_block *block, + struct request *req) { struct dasd_eckd_private *private; unsigned long *idaws; @@ -1185,8 +1442,11 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) sector_t first_trk, last_trk; unsigned int first_offs, last_offs; unsigned char cmd, rcmd; + int use_prefix; + struct dasd_device *basedev; - private = (struct dasd_eckd_private *) device->private; + basedev = block->base; + private = (struct dasd_eckd_private *) basedev->private; if (rq_data_dir(req) == READ) cmd = DASD_ECKD_CCW_READ_MT; else if (rq_data_dir(req) == WRITE) @@ -1194,13 +1454,13 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) else return ERR_PTR(-EINVAL); /* Calculate number of blocks/records per track. */ - blksize = device->bp_block; + blksize = block->bp_block; blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); /* Calculate record id of first and last block. */ - first_rec = first_trk = req->sector >> device->s2b_shift; + first_rec = first_trk = req->sector >> block->s2b_shift; first_offs = sector_div(first_trk, blk_per_trk); last_rec = last_trk = - (req->sector + req->nr_sectors - 1) >> device->s2b_shift; + (req->sector + req->nr_sectors - 1) >> block->s2b_shift; last_offs = sector_div(last_trk, blk_per_trk); /* Check struct bio and count the number of blocks for the request. */ count = 0; @@ -1209,20 +1469,33 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) if (bv->bv_len & (blksize - 1)) /* Eckd can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (device->s2b_shift + 9); + count += bv->bv_len >> (block->s2b_shift + 9); #if defined(CONFIG_64BIT) if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) - cidaw += bv->bv_len >> (device->s2b_shift + 9); + cidaw += bv->bv_len >> (block->s2b_shift + 9); #endif } /* Paranoia. */ if (count != last_rec - first_rec + 1) return ERR_PTR(-EINVAL); - /* 1x define extent + 1x locate record + number of blocks */ - cplength = 2 + count; - /* 1x define extent + 1x locate record + cidaws*sizeof(long) */ - datasize = sizeof(struct DE_eckd_data) + sizeof(struct LO_eckd_data) + - cidaw * sizeof(unsigned long); + + /* use the prefix command if available */ + use_prefix = private->features.feature[8] & 0x01; + if (use_prefix) { + /* 1x prefix + number of blocks */ + cplength = 2 + count; + /* 1x prefix + cidaws*sizeof(long) */ + datasize = sizeof(struct PFX_eckd_data) + + sizeof(struct LO_eckd_data) + + cidaw * sizeof(unsigned long); + } else { + /* 1x define extent + 1x locate record + number of blocks */ + cplength = 2 + count; + /* 1x define extent + 1x locate record + cidaws*sizeof(long) */ + datasize = sizeof(struct DE_eckd_data) + + sizeof(struct LO_eckd_data) + + cidaw * sizeof(unsigned long); + } /* Find out the number of additional locate record ccws for cdl. */ if (private->uses_cdl && first_rec < 2*blk_per_trk) { if (last_rec >= 2*blk_per_trk) @@ -1232,26 +1505,42 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) } /* Allocate the ccw request. */ cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - cplength, datasize, device); + cplength, datasize, startdev); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; - /* First ccw is define extent. */ - if (define_extent(ccw++, cqr->data, first_trk, - last_trk, cmd, device) == -EAGAIN) { - /* Clock not in sync and XRC is enabled. Try again later. */ - dasd_sfree_request(cqr, device); - return ERR_PTR(-EAGAIN); + /* First ccw is define extent or prefix. */ + if (use_prefix) { + if (prefix(ccw++, cqr->data, first_trk, + last_trk, cmd, basedev, startdev) == -EAGAIN) { + /* Clock not in sync and XRC is enabled. + * Try again later. + */ + dasd_sfree_request(cqr, startdev); + return ERR_PTR(-EAGAIN); + } + idaws = (unsigned long *) (cqr->data + + sizeof(struct PFX_eckd_data)); + } else { + if (define_extent(ccw++, cqr->data, first_trk, + last_trk, cmd, startdev) == -EAGAIN) { + /* Clock not in sync and XRC is enabled. + * Try again later. + */ + dasd_sfree_request(cqr, startdev); + return ERR_PTR(-EAGAIN); + } + idaws = (unsigned long *) (cqr->data + + sizeof(struct DE_eckd_data)); } /* Build locate_record+read/write/ccws. */ - idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data)); LO_data = (struct LO_eckd_data *) (idaws + cidaw); recid = first_rec; if (private->uses_cdl == 0 || recid > 2*blk_per_trk) { /* Only standard blocks so there is just one locate record. */ ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, LO_data++, first_trk, first_offs + 1, - last_rec - recid + 1, cmd, device, blksize); + last_rec - recid + 1, cmd, basedev, blksize); } rq_for_each_segment(bv, req, iter) { dst = page_address(bv->bv_page) + bv->bv_offset; @@ -1281,7 +1570,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, LO_data++, trkid, recoffs + 1, - 1, rcmd, device, count); + 1, rcmd, basedev, count); } /* Locate record for standard blocks ? */ if (private->uses_cdl && recid == 2*blk_per_trk) { @@ -1289,7 +1578,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) locate_record(ccw++, LO_data++, trkid, recoffs + 1, last_rec - recid + 1, - cmd, device, count); + cmd, basedev, count); } /* Read/write ccw. */ ccw[-1].flags |= CCW_FLAG_CC; @@ -1310,7 +1599,9 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) } if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->device = device; + cqr->startdev = startdev; + cqr->memdev = startdev; + cqr->block = block; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ cqr->lpm = private->path_data.ppm; cqr->retries = 256; @@ -1333,10 +1624,10 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) if (!dasd_page_cache) goto out; - private = (struct dasd_eckd_private *) cqr->device->private; - blksize = cqr->device->bp_block; + private = (struct dasd_eckd_private *) cqr->block->base->private; + blksize = cqr->block->bp_block; blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); - recid = req->sector >> cqr->device->s2b_shift; + recid = req->sector >> cqr->block->s2b_shift; ccw = cqr->cpaddr; /* Skip over define extent & locate record. */ ccw++; @@ -1367,10 +1658,71 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) } out: status = cqr->status == DASD_CQR_DONE; - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return status; } +/* + * Modify ccw chain in cqr so it can be started on a base device. + * + * Note that this is not enough to restart the cqr! + * Either reset cqr->startdev as well (summary unit check handling) + * or restart via separate cqr (as in ERP handling). + */ +void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *cqr) +{ + struct ccw1 *ccw; + struct PFX_eckd_data *pfxdata; + + ccw = cqr->cpaddr; + pfxdata = cqr->data; + + if (ccw->cmd_code == DASD_ECKD_CCW_PFX) { + pfxdata->validity.verify_base = 0; + pfxdata->validity.hyper_pav = 0; + } +} + +#define DASD_ECKD_CHANQ_MAX_SIZE 4 + +static struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base, + struct dasd_block *block, + struct request *req) +{ + struct dasd_eckd_private *private; + struct dasd_device *startdev; + unsigned long flags; + struct dasd_ccw_req *cqr; + + startdev = dasd_alias_get_start_dev(base); + if (!startdev) + startdev = base; + private = (struct dasd_eckd_private *) startdev->private; + if (private->count >= DASD_ECKD_CHANQ_MAX_SIZE) + return ERR_PTR(-EBUSY); + + spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags); + private->count++; + cqr = dasd_eckd_build_cp(startdev, block, req); + if (IS_ERR(cqr)) + private->count--; + spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags); + return cqr; +} + +static int dasd_eckd_free_alias_cp(struct dasd_ccw_req *cqr, + struct request *req) +{ + struct dasd_eckd_private *private; + unsigned long flags; + + spin_lock_irqsave(get_ccwdev_lock(cqr->memdev->cdev), flags); + private = (struct dasd_eckd_private *) cqr->memdev->private; + private->count--; + spin_unlock_irqrestore(get_ccwdev_lock(cqr->memdev->cdev), flags); + return dasd_eckd_free_cp(cqr, req); +} + static int dasd_eckd_fill_info(struct dasd_device * device, struct dasd_information2_t * info) @@ -1384,9 +1736,9 @@ dasd_eckd_fill_info(struct dasd_device * device, info->characteristics_size = sizeof(struct dasd_eckd_characteristics); memcpy(info->characteristics, &private->rdc_data, sizeof(struct dasd_eckd_characteristics)); - info->confdata_size = sizeof (struct dasd_eckd_confdata); + info->confdata_size = sizeof(struct dasd_eckd_confdata); memcpy(info->configuration_data, &private->conf_data, - sizeof (struct dasd_eckd_confdata)); + sizeof(struct dasd_eckd_confdata)); return 0; } @@ -1419,7 +1771,8 @@ dasd_eckd_release(struct dasd_device *device) cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->count = 32; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ @@ -1429,7 +1782,7 @@ dasd_eckd_release(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1459,7 +1812,8 @@ dasd_eckd_reserve(struct dasd_device *device) cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->count = 32; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ @@ -1469,7 +1823,7 @@ dasd_eckd_reserve(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1498,7 +1852,8 @@ dasd_eckd_steal_lock(struct dasd_device *device) cqr->cpaddr->flags |= CCW_FLAG_SLI; cqr->cpaddr->count = 32; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ @@ -1508,7 +1863,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1526,52 +1881,52 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) cqr = dasd_smalloc_request(dasd_eckd_discipline.name, 1 /* PSF */ + 1 /* RSSD */ , - (sizeof (struct dasd_psf_prssd_data) + - sizeof (struct dasd_rssd_perf_stats_t)), + (sizeof(struct dasd_psf_prssd_data) + + sizeof(struct dasd_rssd_perf_stats_t)), device); if (IS_ERR(cqr)) { DEV_MESSAGE(KERN_WARNING, device, "%s", "Could not allocate initialization request"); return PTR_ERR(cqr); } - cqr->device = device; + cqr->startdev = device; + cqr->memdev = device; cqr->retries = 0; cqr->expires = 10 * HZ; /* Prepare for Read Subsystem Data */ prssdp = (struct dasd_psf_prssd_data *) cqr->data; - memset(prssdp, 0, sizeof (struct dasd_psf_prssd_data)); + memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); prssdp->order = PSF_ORDER_PRSSD; prssdp->suborder = 0x01; /* Performance Statistics */ prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */ ccw = cqr->cpaddr; ccw->cmd_code = DASD_ECKD_CCW_PSF; - ccw->count = sizeof (struct dasd_psf_prssd_data); + ccw->count = sizeof(struct dasd_psf_prssd_data); ccw->flags |= CCW_FLAG_CC; ccw->cda = (__u32)(addr_t) prssdp; /* Read Subsystem Data - Performance Statistics */ stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); - memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t)); + memset(stats, 0, sizeof(struct dasd_rssd_perf_stats_t)); ccw++; ccw->cmd_code = DASD_ECKD_CCW_RSSD; - ccw->count = sizeof (struct dasd_rssd_perf_stats_t); + ccw->count = sizeof(struct dasd_rssd_perf_stats_t); ccw->cda = (__u32)(addr_t) stats; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on(cqr); if (rc == 0) { - /* Prepare for Read Subsystem Data */ prssdp = (struct dasd_psf_prssd_data *) cqr->data; stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); if (copy_to_user(argp, stats, sizeof(struct dasd_rssd_perf_stats_t))) rc = -EFAULT; } - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1594,7 +1949,7 @@ dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp) rc = 0; if (copy_to_user(argp, (long *) &attrib, - sizeof (struct attrib_data_t))) + sizeof(struct attrib_data_t))) rc = -EFAULT; return rc; @@ -1627,8 +1982,10 @@ dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp) } static int -dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp) +dasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp) { + struct dasd_device *device = block->base; + switch (cmd) { case BIODASDGATTR: return dasd_eckd_get_attrib(device, argp); @@ -1685,9 +2042,8 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) * Print sense data and related channel program. * Parts are printed because printk buffer is only 1024 bytes. */ -static void -dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, - struct irb *irb) +static void dasd_eckd_dump_sense(struct dasd_device *device, + struct dasd_ccw_req *req, struct irb *irb) { char *page; struct ccw1 *first, *last, *fail, *from, *to; @@ -1743,37 +2099,40 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, } printk("%s", page); - /* dump the Channel Program (max 140 Bytes per line) */ - /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */ - first = req->cpaddr; - for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); - to = min(first + 6, last); - len = sprintf(page, KERN_ERR PRINTK_HEADER - " Related CP in req: %p\n", req); - dasd_eckd_dump_ccw_range(first, to, page + len); - printk("%s", page); + if (req) { + /* req == NULL for unsolicited interrupts */ + /* dump the Channel Program (max 140 Bytes per line) */ + /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */ + first = req->cpaddr; + for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); + to = min(first + 6, last); + len = sprintf(page, KERN_ERR PRINTK_HEADER + " Related CP in req: %p\n", req); + dasd_eckd_dump_ccw_range(first, to, page + len); + printk("%s", page); - /* print failing CCW area (maximum 4) */ - /* scsw->cda is either valid or zero */ - len = 0; - from = ++to; - fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */ - if (from < fail - 2) { - from = fail - 2; /* there is a gap - print header */ - len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); - } - to = min(fail + 1, last); - len += dasd_eckd_dump_ccw_range(from, to, page + len); - - /* print last CCWs (maximum 2) */ - from = max(from, ++to); - if (from < last - 1) { - from = last - 1; /* there is a gap - print header */ - len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + /* print failing CCW area (maximum 4) */ + /* scsw->cda is either valid or zero */ + len = 0; + from = ++to; + fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */ + if (from < fail - 2) { + from = fail - 2; /* there is a gap - print header */ + len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); + } + to = min(fail + 1, last); + len += dasd_eckd_dump_ccw_range(from, to, page + len); + + /* print last CCWs (maximum 2) */ + from = max(from, ++to); + if (from < last - 1) { + from = last - 1; /* there is a gap - print header */ + len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); + } + len += dasd_eckd_dump_ccw_range(from, last, page + len); + if (len > 0) + printk("%s", page); } - len += dasd_eckd_dump_ccw_range(from, last, page + len); - if (len > 0) - printk("%s", page); free_page((unsigned long) page); } @@ -1796,16 +2155,20 @@ static struct dasd_discipline dasd_eckd_discipline = { .ebcname = "ECKD", .max_blocks = 240, .check_device = dasd_eckd_check_characteristics, + .uncheck_device = dasd_eckd_uncheck_device, .do_analysis = dasd_eckd_do_analysis, + .ready_to_online = dasd_eckd_ready_to_online, + .online_to_ready = dasd_eckd_online_to_ready, .fill_geometry = dasd_eckd_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, + .handle_terminated_request = dasd_eckd_handle_terminated_request, .format_device = dasd_eckd_format_device, - .examine_error = dasd_eckd_examine_error, .erp_action = dasd_eckd_erp_action, .erp_postaction = dasd_eckd_erp_postaction, - .build_cp = dasd_eckd_build_cp, - .free_cp = dasd_eckd_free_cp, + .handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt, + .build_cp = dasd_eckd_build_alias_cp, + .free_cp = dasd_eckd_free_alias_cp, .dump_sense = dasd_eckd_dump_sense, .fill_info = dasd_eckd_fill_info, .ioctl = dasd_eckd_ioctl, diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 712ff16..fc2509c 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -39,6 +39,8 @@ #define DASD_ECKD_CCW_READ_CKD_MT 0x9e #define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d #define DASD_ECKD_CCW_RESERVE 0xB4 +#define DASD_ECKD_CCW_PFX 0xE7 +#define DASD_ECKD_CCW_RSCK 0xF9 /* * Perform Subsystem Function / Sub-Orders @@ -137,6 +139,25 @@ struct LO_eckd_data { __u16 length; } __attribute__ ((packed)); +/* Prefix data for format 0x00 and 0x01 */ +struct PFX_eckd_data { + unsigned char format; + struct { + unsigned char define_extend:1; + unsigned char time_stamp:1; + unsigned char verify_base:1; + unsigned char hyper_pav:1; + unsigned char reserved:4; + } __attribute__ ((packed)) validity; + __u8 base_address; + __u8 aux; + __u8 base_lss; + __u8 reserved[7]; + struct DE_eckd_data define_extend; + struct LO_eckd_data locate_record; + __u8 LO_extended_data[4]; +} __attribute__ ((packed)); + struct dasd_eckd_characteristics { __u16 cu_type; struct { @@ -254,7 +275,9 @@ struct dasd_eckd_confdata { } __attribute__ ((packed)) ned; struct { unsigned char flags; /* byte 0 */ - unsigned char res2[7]; /* byte 1- 7 */ + unsigned char res1; /* byte 1 */ + __u16 format; /* byte 2-3 */ + unsigned char res2[4]; /* byte 4-7 */ unsigned char sua_flags; /* byte 8 */ __u8 base_unit_addr; /* byte 9 */ unsigned char res3[22]; /* byte 10-31 */ @@ -343,6 +366,11 @@ struct dasd_eckd_path { __u8 npm; }; +struct dasd_rssd_features { + char feature[256]; +} __attribute__((packed)); + + /* * Perform Subsystem Function - Prepare for Read Subsystem Data */ @@ -365,4 +393,99 @@ struct dasd_psf_ssc_data { unsigned char reserved[59]; } __attribute__((packed)); + +/* + * some structures and definitions for alias handling + */ +struct dasd_unit_address_configuration { + struct { + char ua_type; + char base_ua; + } unit[256]; +} __attribute__((packed)); + + +#define MAX_DEVICES_PER_LCU 256 + +/* flags on the LCU */ +#define NEED_UAC_UPDATE 0x01 +#define UPDATE_PENDING 0x02 + +enum pavtype {NO_PAV, BASE_PAV, HYPER_PAV}; + + +struct alias_root { + struct list_head serverlist; + spinlock_t lock; +}; + +struct alias_server { + struct list_head server; + struct dasd_uid uid; + struct list_head lculist; +}; + +struct summary_unit_check_work_data { + char reason; + struct dasd_device *device; + struct work_struct worker; +}; + +struct read_uac_work_data { + struct dasd_device *device; + struct delayed_work dwork; +}; + +struct alias_lcu { + struct list_head lcu; + struct dasd_uid uid; + enum pavtype pav; + char flags; + spinlock_t lock; + struct list_head grouplist; + struct list_head active_devices; + struct list_head inactive_devices; + struct dasd_unit_address_configuration *uac; + struct summary_unit_check_work_data suc_data; + struct read_uac_work_data ruac_data; + struct dasd_ccw_req *rsu_cqr; +}; + +struct alias_pav_group { + struct list_head group; + struct dasd_uid uid; + struct alias_lcu *lcu; + struct list_head baselist; + struct list_head aliaslist; + struct dasd_device *next; +}; + + +struct dasd_eckd_private { + struct dasd_eckd_characteristics rdc_data; + struct dasd_eckd_confdata conf_data; + struct dasd_eckd_path path_data; + struct eckd_count count_area[5]; + int init_cqr_status; + int uses_cdl; + struct attrib_data_t attrib; /* e.g. cache operations */ + struct dasd_rssd_features features; + + /* alias managemnet */ + struct dasd_uid uid; + struct alias_pav_group *pavgroup; + struct alias_lcu *lcu; + int count; +}; + + + +int dasd_alias_make_device_known_to_lcu(struct dasd_device *); +void dasd_alias_disconnect_device_from_lcu(struct dasd_device *); +int dasd_alias_add_device(struct dasd_device *); +int dasd_alias_remove_device(struct dasd_device *); +struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *); +void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *); +void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); + #endif /* DASD_ECKD_H */ diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index 0c081a6..6e53ab6 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device, unsigned long flags; struct eerbuffer *eerb; - snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; + snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; if (snss_rc) data_size = 0; else @@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device) set_bit(DASD_FLAG_EER_SNSS, &device->flags); return; } + /* cdev is already locked, can't use dasd_add_request_head */ clear_bit(DASD_FLAG_EER_SNSS, &device->flags); cqr->status = DASD_CQR_QUEUED; - list_add(&cqr->list, &device->ccw_queue); - dasd_schedule_bh(device); + list_add(&cqr->devlist, &device->ccw_queue); + dasd_schedule_device_bh(device); } /* @@ -415,7 +416,7 @@ void dasd_eer_snss(struct dasd_device *device) */ static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data) { - struct dasd_device *device = cqr->device; + struct dasd_device *device = cqr->startdev; unsigned long flags; dasd_eer_write(device, cqr, DASD_EER_STATECHANGE); @@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device) if (!cqr) return -ENOMEM; - cqr->device = device; + cqr->startdev = device; cqr->retries = 255; cqr->expires = 10 * HZ; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index caa5d91..8f10000 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, if (cqr == NULL) return ERR_PTR(-ENOMEM); memset(cqr, 0, sizeof(struct dasd_ccw_req)); + INIT_LIST_HEAD(&cqr->devlist); + INIT_LIST_HEAD(&cqr->blocklist); data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L); cqr->cpaddr = NULL; if (cplength > 0) { @@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, } void -dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) +dasd_free_erp_request(struct dasd_ccw_req *cqr, struct dasd_device * device) { unsigned long flags; @@ -81,11 +83,11 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) * dasd_default_erp_action just retries the current cqr */ struct dasd_ccw_req * -dasd_default_erp_action(struct dasd_ccw_req * cqr) +dasd_default_erp_action(struct dasd_ccw_req *cqr) { struct dasd_device *device; - device = cqr->device; + device = cqr->startdev; /* just retry - there is nothing to save ... I got no sense data.... */ if (cqr->retries > 0) { @@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) "default ERP called (%i retries left)", cqr->retries); cqr->lpm = LPM_ANYPATH; - cqr->status = DASD_CQR_QUEUED; + cqr->status = DASD_CQR_FILLED; } else { DEV_MESSAGE (KERN_WARNING, device, "%s", "default ERP called (NO retry left)"); cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock (); + cqr->stopclk = get_clock(); } return cqr; } /* end dasd_default_erp_action */ @@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) * RETURN VALUES * cqr pointer to the original CQR */ -struct dasd_ccw_req * -dasd_default_erp_postaction(struct dasd_ccw_req * cqr) +struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr) { - struct dasd_device *device; int success; BUG_ON(cqr->refers == NULL || cqr->function == NULL); - device = cqr->device; success = cqr->status == DASD_CQR_DONE; /* free all ERPs - but NOT the original cqr */ @@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr) struct dasd_ccw_req *refers; refers = cqr->refers; - /* remove the request from the device queue */ - list_del(&cqr->list); + /* remove the request from the block queue */ + list_del(&cqr->blocklist); /* free the finished erp request */ - dasd_free_erp_request(cqr, device); + dasd_free_erp_request(cqr, cqr->memdev); cqr = refers; } @@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) { struct dasd_device *device; - device = cqr->device; + device = cqr->startdev; /* dump sense data */ if (device->discipline && device->discipline->dump_sense) device->discipline->dump_sense(device, cqr, irb); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 1d95822..d13ea05 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -117,6 +117,7 @@ locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, static int dasd_fba_check_characteristics(struct dasd_device *device) { + struct dasd_block *block; struct dasd_fba_private *private; struct ccw_device *cdev = device->cdev; void *rdc_data; @@ -133,6 +134,16 @@ dasd_fba_check_characteristics(struct dasd_device *device) } device->private = (void *) private; } + block = dasd_alloc_block(); + if (IS_ERR(block)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "could not allocate dasd block structure"); + kfree(device->private); + return PTR_ERR(block); + } + device->block = block; + block->base = device; + /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32); @@ -155,60 +166,37 @@ dasd_fba_check_characteristics(struct dasd_device *device) return 0; } -static int -dasd_fba_do_analysis(struct dasd_device *device) +static int dasd_fba_do_analysis(struct dasd_block *block) { struct dasd_fba_private *private; int sb, rc; - private = (struct dasd_fba_private *) device->private; + private = (struct dasd_fba_private *) block->base->private; rc = dasd_check_blocksize(private->rdc_data.blk_size); if (rc) { - DEV_MESSAGE(KERN_INFO, device, "unknown blocksize %d", + DEV_MESSAGE(KERN_INFO, block->base, "unknown blocksize %d", private->rdc_data.blk_size); return rc; } - device->blocks = private->rdc_data.blk_bdsa; - device->bp_block = private->rdc_data.blk_size; - device->s2b_shift = 0; /* bits to shift 512 to get a block */ + block->blocks = private->rdc_data.blk_bdsa; + block->bp_block = private->rdc_data.blk_size; + block->s2b_shift = 0; /* bits to shift 512 to get a block */ for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) - device->s2b_shift++; + block->s2b_shift++; return 0; } -static int -dasd_fba_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) +static int dasd_fba_fill_geometry(struct dasd_block *block, + struct hd_geometry *geo) { - if (dasd_check_blocksize(device->bp_block) != 0) + if (dasd_check_blocksize(block->bp_block) != 0) return -EINVAL; - geo->cylinders = (device->blocks << device->s2b_shift) >> 10; + geo->cylinders = (block->blocks << block->s2b_shift) >> 10; geo->heads = 16; - geo->sectors = 128 >> device->s2b_shift; + geo->sectors = 128 >> block->s2b_shift; return 0; } -static dasd_era_t -dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) -{ - struct dasd_device *device; - struct ccw_device *cdev; - - device = (struct dasd_device *) cqr->device; - if (irb->scsw.cstat == 0x00 && - irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) - return dasd_era_none; - - cdev = device->cdev; - switch (cdev->id.dev_type) { - case 0x3370: - return dasd_3370_erp_examine(cqr, irb); - case 0x9336: - return dasd_9336_erp_examine(cqr, irb); - default: - return dasd_era_recover; - } -} - static dasd_erp_fn_t dasd_fba_erp_action(struct dasd_ccw_req * cqr) { @@ -221,13 +209,34 @@ dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) if (cqr->function == dasd_default_erp_action) return dasd_default_erp_postaction; - DEV_MESSAGE(KERN_WARNING, cqr->device, "unknown ERP action %p", + DEV_MESSAGE(KERN_WARNING, cqr->startdev, "unknown ERP action %p", cqr->function); return NULL; } -static struct dasd_ccw_req * -dasd_fba_build_cp(struct dasd_device * device, struct request *req) +static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device, + struct irb *irb) +{ + char mask; + + /* first of all check for state change pending interrupt */ + mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; + if ((irb->scsw.dstat & mask) == mask) { + dasd_generic_handle_state_change(device); + return; + } + + /* check for unsolicited interrupts */ + DEV_MESSAGE(KERN_DEBUG, device, "%s", + "unsolicited interrupt received"); + device->discipline->dump_sense(device, NULL, irb); + dasd_schedule_device_bh(device); + return; +}; + +static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, + struct dasd_block *block, + struct request *req) { struct dasd_fba_private *private; unsigned long *idaws; @@ -242,17 +251,17 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) unsigned int blksize, off; unsigned char cmd; - private = (struct dasd_fba_private *) device->private; + private = (struct dasd_fba_private *) block->base->private; if (rq_data_dir(req) == READ) { cmd = DASD_FBA_CCW_READ; } else if (rq_data_dir(req) == WRITE) { cmd = DASD_FBA_CCW_WRITE; } else return ERR_PTR(-EINVAL); - blksize = device->bp_block; + blksize = block->bp_block; /* Calculate record id of first and last block. */ - first_rec = req->sector >> device->s2b_shift; - last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift; + first_rec = req->sector >> block->s2b_shift; + last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift; /* Check struct bio and count the number of blocks for the request. */ count = 0; cidaw = 0; @@ -260,7 +269,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) if (bv->bv_len & (blksize - 1)) /* Fba can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (device->s2b_shift + 9); + count += bv->bv_len >> (block->s2b_shift + 9); #if defined(CONFIG_64BIT) if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) cidaw += bv->bv_len / blksize; @@ -284,13 +293,13 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) } /* Allocate the ccw request. */ cqr = dasd_smalloc_request(dasd_fba_discipline.name, - cplength, datasize, device); + cplength, datasize, memdev); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; /* First ccw is define extent. */ define_extent(ccw++, cqr->data, rq_data_dir(req), - device->bp_block, req->sector, req->nr_sectors); + block->bp_block, req->sector, req->nr_sectors); /* Build locate_record + read/write ccws. */ idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); LO_data = (struct LO_fba_data *) (idaws + cidaw); @@ -326,7 +335,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ccw[-1].flags |= CCW_FLAG_CC; } ccw->cmd_code = cmd; - ccw->count = device->bp_block; + ccw->count = block->bp_block; if (idal_is_needed(dst, blksize)) { ccw->cda = (__u32)(addr_t) idaws; ccw->flags = CCW_FLAG_IDA; @@ -342,7 +351,9 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) } if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->device = device; + cqr->startdev = memdev; + cqr->memdev = memdev; + cqr->block = block; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ cqr->retries = 32; cqr->buildclk = get_clock(); @@ -363,8 +374,8 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) if (!dasd_page_cache) goto out; - private = (struct dasd_fba_private *) cqr->device->private; - blksize = cqr->device->bp_block; + private = (struct dasd_fba_private *) cqr->block->base->private; + blksize = cqr->block->bp_block; ccw = cqr->cpaddr; /* Skip over define extent & locate record. */ ccw++; @@ -394,10 +405,15 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) } out: status = cqr->status == DASD_CQR_DONE; - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); return status; } +static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) +{ + cqr->status = DASD_CQR_FILLED; +}; + static int dasd_fba_fill_info(struct dasd_device * device, struct dasd_information2_t * info) @@ -546,9 +562,10 @@ static struct dasd_discipline dasd_fba_discipline = { .fill_geometry = dasd_fba_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, - .examine_error = dasd_fba_examine_error, + .handle_terminated_request = dasd_fba_handle_terminated_request, .erp_action = dasd_fba_erp_action, .erp_postaction = dasd_fba_erp_postaction, + .handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt, .build_cp = dasd_fba_build_cp, .free_cp = dasd_fba_free_cp, .dump_sense = dasd_fba_dump_sense, diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c index 47ba446..aee6565 100644 --- a/drivers/s390/block/dasd_genhd.c +++ b/drivers/s390/block/dasd_genhd.c @@ -25,14 +25,15 @@ /* * Allocate and register gendisk structure for device. */ -int -dasd_gendisk_alloc(struct dasd_device *device) +int dasd_gendisk_alloc(struct dasd_block *block) { struct gendisk *gdp; + struct dasd_device *base; int len; /* Make sure the minor for this device exists. */ - if (device->devindex >= DASD_PER_MAJOR) + base = block->base; + if (base->devindex >= DASD_PER_MAJOR) return -EBUSY; gdp = alloc_disk(1 << DASD_PARTN_BITS); @@ -41,9 +42,9 @@ dasd_gendisk_alloc(struct dasd_device *device) /* Initialize gendisk structure. */ gdp->major = DASD_MAJOR; - gdp->first_minor = device->devindex << DASD_PARTN_BITS; + gdp->first_minor = base->devindex << DASD_PARTN_BITS; gdp->fops = &dasd_device_operations; - gdp->driverfs_dev = &device->cdev->dev; + gdp->driverfs_dev = &base->cdev->dev; /* * Set device name. @@ -53,53 +54,51 @@ dasd_gendisk_alloc(struct dasd_device *device) * dasdaaaa - dasdzzzz : 456976 devices, added up = 475252 */ len = sprintf(gdp->disk_name, "dasd"); - if (device->devindex > 25) { - if (device->devindex > 701) { - if (device->devindex > 18277) + if (base->devindex > 25) { + if (base->devindex > 701) { + if (base->devindex > 18277) len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((device->devindex-18278) + 'a'+(((base->devindex-18278) /17576)%26)); len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((device->devindex-702)/676)%26)); + 'a'+(((base->devindex-702)/676)%26)); } len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((device->devindex-26)/26)%26)); + 'a'+(((base->devindex-26)/26)%26)); } - len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26)); + len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26)); - if (device->features & DASD_FEATURE_READONLY) + if (block->base->features & DASD_FEATURE_READONLY) set_disk_ro(gdp, 1); - gdp->private_data = device; - gdp->queue = device->request_queue; - device->gdp = gdp; - set_capacity(device->gdp, 0); - add_disk(device->gdp); + gdp->private_data = block; + gdp->queue = block->request_queue; + block->gdp = gdp; + set_capacity(block->gdp, 0); + add_disk(block->gdp); return 0; } /* * Unregister and free gendisk structure for device. */ -void -dasd_gendisk_free(struct dasd_device *device) +void dasd_gendisk_free(struct dasd_block *block) { - if (device->gdp) { - del_gendisk(device->gdp); - device->gdp->queue = NULL; - put_disk(device->gdp); - device->gdp = NULL; + if (block->gdp) { + del_gendisk(block->gdp); + block->gdp->queue = NULL; + put_disk(block->gdp); + block->gdp = NULL; } } /* * Trigger a partition detection. */ -int -dasd_scan_partitions(struct dasd_device * device) +int dasd_scan_partitions(struct dasd_block *block) { struct block_device *bdev; - bdev = bdget_disk(device->gdp, 0); + bdev = bdget_disk(block->gdp, 0); if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0) return -ENODEV; /* @@ -117,7 +116,7 @@ dasd_scan_partitions(struct dasd_device * device) * is why the assignment to device->bdev is done AFTER * the BLKRRPART ioctl. */ - device->bdev = bdev; + block->bdev = bdev; return 0; } @@ -125,8 +124,7 @@ dasd_scan_partitions(struct dasd_device * device) * Remove all inodes in the system for a device, delete the * partitions and make device unusable by setting its size to zero. */ -void -dasd_destroy_partitions(struct dasd_device * device) +void dasd_destroy_partitions(struct dasd_block *block) { /* The two structs have 168/176 byte on 31/64 bit. */ struct blkpg_partition bpart; @@ -137,8 +135,8 @@ dasd_destroy_partitions(struct dasd_device * device) * Get the bdev pointer from the device structure and clear * device->bdev to lower the offline open_count limit again. */ - bdev = device->bdev; - device->bdev = NULL; + bdev = block->bdev; + block->bdev = NULL; /* * See fs/partition/check.c:delete_partition @@ -149,17 +147,16 @@ dasd_destroy_partitions(struct dasd_device * device) memset(&barg, 0, sizeof(struct blkpg_ioctl_arg)); barg.data = (void __force __user *) &bpart; barg.op = BLKPG_DEL_PARTITION; - for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--) + for (bpart.pno = block->gdp->minors - 1; bpart.pno > 0; bpart.pno--) ioctl_by_bdev(bdev, BLKPG, (unsigned long) &barg); - invalidate_partition(device->gdp, 0); + invalidate_partition(block->gdp, 0); /* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */ blkdev_put(bdev); - set_capacity(device->gdp, 0); + set_capacity(block->gdp, 0); } -int -dasd_gendisk_init(void) +int dasd_gendisk_init(void) { int rc; @@ -174,8 +171,7 @@ dasd_gendisk_init(void) return 0; } -void -dasd_gendisk_exit(void) +void dasd_gendisk_exit(void) { unregister_blkdev(DASD_MAJOR, "dasd"); } diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index d427dae..44b2984 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -64,13 +64,7 @@ * SECTION: Type definitions */ struct dasd_device; - -typedef enum { - dasd_era_fatal = -1, /* no chance to recover */ - dasd_era_none = 0, /* don't recover, everything alright */ - dasd_era_msg = 1, /* don't recover, just report... */ - dasd_era_recover = 2 /* recovery action recommended */ -} dasd_era_t; +struct dasd_block; /* BIT DEFINITIONS FOR SENSE DATA */ #define DASD_SENSE_BIT_0 0x80 @@ -151,19 +145,22 @@ do { \ struct dasd_ccw_req { unsigned int magic; /* Eye catcher */ - struct list_head list; /* list_head for request queueing. */ + struct list_head devlist; /* for dasd_device request queue */ + struct list_head blocklist; /* for dasd_block request queue */ /* Where to execute what... */ - struct dasd_device *device; /* device the request is for */ + struct dasd_block *block; /* the originating block device */ + struct dasd_device *memdev; /* the device used to allocate this */ + struct dasd_device *startdev; /* device the request is started on */ struct ccw1 *cpaddr; /* address of channel program */ - char status; /* status of this request */ + char status; /* status of this request */ short retries; /* A retry counter */ unsigned long flags; /* flags of this request */ /* ... and how */ unsigned long starttime; /* jiffies time of request start */ int expires; /* expiration period in jiffies */ - char lpm; /* logical path mask */ + char lpm; /* logical path mask */ void *data; /* pointer to data area */ /* these are important for recovering erroneous requests */ @@ -178,20 +175,27 @@ struct dasd_ccw_req { unsigned long long endclk; /* TOD-clock of request termination */ /* Callback that is called after reaching final status. */ - void (*callback)(struct dasd_ccw_req *, void *data); - void *callback_data; + void (*callback)(struct dasd_ccw_req *, void *data); + void *callback_data; }; /* * dasd_ccw_req -> status can be: */ -#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */ -#define DASD_CQR_QUEUED 0x01 /* request is queued to be processed */ -#define DASD_CQR_IN_IO 0x02 /* request is currently in IO */ -#define DASD_CQR_DONE 0x03 /* request is completed successfully */ -#define DASD_CQR_ERROR 0x04 /* request is completed with error */ -#define DASD_CQR_FAILED 0x05 /* request is finally failed */ -#define DASD_CQR_CLEAR 0x06 /* request is clear pending */ +#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */ +#define DASD_CQR_DONE 0x01 /* request is completed successfully */ +#define DASD_CQR_NEED_ERP 0x02 /* request needs recovery action */ +#define DASD_CQR_IN_ERP 0x03 /* request is in recovery */ +#define DASD_CQR_FAILED 0x04 /* request is finally failed */ +#define DASD_CQR_TERMINATED 0x05 /* request was stopped by driver */ + +#define DASD_CQR_QUEUED 0x80 /* request is queued to be processed */ +#define DASD_CQR_IN_IO 0x81 /* request is currently in IO */ +#define DASD_CQR_ERROR 0x82 /* request is completed with error */ +#define DASD_CQR_CLEAR_PENDING 0x83 /* request is clear pending */ +#define DASD_CQR_CLEARED 0x84 /* request was cleared */ +#define DASD_CQR_SUCCESS 0x85 /* request was successfull */ + /* per dasd_ccw_req flags */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ @@ -214,52 +218,71 @@ struct dasd_discipline { struct list_head list; /* used for list of disciplines */ - /* - * Device recognition functions. check_device is used to verify - * the sense data and the information returned by read device - * characteristics. It returns 0 if the discipline can be used - * for the device in question. - * do_analysis is used in the step from device state "basic" to - * state "accept". It returns 0 if the device can be made ready, - * it returns -EMEDIUMTYPE if the device can't be made ready or - * -EAGAIN if do_analysis started a ccw that needs to complete - * before the analysis may be repeated. - */ - int (*check_device)(struct dasd_device *); - int (*do_analysis) (struct dasd_device *); - - /* - * Device operation functions. build_cp creates a ccw chain for - * a block device request, start_io starts the request and - * term_IO cancels it (e.g. in case of a timeout). format_device - * returns a ccw chain to be used to format the device. - */ + /* + * Device recognition functions. check_device is used to verify + * the sense data and the information returned by read device + * characteristics. It returns 0 if the discipline can be used + * for the device in question. uncheck_device is called during + * device shutdown to deregister a device from its discipline. + */ + int (*check_device) (struct dasd_device *); + void (*uncheck_device) (struct dasd_device *); + + /* + * do_analysis is used in the step from device state "basic" to + * state "accept". It returns 0 if the device can be made ready, + * it returns -EMEDIUMTYPE if the device can't be made ready or + * -EAGAIN if do_analysis started a ccw that needs to complete + * before the analysis may be repeated. + */ + int (*do_analysis) (struct dasd_block *); + + /* + * Last things to do when a device is set online, and first things + * when it is set offline. + */ + int (*ready_to_online) (struct dasd_device *); + int (*online_to_ready) (struct dasd_device *); + + /* + * Device operation functions. build_cp creates a ccw chain for + * a block device request, start_io starts the request and + * term_IO cancels it (e.g. in case of a timeout). format_device + * returns a ccw chain to be used to format the device. + * handle_terminated_request allows to examine a cqr and prepare + * it for retry. + */ struct dasd_ccw_req *(*build_cp) (struct dasd_device *, + struct dasd_block *, struct request *); int (*start_IO) (struct dasd_ccw_req *); int (*term_IO) (struct dasd_ccw_req *); + void (*handle_terminated_request) (struct dasd_ccw_req *); struct dasd_ccw_req *(*format_device) (struct dasd_device *, struct format_data_t *); int (*free_cp) (struct dasd_ccw_req *, struct request *); - /* - * Error recovery functions. examine_error() returns a value that - * indicates what to do for an error condition. If examine_error() + + /* + * Error recovery functions. examine_error() returns a value that + * indicates what to do for an error condition. If examine_error() * returns 'dasd_era_recover' erp_action() is called to create a - * special error recovery ccw. erp_postaction() is called after - * an error recovery ccw has finished its execution. dump_sense - * is called for every error condition to print the sense data - * to the console. - */ - dasd_era_t(*examine_error) (struct dasd_ccw_req *, struct irb *); + * special error recovery ccw. erp_postaction() is called after + * an error recovery ccw has finished its execution. dump_sense + * is called for every error condition to print the sense data + * to the console. + */ dasd_erp_fn_t(*erp_action) (struct dasd_ccw_req *); dasd_erp_fn_t(*erp_postaction) (struct dasd_ccw_req *); void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *, struct irb *); + void (*handle_unsolicited_interrupt) (struct dasd_device *, + struct irb *); + /* i/o control functions. */ - int (*fill_geometry) (struct dasd_device *, struct hd_geometry *); + int (*fill_geometry) (struct dasd_block *, struct hd_geometry *); int (*fill_info) (struct dasd_device *, struct dasd_information2_t *); - int (*ioctl) (struct dasd_device *, unsigned int, void __user *); + int (*ioctl) (struct dasd_block *, unsigned int, void __user *); }; extern struct dasd_discipline *dasd_diag_discipline_pointer; @@ -267,12 +290,18 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer; /* * Unique identifier for dasd device. */ +#define UA_NOT_CONFIGURED 0x00 +#define UA_BASE_DEVICE 0x01 +#define UA_BASE_PAV_ALIAS 0x02 +#define UA_HYPER_PAV_ALIAS 0x03 + struct dasd_uid { - __u8 alias; + __u8 type; char vendor[4]; char serial[15]; __u16 ssid; - __u8 unit_addr; + __u8 real_unit_addr; + __u8 base_unit_addr; }; /* @@ -293,14 +322,9 @@ struct dasd_uid { struct dasd_device { /* Block device stuff. */ - struct gendisk *gdp; - struct request_queue *request_queue; - spinlock_t request_queue_lock; - struct block_device *bdev; + struct dasd_block *block; + unsigned int devindex; - unsigned long blocks; /* size of volume in blocks */ - unsigned int bp_block; /* bytes per block */ - unsigned int s2b_shift; /* log2 (bp_block/512) */ unsigned long flags; /* per device flags */ unsigned short features; /* copy of devmap-features (read-only!) */ @@ -316,9 +340,8 @@ struct dasd_device { int state, target; int stopped; /* device (ccw_device_start) was stopped */ - /* Open and reference count. */ + /* reference count. */ atomic_t ref_count; - atomic_t open_count; /* ccw queue and memory for static ccw/erp buffers. */ struct list_head ccw_queue; @@ -337,20 +360,45 @@ struct dasd_device { struct ccw_device *cdev; + /* hook for alias management */ + struct list_head alias_list; +}; + +struct dasd_block { + /* Block device stuff. */ + struct gendisk *gdp; + struct request_queue *request_queue; + spinlock_t request_queue_lock; + struct block_device *bdev; + atomic_t open_count; + + unsigned long blocks; /* size of volume in blocks */ + unsigned int bp_block; /* bytes per block */ + unsigned int s2b_shift; /* log2 (bp_block/512) */ + + struct dasd_device *base; + struct list_head ccw_queue; + spinlock_t queue_lock; + + atomic_t tasklet_scheduled; + struct tasklet_struct tasklet; + struct timer_list timer; + #ifdef CONFIG_DASD_PROFILE struct dasd_profile_info_t profile; #endif }; + + /* reasons why device (ccw_device_start) was stopped */ #define DASD_STOPPED_NOT_ACC 1 /* not accessible */ #define DASD_STOPPED_QUIESCE 2 /* Quiesced */ #define DASD_STOPPED_PENDING 4 /* long busy */ #define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */ -#define DASD_STOPPED_DC_EIO 16 /* disconnected, return -EIO */ +#define DASD_STOPPED_SU 16 /* summary unit check handling */ /* per device flags */ -#define DASD_FLAG_DSC_ERROR 2 /* return -EIO when disconnected */ #define DASD_FLAG_OFFLINE 3 /* device is in offline processing */ #define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */ #define DASD_FLAG_EER_IN_USE 5 /* A SNSS request is running */ @@ -489,6 +537,9 @@ dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device) struct dasd_device *dasd_alloc_device(void); void dasd_free_device(struct dasd_device *); +struct dasd_block *dasd_alloc_block(void); +void dasd_free_block(struct dasd_block *); + void dasd_enable_device(struct dasd_device *); void dasd_set_target_state(struct dasd_device *, int); void dasd_kick_device(struct dasd_device *); @@ -497,18 +548,23 @@ void dasd_add_request_head(struct dasd_ccw_req *); void dasd_add_request_tail(struct dasd_ccw_req *); int dasd_start_IO(struct dasd_ccw_req *); int dasd_term_IO(struct dasd_ccw_req *); -void dasd_schedule_bh(struct dasd_device *); +void dasd_schedule_device_bh(struct dasd_device *); +void dasd_schedule_block_bh(struct dasd_block *); int dasd_sleep_on(struct dasd_ccw_req *); int dasd_sleep_on_immediatly(struct dasd_ccw_req *); int dasd_sleep_on_interruptible(struct dasd_ccw_req *); -void dasd_set_timer(struct dasd_device *, int); -void dasd_clear_timer(struct dasd_device *); +void dasd_device_set_timer(struct dasd_device *, int); +void dasd_device_clear_timer(struct dasd_device *); +void dasd_block_set_timer(struct dasd_block *, int); +void dasd_block_clear_timer(struct dasd_block *); int dasd_cancel_req(struct dasd_ccw_req *); +int dasd_flush_device_queue(struct dasd_device *); int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *); void dasd_generic_remove (struct ccw_device *cdev); int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); int dasd_generic_set_offline (struct ccw_device *cdev); int dasd_generic_notify(struct ccw_device *, int); +void dasd_generic_handle_state_change(struct dasd_device *); int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int); @@ -542,10 +598,10 @@ int dasd_busid_known(char *); /* externals in dasd_gendisk.c */ int dasd_gendisk_init(void); void dasd_gendisk_exit(void); -int dasd_gendisk_alloc(struct dasd_device *); -void dasd_gendisk_free(struct dasd_device *); -int dasd_scan_partitions(struct dasd_device *); -void dasd_destroy_partitions(struct dasd_device *); +int dasd_gendisk_alloc(struct dasd_block *); +void dasd_gendisk_free(struct dasd_block *); +int dasd_scan_partitions(struct dasd_block *); +void dasd_destroy_partitions(struct dasd_block *); /* externals in dasd_ioctl.c */ int dasd_ioctl(struct inode *, struct file *, unsigned int, unsigned long); @@ -563,20 +619,9 @@ struct dasd_ccw_req *dasd_alloc_erp_request(char *, int, int, void dasd_free_erp_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_log_sense(struct dasd_ccw_req *, struct irb *); -/* externals in dasd_3370_erp.c */ -dasd_era_t dasd_3370_erp_examine(struct dasd_ccw_req *, struct irb *); - /* externals in dasd_3990_erp.c */ -dasd_era_t dasd_3990_erp_examine(struct dasd_ccw_req *, struct irb *); struct dasd_ccw_req *dasd_3990_erp_action(struct dasd_ccw_req *); -/* externals in dasd_9336_erp.c */ -dasd_era_t dasd_9336_erp_examine(struct dasd_ccw_req *, struct irb *); - -/* externals in dasd_9336_erp.c */ -dasd_era_t dasd_9343_erp_examine(struct dasd_ccw_req *, struct irb *); -struct dasd_ccw_req *dasd_9343_erp_action(struct dasd_ccw_req *); - /* externals in dasd_eer.c */ #ifdef CONFIG_DASD_EER int dasd_eer_init(void); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 672eb0a..91a6463 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -38,15 +38,15 @@ dasd_ioctl_api_version(void __user *argp) static int dasd_ioctl_enable(struct block_device *bdev) { - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - dasd_enable_device(device); + dasd_enable_device(block->base); /* Formatting the dasd device can change the capacity. */ mutex_lock(&bdev->bd_mutex); - i_size_write(bdev->bd_inode, (loff_t)get_capacity(device->gdp) << 9); + i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9); mutex_unlock(&bdev->bd_mutex); return 0; } @@ -58,7 +58,7 @@ dasd_ioctl_enable(struct block_device *bdev) static int dasd_ioctl_disable(struct block_device *bdev) { - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -71,7 +71,7 @@ dasd_ioctl_disable(struct block_device *bdev) * using the BIODASDFMT ioctl. Therefore the correct state for the * device is DASD_STATE_BASIC that allows to do basic i/o. */ - dasd_set_target_state(device, DASD_STATE_BASIC); + dasd_set_target_state(block->base, DASD_STATE_BASIC); /* * Set i_size to zero, since read, write, etc. check against this * value. @@ -85,19 +85,19 @@ dasd_ioctl_disable(struct block_device *bdev) /* * Quiesce device. */ -static int -dasd_ioctl_quiesce(struct dasd_device *device) +static int dasd_ioctl_quiesce(struct dasd_block *block) { unsigned long flags; + struct dasd_device *base; + base = block->base; if (!capable (CAP_SYS_ADMIN)) return -EACCES; - DEV_MESSAGE (KERN_DEBUG, device, "%s", - "Quiesce IO on device"); - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped |= DASD_STOPPED_QUIESCE; - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + DEV_MESSAGE(KERN_DEBUG, base, "%s", "Quiesce IO on device"); + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); + base->stopped |= DASD_STOPPED_QUIESCE; + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); return 0; } @@ -105,22 +105,21 @@ dasd_ioctl_quiesce(struct dasd_device *device) /* * Quiesce device. */ -static int -dasd_ioctl_resume(struct dasd_device *device) +static int dasd_ioctl_resume(struct dasd_block *block) { unsigned long flags; + struct dasd_device *base; + base = block->base; if (!capable (CAP_SYS_ADMIN)) return -EACCES; - DEV_MESSAGE (KERN_DEBUG, device, "%s", - "resume IO on device"); - - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_QUIESCE; - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + DEV_MESSAGE(KERN_DEBUG, base, "%s", "resume IO on device"); + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); + base->stopped &= ~DASD_STOPPED_QUIESCE; + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); - dasd_schedule_bh (device); + dasd_schedule_block_bh(block); return 0; } @@ -130,22 +129,23 @@ dasd_ioctl_resume(struct dasd_device *device) * commands to format a single unit of the device. In terms of the ECKD * devices this means CCWs are generated to format a single track. */ -static int -dasd_format(struct dasd_device * device, struct format_data_t * fdata) +static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) { struct dasd_ccw_req *cqr; + struct dasd_device *base; int rc; - if (device->discipline->format_device == NULL) + base = block->base; + if (base->discipline->format_device == NULL) return -EPERM; - if (device->state != DASD_STATE_BASIC) { - DEV_MESSAGE(KERN_WARNING, device, "%s", + if (base->state != DASD_STATE_BASIC) { + DEV_MESSAGE(KERN_WARNING, base, "%s", "dasd_format: device is not disabled! "); return -EBUSY; } - DBF_DEV_EVENT(DBF_NOTICE, device, + DBF_DEV_EVENT(DBF_NOTICE, base, "formatting units %d to %d (%d B blocks) flags %d", fdata->start_unit, fdata->stop_unit, fdata->blksize, fdata->intensity); @@ -156,20 +156,20 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) * enabling the device later. */ if (fdata->start_unit == 0) { - struct block_device *bdev = bdget_disk(device->gdp, 0); + struct block_device *bdev = bdget_disk(block->gdp, 0); bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize); bdput(bdev); } while (fdata->start_unit <= fdata->stop_unit) { - cqr = device->discipline->format_device(device, fdata); + cqr = base->discipline->format_device(base, fdata); if (IS_ERR(cqr)) return PTR_ERR(cqr); rc = dasd_sleep_on_interruptible(cqr); - dasd_sfree_request(cqr, cqr->device); + dasd_sfree_request(cqr, cqr->memdev); if (rc) { if (rc != -ERESTARTSYS) - DEV_MESSAGE(KERN_ERR, device, + DEV_MESSAGE(KERN_ERR, base, " Formatting of unit %d failed " "with rc = %d", fdata->start_unit, rc); @@ -186,7 +186,7 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) static int dasd_ioctl_format(struct block_device *bdev, void __user *argp) { - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; struct format_data_t fdata; if (!capable(CAP_SYS_ADMIN)) @@ -194,51 +194,47 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) if (!argp) return -EINVAL; - if (device->features & DASD_FEATURE_READONLY) + if (block->base->features & DASD_FEATURE_READONLY) return -EROFS; if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) return -EFAULT; if (bdev != bdev->bd_contains) { - DEV_MESSAGE(KERN_WARNING, device, "%s", + DEV_MESSAGE(KERN_WARNING, block->base, "%s", "Cannot low-level format a partition"); return -EINVAL; } - return dasd_format(device, &fdata); + return dasd_format(block, &fdata); } #ifdef CONFIG_DASD_PROFILE /* * Reset device profile information */ -static int -dasd_ioctl_reset_profile(struct dasd_device *device) +static int dasd_ioctl_reset_profile(struct dasd_block *block) { - memset(&device->profile, 0, sizeof (struct dasd_profile_info_t)); + memset(&block->profile, 0, sizeof(struct dasd_profile_info_t)); return 0; } /* * Return device profile information */ -static int -dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) +static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) { if (dasd_profile_level == DASD_PROFILE_OFF) return -EIO; - if (copy_to_user(argp, &device->profile, - sizeof (struct dasd_profile_info_t))) + if (copy_to_user(argp, &block->profile, + sizeof(struct dasd_profile_info_t))) return -EFAULT; return 0; } #else -static int -dasd_ioctl_reset_profile(struct dasd_device *device) +static int dasd_ioctl_reset_profile(struct dasd_block *block) { return -ENOSYS; } -static int -dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) +static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) { return -ENOSYS; } @@ -247,87 +243,88 @@ dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) /* * Return dasd information. Used for BIODASDINFO and BIODASDINFO2. */ -static int -dasd_ioctl_information(struct dasd_device *device, - unsigned int cmd, void __user *argp) +static int dasd_ioctl_information(struct dasd_block *block, + unsigned int cmd, void __user *argp) { struct dasd_information2_t *dasd_info; unsigned long flags; int rc; + struct dasd_device *base; struct ccw_device *cdev; struct ccw_dev_id dev_id; - if (!device->discipline->fill_info) + base = block->base; + if (!base->discipline->fill_info) return -EINVAL; dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL); if (dasd_info == NULL) return -ENOMEM; - rc = device->discipline->fill_info(device, dasd_info); + rc = base->discipline->fill_info(base, dasd_info); if (rc) { kfree(dasd_info); return rc; } - cdev = device->cdev; + cdev = base->cdev; ccw_device_get_id(cdev, &dev_id); dasd_info->devno = dev_id.devno; - dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev); + dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev); dasd_info->cu_type = cdev->id.cu_type; dasd_info->cu_model = cdev->id.cu_model; dasd_info->dev_type = cdev->id.dev_type; dasd_info->dev_model = cdev->id.dev_model; - dasd_info->status = device->state; + dasd_info->status = base->state; /* * The open_count is increased for every opener, that includes * the blkdev_get in dasd_scan_partitions. * This must be hidden from user-space. */ - dasd_info->open_count = atomic_read(&device->open_count); - if (!device->bdev) + dasd_info->open_count = atomic_read(&block->open_count); + if (!block->bdev) dasd_info->open_count++; /* * check if device is really formatted * LDL / CDL was returned by 'fill_info' */ - if ((device->state < DASD_STATE_READY) || - (dasd_check_blocksize(device->bp_block))) + if ((base->state < DASD_STATE_READY) || + (dasd_check_blocksize(block->bp_block))) dasd_info->format = DASD_FORMAT_NONE; dasd_info->features |= - ((device->features & DASD_FEATURE_READONLY) != 0); + ((base->features & DASD_FEATURE_READONLY) != 0); - if (device->discipline) - memcpy(dasd_info->type, device->discipline->name, 4); + if (base->discipline) + memcpy(dasd_info->type, base->discipline->name, 4); else memcpy(dasd_info->type, "none", 4); - if (device->request_queue->request_fn) { + if (block->request_queue->request_fn) { struct list_head *l; #ifdef DASD_EXTENDED_PROFILING { struct list_head *l; - spin_lock_irqsave(&device->lock, flags); - list_for_each(l, &device->request_queue->queue_head) + spin_lock_irqsave(&block->lock, flags); + list_for_each(l, &block->request_queue->queue_head) dasd_info->req_queue_len++; - spin_unlock_irqrestore(&device->lock, flags); + spin_unlock_irqrestore(&block->lock, flags); } #endif /* DASD_EXTENDED_PROFILING */ - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - list_for_each(l, &device->ccw_queue) + spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); + list_for_each(l, &base->ccw_queue) dasd_info->chanq_len++; - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), + spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); } rc = 0; if (copy_to_user(argp, dasd_info, ((cmd == (unsigned int) BIODASDINFO2) ? - sizeof (struct dasd_information2_t) : - sizeof (struct dasd_information_t)))) + sizeof(struct dasd_information2_t) : + sizeof(struct dasd_information_t)))) rc = -EFAULT; kfree(dasd_info); return rc; @@ -339,7 +336,7 @@ dasd_ioctl_information(struct dasd_device *device, static int dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) { - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; int intval; if (!capable(CAP_SYS_ADMIN)) @@ -351,11 +348,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) return -EFAULT; set_disk_ro(bdev->bd_disk, intval); - return dasd_set_feature(device->cdev, DASD_FEATURE_READONLY, intval); + return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval); } -static int -dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd, +static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd, unsigned long arg) { struct cmbdata __user *argp = (void __user *) arg; @@ -363,7 +359,7 @@ dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd, struct cmbdata data; int ret; - ret = cmf_readall(device->cdev, &data); + ret = cmf_readall(block->base->cdev, &data); if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp)))) return -EFAULT; return ret; @@ -374,10 +370,10 @@ dasd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - struct dasd_device *device = bdev->bd_disk->private_data; + struct dasd_block *block = bdev->bd_disk->private_data; void __user *argp = (void __user *)arg; - if (!device) + if (!block) return -ENODEV; if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) { @@ -391,33 +387,33 @@ dasd_ioctl(struct inode *inode, struct file *file, case BIODASDENABLE: return dasd_ioctl_enable(bdev); case BIODASDQUIESCE: - return dasd_ioctl_quiesce(device); + return dasd_ioctl_quiesce(block); case BIODASDRESUME: - return dasd_ioctl_resume(device); + return dasd_ioctl_resume(block); case BIODASDFMT: return dasd_ioctl_format(bdev, argp); case BIODASDINFO: - return dasd_ioctl_information(device, cmd, argp); + return dasd_ioctl_information(block, cmd, argp); case BIODASDINFO2: - return dasd_ioctl_information(device, cmd, argp); + return dasd_ioctl_information(block, cmd, argp); case BIODASDPRRD: - return dasd_ioctl_read_profile(device, argp); + return dasd_ioctl_read_profile(block, argp); case BIODASDPRRST: - return dasd_ioctl_reset_profile(device); + return dasd_ioctl_reset_profile(block); case BLKROSET: return dasd_ioctl_set_ro(bdev, argp); case DASDAPIVER: return dasd_ioctl_api_version(argp); case BIODASDCMFENABLE: - return enable_cmf(device->cdev); + return enable_cmf(block->base->cdev); case BIODASDCMFDISABLE: - return disable_cmf(device->cdev); + return disable_cmf(block->base->cdev); case BIODASDREADALLCMB: - return dasd_ioctl_readall_cmb(device, cmd, arg); + return dasd_ioctl_readall_cmb(block, cmd, arg); default: /* if the discipline has an ioctl method try it. */ - if (device->discipline->ioctl) { - int rval = device->discipline->ioctl(device, cmd, argp); + if (block->base->discipline->ioctl) { + int rval = block->base->discipline->ioctl(block, cmd, argp); if (rval != -ENOIOCTLCMD) return rval; } diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index ac7e8ef..0584a7d 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -54,11 +54,16 @@ static int dasd_devices_show(struct seq_file *m, void *v) { struct dasd_device *device; + struct dasd_block *block; char *substr; device = dasd_device_from_devindex((unsigned long) v - 1); if (IS_ERR(device)) return 0; + if (device->block) + block = device->block; + else + return 0; /* Print device number. */ seq_printf(m, "%s", device->cdev->dev.bus_id); /* Print discipline string. */ @@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v) else seq_printf(m, "(none)"); /* Print kdev. */ - if (device->gdp) + if (block->gdp) seq_printf(m, " at (%3d:%6d)", - device->gdp->major, device->gdp->first_minor); + block->gdp->major, block->gdp->first_minor); else seq_printf(m, " at (???:??????)"); /* Print device name. */ - if (device->gdp) - seq_printf(m, " is %-8s", device->gdp->disk_name); + if (block->gdp) + seq_printf(m, " is %-8s", block->gdp->disk_name); else seq_printf(m, " is ????????"); /* Print devices features. */ @@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v) case DASD_STATE_READY: case DASD_STATE_ONLINE: seq_printf(m, "active "); - if (dasd_check_blocksize(device->bp_block)) + if (dasd_check_blocksize(block->bp_block)) seq_printf(m, "n/f "); else seq_printf(m, "at blocksize: %d, %ld blocks, %ld MB", - device->bp_block, device->blocks, - ((device->bp_block >> 9) * - device->blocks) >> 11); + block->bp_block, block->blocks, + ((block->bp_block >> 9) * + block->blocks) >> 11); break; default: seq_printf(m, "no stat"); -- cgit v0.10.2 From 53360fb19672e9ca94d769694a18f46f6e0612a9 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sat, 26 Jan 2008 14:11:24 +0100 Subject: [S390] Remove TOPDIR from Makefile This patch removes TOPDIR from arch/s390/kernel/Makefile. Cc: Sam Ravnborg Signed-off-by: WANG Cong Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 56cb710..b3b650a 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -31,7 +31,3 @@ S390_KEXEC_OBJS := machine_kexec.o crash.o S390_KEXEC_OBJS += $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o) obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS) -# -# This is just to get the dependencies... -# -binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c -- cgit v0.10.2 From 028fed8233b669e03def295a4e632b1b933720f9 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Sat, 26 Jan 2008 14:11:25 +0100 Subject: [S390] Unused field / extern declaration in processor.h Remove extern declaration of non-existent last_task_used_math and remove unused field error_code from the thread_struct. Signed-off-by: Martin Schwidefsky diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h index 21d40a1..c86b982 100644 --- a/include/asm-s390/processor.h +++ b/include/asm-s390/processor.h @@ -59,9 +59,6 @@ extern void s390_adjust_jiffies(void); extern void print_cpu_info(struct cpuinfo_S390 *); extern int get_cpu_capability(unsigned int *); -/* Lazy FPU handling on uni-processor */ -extern struct task_struct *last_task_used_math; - /* * User space process size: 2GB for 31 bit, 4TB for 64 bit. */ @@ -95,7 +92,6 @@ struct thread_struct { unsigned long ksp; /* kernel stack pointer */ mm_segment_t mm_segment; unsigned long prot_addr; /* address of protection-excep. */ - unsigned int error_code; /* error-code of last prog-excep. */ unsigned int trap_no; per_struct per_info; /* Used to give failing instruction back to user for ieee exceptions */ -- cgit v0.10.2 From 7dc1da9ffae5a344f7115d019e2be069d3e1bb8d Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Sat, 26 Jan 2008 14:11:26 +0100 Subject: [S390] dasd: fix loop in request expiration handling Add time to the 'expires' value to avoid a loop caused by the cqr termination function Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index db9193d..99268b9 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1197,12 +1197,13 @@ static void __dasd_device_check_expire(struct dasd_device *device) (time_after_eq(jiffies, cqr->expires + cqr->starttime))) { if (device->discipline->term_IO(cqr) != 0) { /* Hmpf, try again in 5 sec */ - dasd_device_set_timer(device, 5*HZ); DEV_MESSAGE(KERN_ERR, device, "internal error - timeout (%is) expired " "for cqr %p, termination failed, " "retrying in 5s", (cqr->expires/HZ), cqr); + cqr->expires += 5*HZ; + dasd_device_set_timer(device, 5*HZ); } else { DEV_MESSAGE(KERN_ERR, device, "internal error - timeout (%is) expired " -- cgit v0.10.2 From dab5209cd878c146d9f6923f061d1c2725ff210f Mon Sep 17 00:00:00 2001 From: Carsten Otte Date: Sat, 26 Jan 2008 14:11:27 +0100 Subject: [S390] add smp_call_function_mask This patch adds the s390 variant for smp_call_function_mask(). The implementation is pretty straight forward using the wrapper __smp_call_function_map() which already takes a cpumask_t argument. Signed-off-by: Carsten Otte Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 040406d..f66db71 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -210,6 +210,33 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, } EXPORT_SYMBOL(smp_call_function_single); +/** + * smp_call_function_mask(): Run a function on a set of other CPUs. + * @mask: The set of cpus to run on. Must not include the current cpu. + * @func: The function to run. This must be fast and non-blocking. + * @info: An arbitrary pointer to pass to the function. + * @wait: If true, wait (atomically) until function has completed on other CPUs. + * + * Returns 0 on success, else a negative status code. + * + * If @wait is true, then returns once @func has returned; otherwise + * it returns just before the target cpu calls @func. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler or from a bottom half handler. + */ +int +smp_call_function_mask(cpumask_t mask, + void (*func)(void *), void *info, + int wait) +{ + preempt_disable(); + __smp_call_function_map(func, info, 0, wait, mask); + preempt_enable(); + return 0; +} +EXPORT_SYMBOL(smp_call_function_mask); + void smp_send_stop(void) { int cpu, rc; diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h index 218454b..c7b7432 100644 --- a/include/asm-s390/smp.h +++ b/include/asm-s390/smp.h @@ -90,6 +90,8 @@ extern void __cpu_die (unsigned int cpu); extern void cpu_die (void) __attribute__ ((noreturn)); extern int __cpu_up (unsigned int cpu); +extern int smp_call_function_mask(cpumask_t mask, void (*func)(void *), + void *info, int wait); #endif #ifndef CONFIG_SMP -- cgit v0.10.2 From 894cdde26b538c77b9943bc72f0570abf6e58e37 Mon Sep 17 00:00:00 2001 From: Hisashi Hifumi Date: Sat, 26 Jan 2008 14:11:28 +0100 Subject: [S390] do local_irq_restore while spinning in spin_lock_irqsave. In s390's spin_lock_irqsave, interrupts remain disabled while spinning. In other architectures like x86 and powerpc, interrupts are re-enabled while spinning if IRQ is not masked before spin_lock_irqsave is called. The following patch re-enables interrupts through local_irq_restore while spinning for a lock acquisition. This can improve system response. [heiko.carstens@de.ibm.com: removed saving of pc] Signed-off-by: Hisashi Hifumi Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index 59c56c3..e41f400 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c @@ -59,6 +59,29 @@ void _raw_spin_lock_wait(raw_spinlock_t *lp) } EXPORT_SYMBOL(_raw_spin_lock_wait); +void _raw_spin_lock_wait_flags(raw_spinlock_t *lp, unsigned long flags) +{ + int count = spin_retry; + unsigned int cpu = ~smp_processor_id(); + + local_irq_restore(flags); + while (1) { + if (count-- <= 0) { + unsigned int owner = lp->owner_cpu; + if (owner != 0) + _raw_yield_cpu(~owner); + count = spin_retry; + } + if (__raw_spin_is_locked(lp)) + continue; + local_irq_disable(); + if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) + return; + local_irq_restore(flags); + } +} +EXPORT_SYMBOL(_raw_spin_lock_wait_flags); + int _raw_spin_trylock_retry(raw_spinlock_t *lp) { unsigned int cpu = ~smp_processor_id(); diff --git a/include/asm-s390/spinlock.h b/include/asm-s390/spinlock.h index c1d6e7e..df84ae9 100644 --- a/include/asm-s390/spinlock.h +++ b/include/asm-s390/spinlock.h @@ -53,12 +53,12 @@ _raw_compare_and_swap(volatile unsigned int *lock, */ #define __raw_spin_is_locked(x) ((x)->owner_cpu != 0) -#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) #define __raw_spin_unlock_wait(lock) \ do { while (__raw_spin_is_locked(lock)) \ _raw_spin_relax(lock); } while (0) extern void _raw_spin_lock_wait(raw_spinlock_t *); +extern void _raw_spin_lock_wait_flags(raw_spinlock_t *, unsigned long flags); extern int _raw_spin_trylock_retry(raw_spinlock_t *); extern void _raw_spin_relax(raw_spinlock_t *lock); @@ -72,6 +72,17 @@ static inline void __raw_spin_lock(raw_spinlock_t *lp) _raw_spin_lock_wait(lp); } +static inline void __raw_spin_lock_flags(raw_spinlock_t *lp, + unsigned long flags) +{ + int old; + + old = _raw_compare_and_swap(&lp->owner_cpu, 0, ~smp_processor_id()); + if (likely(old == 0)) + return; + _raw_spin_lock_wait_flags(lp, flags); +} + static inline int __raw_spin_trylock(raw_spinlock_t *lp) { int old; -- cgit v0.10.2 From 5c81cdbeff72e7fc2eb62ee5dc26197e54f9a06c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 26 Jan 2008 14:11:29 +0100 Subject: [S390] constify function pointer tables. Signed-off-by: Jan Engelhardt Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c index 23ed094..0cfefdd 100644 --- a/arch/s390/crypto/prng.c +++ b/arch/s390/crypto/prng.c @@ -146,7 +146,7 @@ static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes, return ret; } -static struct file_operations prng_fops = { +static const struct file_operations prng_fops = { .owner = THIS_MODULE, .open = &prng_open, .release = NULL, diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index fcebf64..766c783 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -917,7 +917,7 @@ static void *c_next(struct seq_file *m, void *v, loff_t *pos) static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 0584a7d..28a86f07 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -142,7 +142,7 @@ static void dasd_devices_stop(struct seq_file *m, void *v) { } -static struct seq_operations dasd_devices_seq_ops = { +static const struct seq_operations dasd_devices_seq_ops = { .start = dasd_devices_start, .next = dasd_devices_next, .stop = dasd_devices_stop, diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c index cea49f0..c9b96d5 100644 --- a/drivers/s390/char/tape_proc.c +++ b/drivers/s390/char/tape_proc.c @@ -97,7 +97,7 @@ static void tape_proc_stop(struct seq_file *m, void *v) { } -static struct seq_operations tape_proc_seq = { +static const struct seq_operations tape_proc_seq = { .start = tape_proc_start, .next = tape_proc_next, .stop = tape_proc_stop, diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index d70a6e6..7689b50 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -759,7 +759,7 @@ static loff_t ur_llseek(struct file *file, loff_t offset, int whence) return newpos; } -static struct file_operations ur_fops = { +static const struct file_operations ur_fops = { .owner = THIS_MODULE, .open = ur_open, .release = ur_release, diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 7073daf..f523501 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -470,7 +470,7 @@ static loff_t zcore_lseek(struct file *file, loff_t offset, int orig) return rc; } -static struct file_operations zcore_fops = { +static const struct file_operations zcore_fops = { .owner = THIS_MODULE, .llseek = zcore_lseek, .read = zcore_read, diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index bd5f16f..e8597ec 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -348,7 +348,7 @@ cio_ignore_write(struct file *file, const char __user *user_buf, return user_len; } -static struct seq_operations cio_ignore_proc_seq_ops = { +static const struct seq_operations cio_ignore_proc_seq_ops = { .start = cio_ignore_proc_seq_start, .stop = cio_ignore_proc_seq_stop, .next = cio_ignore_proc_seq_next, diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c index f1ff165..46ecd03 100644 --- a/drivers/s390/net/qeth_proc.c +++ b/drivers/s390/net/qeth_proc.c @@ -146,7 +146,7 @@ qeth_procfile_seq_show(struct seq_file *s, void *it) return 0; } -static struct seq_operations qeth_procfile_seq_ops = { +static const struct seq_operations qeth_procfile_seq_ops = { .start = qeth_procfile_seq_start, .stop = qeth_procfile_seq_stop, .next = qeth_procfile_seq_next, @@ -264,7 +264,7 @@ qeth_perf_procfile_seq_show(struct seq_file *s, void *it) return 0; } -static struct seq_operations qeth_perf_procfile_seq_ops = { +static const struct seq_operations qeth_perf_procfile_seq_ops = { .start = qeth_procfile_seq_start, .stop = qeth_procfile_seq_stop, .next = qeth_procfile_seq_next, -- cgit v0.10.2 From 361f494d4e62ee5f7a971bf34945deeb69392159 Mon Sep 17 00:00:00 2001 From: Peter Tiedemann Date: Sat, 26 Jan 2008 14:11:30 +0100 Subject: [S390] usage of s390dbf: shrink number of debug areas to use. Signed-off-by: Peter Tiedemann diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 99268b9..1db15f3 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -214,7 +214,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device) return rc; } /* register 'device' debug area, used for all DBF_DEV_XXX calls */ - device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2, + device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 1, 8 * sizeof(long)); debug_register_view(device->debug_area, &debug_sprintf_view); debug_set_level(device->debug_area, DBF_WARNING); @@ -2435,7 +2435,7 @@ static int __init dasd_init(void) init_waitqueue_head(&dasd_flush_wq); /* register 'common' DASD debug area, used for all DBF_XXX calls */ - dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof(long)); + dasd_debug_area = debug_register("dasd", 1, 1, 8 * sizeof(long)); if (dasd_debug_area == NULL) { rc = -ENOMEM; goto failed; diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 11f632f..60590a1 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -64,17 +64,17 @@ __setup ("cio_msg=", cio_setup); */ static int __init cio_debug_init(void) { - cio_debug_msg_id = debug_register("cio_msg", 16, 4, 16 * sizeof(long)); + cio_debug_msg_id = debug_register("cio_msg", 16, 1, 16 * sizeof(long)); if (!cio_debug_msg_id) goto out_unregister; debug_register_view(cio_debug_msg_id, &debug_sprintf_view); debug_set_level(cio_debug_msg_id, 2); - cio_debug_trace_id = debug_register("cio_trace", 16, 4, 16); + cio_debug_trace_id = debug_register("cio_trace", 16, 1, 16); if (!cio_debug_trace_id) 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, 4, 16 * sizeof(long)); + cio_debug_crw_id = debug_register("cio_crw", 16, 1, 16 * sizeof(long)); if (!cio_debug_crw_id) goto out_unregister; debug_register_view(cio_debug_crw_id, &debug_sprintf_view); -- cgit v0.10.2 From 9d40d2e3955185b69c264583d080eb3defcb05a0 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Sat, 26 Jan 2008 14:11:31 +0100 Subject: [S390] replace lock_cpu_hotplug with get_online_cpus Git commit 86ef5c9a8edd78e6bf92879f32329d89b2d55b5a forgot a few lock_cpu_hotplug/unlock_cpu_hotplug pairs in arch/s390/kernel/smp.c Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index f66db71..aa37fa1 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -568,9 +568,9 @@ static void __init smp_detect_cpus(void) out: kfree(info); printk(KERN_INFO "CPUs: %d configured, %d standby\n", c_cpus, s_cpus); - lock_cpu_hotplug(); + get_online_cpus(); smp_rescan_cpus(); - unlock_cpu_hotplug(); + put_online_cpus(); } /* @@ -872,7 +872,7 @@ static ssize_t cpu_configure_store(struct sys_device *dev, const char *buf, return -EINVAL; mutex_lock(&smp_cpu_state_mutex); - lock_cpu_hotplug(); + get_online_cpus(); rc = -EBUSY; if (cpu_online(cpu)) goto out; @@ -896,7 +896,7 @@ static ssize_t cpu_configure_store(struct sys_device *dev, const char *buf, break; } out: - unlock_cpu_hotplug(); + put_online_cpus(); mutex_unlock(&smp_cpu_state_mutex); return rc ? rc : count; } @@ -1044,7 +1044,7 @@ static ssize_t rescan_store(struct sys_device *dev, const char *buf, int rc; mutex_lock(&smp_cpu_state_mutex); - lock_cpu_hotplug(); + get_online_cpus(); newcpus = cpu_present_map; rc = smp_rescan_cpus(); if (rc) @@ -1057,7 +1057,7 @@ static ssize_t rescan_store(struct sys_device *dev, const char *buf, } rc = 0; out: - unlock_cpu_hotplug(); + put_online_cpus(); mutex_unlock(&smp_cpu_state_mutex); return rc ? rc : count; } -- cgit v0.10.2