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
* S/390 common I/O routines -- channel subsystem call
* $Revision: 1.107 $
* $Revision: 1.110 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -148,7 +148,7 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
*/
if (ssd_area->st > 3) { /* uhm, that looks strange... */
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
* 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)
return 0;
} else {
const char *type[4] = {"I/O", "chsc", "message", "ADM"};
CIO_CRW_EVENT(6, "ssd: sch %s is %s subchannel\n",
sch->dev.bus_id, type[ssd_area->st]);
CIO_CRW_EVENT(6, "ssd: sch %04x is %s subchannel\n",
sch->irq, type[ssd_area->st]);
sch->ssd_info.valid = 1;
sch->ssd_info.type = ssd_area->st;
......@@ -818,6 +818,8 @@ s390_vary_chpid( __u8 chpid, int on)
for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
struct schib schib;
if (need_rescan)
break;
sch = get_subchannel_by_schid(irq);
if (sch) {
put_device(&sch->dev);
......@@ -826,15 +828,12 @@ s390_vary_chpid( __u8 chpid, int on)
if (stsch(irq, &schib))
/* We're through */
break;
if (need_rescan)
continue;
/* Put it on the slow path. */
ret = css_enqueue_subchannel_slow(irq);
if (ret) {
css_clear_subchannel_slow_list();
need_rescan = 1;
}
continue;
}
if (need_rescan || css_slow_subchannels_exist())
schedule_work(&varyonoff_work);
......
/*
* drivers/s390/cio/css.c
* driver for channel subsystem
* $Revision: 1.72 $
* $Revision: 1.73 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -124,24 +124,6 @@ css_probe_device(int irq)
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 *
get_subchannel_by_schid(int irq)
{
......@@ -151,13 +133,8 @@ get_subchannel_by_schid(int irq)
if (!get_bus(&css_bus_type))
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);
sch = NULL;
list_for_each(entry, &css_bus_type.devices.list) {
dev = get_device(container_of(entry,
struct device, bus_list));
......@@ -170,7 +147,6 @@ get_subchannel_by_schid(int irq)
sch = NULL;
}
up_read(&css_bus_type.subsys.rwsem);
out:
put_bus(&css_bus_type);
return sch;
......
/*
* drivers/s390/cio/device.c
* bus driver for ccw devices
* $Revision: 1.113 $
* $Revision: 1.115 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -138,6 +138,7 @@ struct css_driver io_subchannel_driver = {
};
struct workqueue_struct *ccw_device_work;
struct workqueue_struct *ccw_device_notify_work;
static wait_queue_head_t ccw_device_init_wq;
static atomic_t ccw_device_init_count;
......@@ -149,20 +150,30 @@ init_ccw_bus_type (void)
init_waitqueue_head(&ccw_device_init_wq);
atomic_set(&ccw_device_init_count, 0);
ccw_device_work = create_workqueue("cio");
ccw_device_work = create_singlethread_workqueue("cio");
if (!ccw_device_work)
return -ENOMEM; /* FIXME: better errno ? */
ccw_device_notify_work = create_singlethread_workqueue("cio_notify");
if (!ccw_device_notify_work) {
ret = -ENOMEM; /* FIXME: better errno ? */
goto out_err;
}
if ((ret = bus_register (&ccw_bus_type)))
return ret;
goto out_err;
if ((ret = driver_register(&io_subchannel_driver.drv)))
return ret;
goto out_err;
wait_event(ccw_device_init_wq,
atomic_read(&ccw_device_init_count) == 0);
flush_workqueue(ccw_device_work);
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
......@@ -170,6 +181,7 @@ cleanup_ccw_bus_type (void)
{
driver_unregister(&io_subchannel_driver.drv);
bus_unregister(&ccw_bus_type);
destroy_workqueue(ccw_device_notify_work);
destroy_workqueue(ccw_device_work);
}
......@@ -553,18 +565,21 @@ io_subchannel_register(void *data)
wake_up(&cdev->private->wait_q);
}
static void
device_call_sch_unregister(void *data)
void
ccw_device_call_sch_unregister(void *data)
{
struct ccw_device *cdev = data;
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
/* Check if device is registered. */
if (!list_empty(&sch->dev.node))
device_unregister(&sch->dev);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
put_device(&cdev->dev);
put_device(&sch->dev);
}
/*
......@@ -587,7 +602,7 @@ io_subchannel_recog_done(struct ccw_device *cdev)
break;
sch = to_subchannel(cdev->dev.parent);
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);
break;
case DEV_STATE_BOXED:
......@@ -982,3 +997,4 @@ EXPORT_SYMBOL(ccw_driver_unregister);
EXPORT_SYMBOL(get_ccwdev_by_busid);
EXPORT_SYMBOL(ccw_bus_type);
EXPORT_SYMBOL(ccw_device_work);
EXPORT_SYMBOL(ccw_device_notify_work);
......@@ -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_notify_work;
void io_subchannel_recog_done(struct ccw_device *cdev);
......@@ -73,7 +74,7 @@ int ccw_device_cancel_halt_clear(struct ccw_device *);
int ccw_device_register(struct ccw_device *);
void ccw_device_do_unreg_rereg(void *);
void ccw_device_call_sch_unregister(void *);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
......
......@@ -328,7 +328,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
cdev->private->flags.donotify = 0;
PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify,
(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);
......@@ -441,10 +441,13 @@ ccw_device_nopath_notify(void *data)
if (get_device(&sch->dev)) {
/* Driver doesn't want to keep device. */
cio_disable_subchannel(sch);
device_unregister(&sch->dev);
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
put_device(&sch->dev);
if (get_device(&cdev->dev)) {
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_call_sch_unregister,
(void *)cdev);
queue_work(ccw_device_work,
&cdev->private->kick_work);
}
}
} else {
cio_disable_subchannel(sch);
......@@ -464,7 +467,7 @@ device_call_nopath_notify(struct subchannel *sch)
cdev = sch->dev.driver_data;
PREPARE_WORK(&cdev->private->kick_work,
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)
default:
PREPARE_WORK(&cdev->private->kick_work,
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);
break;
}
......@@ -722,7 +725,8 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work,
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
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
} else if (cdev->handler)
......@@ -798,7 +802,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work,
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)
/* Start delayed path verification. */
ccw_device_online_verify(cdev, 0);
......@@ -821,7 +825,8 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work,
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
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
return;
......@@ -871,7 +876,7 @@ ccw_device_wait4io_irq(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work,
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)
ccw_device_online_verify(cdev, 0);
}
......@@ -894,7 +899,8 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work,
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
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
return;
......@@ -905,7 +911,7 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event)
if (!sch->lpm) {
PREPARE_WORK(&cdev->private->kick_work,
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)
/* Start delayed path verification. */
ccw_device_online_verify(cdev, 0);
......
......@@ -227,7 +227,7 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
ret = cio_start (sch, cdev->private->iccws,
cdev->private->imask);
/* ret is 0, -EBUSY, -EACCES or -ENODEV */
if (ret != -EACCES)
if ((ret != -EACCES) && (ret != -ENODEV))
return ret;
}
/* 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