Commit 10281b07 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: common i/o layer

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Common i/o layer changes:
 - Don't use bus ids in crw debug feature.
 - Use cio_oper for oper notification to disconnected devices.
 - Remove __get_subchannel_by_stsch.
 - Make cio workqueue a single threaded workqueue.
 - Introduce addiotnal cio_notify workqueue for device driver notification.
 - Switch off path in vpm if cio_start returned -ENODEV.
 - Fix rescan for new subchannels after a logical vary on.
parent 9dfdf818
/* /*
* drivers/s390/cio/chsc.c * drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call * S/390 common I/O routines -- channel subsystem call
* $Revision: 1.107 $ * $Revision: 1.110 $
* *
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -148,7 +148,7 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page) ...@@ -148,7 +148,7 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
*/ */
if (ssd_area->st > 3) { /* uhm, that looks strange... */ if (ssd_area->st > 3) { /* uhm, that looks strange... */
CIO_CRW_EVENT(0, "Strange subchannel type %d" CIO_CRW_EVENT(0, "Strange subchannel type %d"
" for sch %s\n", ssd_area->st, sch->dev.bus_id); " for sch %04x\n", ssd_area->st, sch->irq);
/* /*
* There may have been a new subchannel type defined in the * There may have been a new subchannel type defined in the
* time since this code was written; since we don't know which * time since this code was written; since we don't know which
...@@ -157,8 +157,8 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page) ...@@ -157,8 +157,8 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
return 0; return 0;
} else { } else {
const char *type[4] = {"I/O", "chsc", "message", "ADM"}; const char *type[4] = {"I/O", "chsc", "message", "ADM"};
CIO_CRW_EVENT(6, "ssd: sch %s is %s subchannel\n", CIO_CRW_EVENT(6, "ssd: sch %04x is %s subchannel\n",
sch->dev.bus_id, type[ssd_area->st]); sch->irq, type[ssd_area->st]);
sch->ssd_info.valid = 1; sch->ssd_info.valid = 1;
sch->ssd_info.type = ssd_area->st; sch->ssd_info.type = ssd_area->st;
...@@ -818,6 +818,8 @@ s390_vary_chpid( __u8 chpid, int on) ...@@ -818,6 +818,8 @@ s390_vary_chpid( __u8 chpid, int on)
for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
struct schib schib; struct schib schib;
if (need_rescan)
break;
sch = get_subchannel_by_schid(irq); sch = get_subchannel_by_schid(irq);
if (sch) { if (sch) {
put_device(&sch->dev); put_device(&sch->dev);
...@@ -826,15 +828,12 @@ s390_vary_chpid( __u8 chpid, int on) ...@@ -826,15 +828,12 @@ s390_vary_chpid( __u8 chpid, int on)
if (stsch(irq, &schib)) if (stsch(irq, &schib))
/* We're through */ /* We're through */
break; break;
if (need_rescan)
continue;
/* Put it on the slow path. */ /* Put it on the slow path. */
ret = css_enqueue_subchannel_slow(irq); ret = css_enqueue_subchannel_slow(irq);
if (ret) { if (ret) {
css_clear_subchannel_slow_list(); css_clear_subchannel_slow_list();
need_rescan = 1; need_rescan = 1;
} }
continue;
} }
if (need_rescan || css_slow_subchannels_exist()) if (need_rescan || css_slow_subchannels_exist())
schedule_work(&varyonoff_work); schedule_work(&varyonoff_work);
......
/* /*
* drivers/s390/cio/css.c * drivers/s390/cio/css.c
* driver for channel subsystem * driver for channel subsystem
* $Revision: 1.72 $ * $Revision: 1.73 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -124,24 +124,6 @@ css_probe_device(int irq) ...@@ -124,24 +124,6 @@ css_probe_device(int irq)
return ret; return ret;
} }
static struct subchannel *
__get_subchannel_by_stsch(int irq)
{
struct subchannel *sch;
int cc;
struct schib schib;
cc = stsch(irq, &schib);
if (cc || !schib.pmcw.dnv)
return NULL;
sch = (struct subchannel *)(unsigned long)schib.pmcw.intparm;
if (!sch)
return NULL;
if (get_device(&sch->dev))
return sch;
return NULL;
}
struct subchannel * struct subchannel *
get_subchannel_by_schid(int irq) get_subchannel_by_schid(int irq)
{ {
...@@ -151,13 +133,8 @@ get_subchannel_by_schid(int irq) ...@@ -151,13 +133,8 @@ get_subchannel_by_schid(int irq)
if (!get_bus(&css_bus_type)) if (!get_bus(&css_bus_type))
return NULL; return NULL;
/* Try to get subchannel from pmcw first. */
sch = __get_subchannel_by_stsch(irq);
if (sch)
goto out;
down_read(&css_bus_type.subsys.rwsem); down_read(&css_bus_type.subsys.rwsem);
sch = NULL;
list_for_each(entry, &css_bus_type.devices.list) { list_for_each(entry, &css_bus_type.devices.list) {
dev = get_device(container_of(entry, dev = get_device(container_of(entry,
struct device, bus_list)); struct device, bus_list));
...@@ -170,7 +147,6 @@ get_subchannel_by_schid(int irq) ...@@ -170,7 +147,6 @@ get_subchannel_by_schid(int irq)
sch = NULL; sch = NULL;
} }
up_read(&css_bus_type.subsys.rwsem); up_read(&css_bus_type.subsys.rwsem);
out:
put_bus(&css_bus_type); put_bus(&css_bus_type);
return sch; return sch;
......
/* /*
* drivers/s390/cio/device.c * drivers/s390/cio/device.c
* bus driver for ccw devices * bus driver for ccw devices
* $Revision: 1.113 $ * $Revision: 1.115 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -138,6 +138,7 @@ struct css_driver io_subchannel_driver = { ...@@ -138,6 +138,7 @@ struct css_driver io_subchannel_driver = {
}; };
struct workqueue_struct *ccw_device_work; struct workqueue_struct *ccw_device_work;
struct workqueue_struct *ccw_device_notify_work;
static wait_queue_head_t ccw_device_init_wq; static wait_queue_head_t ccw_device_init_wq;
static atomic_t ccw_device_init_count; static atomic_t ccw_device_init_count;
...@@ -149,20 +150,30 @@ init_ccw_bus_type (void) ...@@ -149,20 +150,30 @@ init_ccw_bus_type (void)
init_waitqueue_head(&ccw_device_init_wq); init_waitqueue_head(&ccw_device_init_wq);
atomic_set(&ccw_device_init_count, 0); atomic_set(&ccw_device_init_count, 0);
ccw_device_work = create_workqueue("cio"); ccw_device_work = create_singlethread_workqueue("cio");
if (!ccw_device_work) if (!ccw_device_work)
return -ENOMEM; /* FIXME: better errno ? */ return -ENOMEM; /* FIXME: better errno ? */
ccw_device_notify_work = create_singlethread_workqueue("cio_notify");
if (!ccw_device_notify_work) {
ret = -ENOMEM; /* FIXME: better errno ? */
goto out_err;
}
if ((ret = bus_register (&ccw_bus_type))) if ((ret = bus_register (&ccw_bus_type)))
return ret; goto out_err;
if ((ret = driver_register(&io_subchannel_driver.drv))) if ((ret = driver_register(&io_subchannel_driver.drv)))
return ret; goto out_err;
wait_event(ccw_device_init_wq, wait_event(ccw_device_init_wq,
atomic_read(&ccw_device_init_count) == 0); atomic_read(&ccw_device_init_count) == 0);
flush_workqueue(ccw_device_work); flush_workqueue(ccw_device_work);
return 0; return 0;
out_err:
if (ccw_device_work)
destroy_workqueue(ccw_device_work);
if (ccw_device_notify_work)
destroy_workqueue(ccw_device_notify_work);
return ret;
} }
static void __exit static void __exit
...@@ -170,6 +181,7 @@ cleanup_ccw_bus_type (void) ...@@ -170,6 +181,7 @@ cleanup_ccw_bus_type (void)
{ {
driver_unregister(&io_subchannel_driver.drv); driver_unregister(&io_subchannel_driver.drv);
bus_unregister(&ccw_bus_type); bus_unregister(&ccw_bus_type);
destroy_workqueue(ccw_device_notify_work);
destroy_workqueue(ccw_device_work); destroy_workqueue(ccw_device_work);
} }
...@@ -553,18 +565,21 @@ io_subchannel_register(void *data) ...@@ -553,18 +565,21 @@ io_subchannel_register(void *data)
wake_up(&cdev->private->wait_q); wake_up(&cdev->private->wait_q);
} }
static void void
device_call_sch_unregister(void *data) ccw_device_call_sch_unregister(void *data)
{ {
struct ccw_device *cdev = data; struct ccw_device *cdev = data;
struct subchannel *sch; struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
device_unregister(&sch->dev); /* Check if device is registered. */
if (!list_empty(&sch->dev.node))
device_unregister(&sch->dev);
/* Reset intparm to zeroes. */ /* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0; sch->schib.pmcw.intparm = 0;
cio_modify(sch); cio_modify(sch);
put_device(&cdev->dev); put_device(&cdev->dev);
put_device(&sch->dev);
} }
/* /*
...@@ -587,7 +602,7 @@ io_subchannel_recog_done(struct ccw_device *cdev) ...@@ -587,7 +602,7 @@ io_subchannel_recog_done(struct ccw_device *cdev)
break; break;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
INIT_WORK(&cdev->private->kick_work, INIT_WORK(&cdev->private->kick_work,
device_call_sch_unregister, (void *) cdev); ccw_device_call_sch_unregister, (void *) cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_work, &cdev->private->kick_work);
break; break;
case DEV_STATE_BOXED: case DEV_STATE_BOXED:
...@@ -982,3 +997,4 @@ EXPORT_SYMBOL(ccw_driver_unregister); ...@@ -982,3 +997,4 @@ EXPORT_SYMBOL(ccw_driver_unregister);
EXPORT_SYMBOL(get_ccwdev_by_busid); EXPORT_SYMBOL(get_ccwdev_by_busid);
EXPORT_SYMBOL(ccw_bus_type); EXPORT_SYMBOL(ccw_bus_type);
EXPORT_SYMBOL(ccw_device_work); EXPORT_SYMBOL(ccw_device_work);
EXPORT_SYMBOL(ccw_device_notify_work);
...@@ -66,6 +66,7 @@ dev_fsm_final_state(struct ccw_device *cdev) ...@@ -66,6 +66,7 @@ dev_fsm_final_state(struct ccw_device *cdev)
} }
extern struct workqueue_struct *ccw_device_work; extern struct workqueue_struct *ccw_device_work;
extern struct workqueue_struct *ccw_device_notify_work;
void io_subchannel_recog_done(struct ccw_device *cdev); void io_subchannel_recog_done(struct ccw_device *cdev);
...@@ -73,7 +74,7 @@ int ccw_device_cancel_halt_clear(struct ccw_device *); ...@@ -73,7 +74,7 @@ int ccw_device_cancel_halt_clear(struct ccw_device *);
int ccw_device_register(struct ccw_device *); int ccw_device_register(struct ccw_device *);
void ccw_device_do_unreg_rereg(void *); void ccw_device_do_unreg_rereg(void *);
void ccw_device_call_sch_unregister(void *);
int ccw_device_recognition(struct ccw_device *); int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *); int ccw_device_online(struct ccw_device *);
......
...@@ -328,7 +328,7 @@ ccw_device_done(struct ccw_device *cdev, int state) ...@@ -328,7 +328,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
cdev->private->flags.donotify = 0; cdev->private->flags.donotify = 0;
PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify, PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify,
(void *)cdev); (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work, &cdev->private->kick_work);
} }
wake_up(&cdev->private->wait_q); wake_up(&cdev->private->wait_q);
...@@ -441,10 +441,13 @@ ccw_device_nopath_notify(void *data) ...@@ -441,10 +441,13 @@ ccw_device_nopath_notify(void *data)
if (get_device(&sch->dev)) { if (get_device(&sch->dev)) {
/* Driver doesn't want to keep device. */ /* Driver doesn't want to keep device. */
cio_disable_subchannel(sch); cio_disable_subchannel(sch);
device_unregister(&sch->dev); if (get_device(&cdev->dev)) {
sch->schib.pmcw.intparm = 0; PREPARE_WORK(&cdev->private->kick_work,
cio_modify(sch); ccw_device_call_sch_unregister,
put_device(&sch->dev); (void *)cdev);
queue_work(ccw_device_work,
&cdev->private->kick_work);
}
} }
} else { } else {
cio_disable_subchannel(sch); cio_disable_subchannel(sch);
...@@ -464,7 +467,7 @@ device_call_nopath_notify(struct subchannel *sch) ...@@ -464,7 +467,7 @@ device_call_nopath_notify(struct subchannel *sch)
cdev = sch->dev.driver_data; cdev = sch->dev.driver_data;
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev); ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work, &cdev->private->kick_work);
} }
...@@ -482,7 +485,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) ...@@ -482,7 +485,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
default: default:
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev); ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work, &cdev->private->kick_work);
ccw_device_done(cdev, DEV_STATE_NOT_OPER); ccw_device_done(cdev, DEV_STATE_NOT_OPER);
break; break;
} }
...@@ -722,7 +725,8 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -722,7 +725,8 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) { if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev); ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work,
&cdev->private->kick_work);
} else } else
dev_fsm_event(cdev, DEV_EVENT_NOTOPER); dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
} else if (cdev->handler) } else if (cdev->handler)
...@@ -798,7 +802,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -798,7 +802,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) { if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev); ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work, &cdev->private->kick_work);
} else if (cdev->private->flags.doverify) } else if (cdev->private->flags.doverify)
/* Start delayed path verification. */ /* Start delayed path verification. */
ccw_device_online_verify(cdev, 0); ccw_device_online_verify(cdev, 0);
...@@ -821,7 +825,8 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -821,7 +825,8 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) { if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev); ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work,
&cdev->private->kick_work);
} else } else
dev_fsm_event(cdev, DEV_EVENT_NOTOPER); dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
return; return;
...@@ -871,7 +876,7 @@ ccw_device_wait4io_irq(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -871,7 +876,7 @@ ccw_device_wait4io_irq(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) { if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev); ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work, &cdev->private->kick_work);
} else if (cdev->private->flags.doverify) } else if (cdev->private->flags.doverify)
ccw_device_online_verify(cdev, 0); ccw_device_online_verify(cdev, 0);
} }
...@@ -894,7 +899,8 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -894,7 +899,8 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) { if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev); ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work,
&cdev->private->kick_work);
} else } else
dev_fsm_event(cdev, DEV_EVENT_NOTOPER); dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
return; return;
...@@ -905,7 +911,7 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -905,7 +911,7 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) { if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_nopath_notify, (void *)cdev); ccw_device_nopath_notify, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_notify_work, &cdev->private->kick_work);
} else if (cdev->private->flags.doverify) } else if (cdev->private->flags.doverify)
/* Start delayed path verification. */ /* Start delayed path verification. */
ccw_device_online_verify(cdev, 0); ccw_device_online_verify(cdev, 0);
......
...@@ -227,7 +227,7 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) ...@@ -227,7 +227,7 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
ret = cio_start (sch, cdev->private->iccws, ret = cio_start (sch, cdev->private->iccws,
cdev->private->imask); cdev->private->imask);
/* ret is 0, -EBUSY, -EACCES or -ENODEV */ /* ret is 0, -EBUSY, -EACCES or -ENODEV */
if (ret != -EACCES) if ((ret != -EACCES) && (ret != -ENODEV))
return ret; return ret;
} }
/* PGID command failed on this path. Switch it off. */ /* PGID command failed on this path. Switch it off. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment