Commit cb1b057d authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: common i/o layer

From: Cornelia Huck <cohuck@de.ibm.com>
From: Thomas Spatzier <tspat@de.ibm.com>

common i/o layer changes:
 - Prevent double unregister of ccw devices.
 - Move unregister out of the subchannel remove function, to
   avoid live-lock due to hotplug if the root device is currently
   indisposed.
 - Delete pending timer after a machine check.
 - Revert change to allocate qdio queues and SLIBS in DMA memory.
 - Decrement ccw_device_init_count only after ccw_device_register is done.
 - Remove unnecessary check in ccw_hotplug.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a486a6f8
/*
* drivers/s390/cio/css.c
* driver for channel subsystem
* $Revision: 1.82 $
* $Revision: 1.84 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -188,6 +188,12 @@ css_evaluate_subchannel(int irq, int slow)
put_device(&sch->dev);
return 0; /* Already processed. */
}
/*
* We've got a machine check, so running I/O won't get an interrupt.
* Kill any pending timers.
*/
if (sch)
device_kill_pending_timer(sch);
if (!disc && !slow) {
if (sch)
put_device(&sch->dev);
......
......@@ -66,6 +66,7 @@ struct senseid {
struct ccw_device_private {
int state; /* device state */
atomic_t onoff;
unsigned long registered;
__u16 devno; /* device number */
__u16 irq; /* subchannel number */
__u8 imask; /* lpm mask for SNID/SID/SPGID */
......@@ -137,6 +138,9 @@ void device_trigger_reprobe(struct subchannel *);
/* Helper functions for vary on/off. */
void device_set_waiting(struct subchannel *);
/* Machine check helper function. */
void device_kill_pending_timer(struct subchannel *);
/* Helper functions to build lists for the slow path. */
int css_enqueue_subchannel_slow(unsigned long schid);
void css_walk_subchannel_slow_list(void (*fn)(unsigned long));
......
/*
* drivers/s390/cio/device.c
* bus driver for ccw devices
* $Revision: 1.124 $
* $Revision: 1.128 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
......@@ -68,9 +68,6 @@ ccw_hotplug (struct device *dev, char **envp, int num_envp,
if (!cdev)
return -ENODEV;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
/* what we want to pass to /sbin/hotplug */
envp[i++] = buffer;
......@@ -501,9 +498,11 @@ ccw_device_register(struct ccw_device *cdev)
if ((ret = device_add(dev)))
return ret;
if ((ret = device_add_files(dev)))
device_del(dev);
set_bit(1, &cdev->private->registered);
if ((ret = device_add_files(dev))) {
if (test_and_clear_bit(1, &cdev->private->registered))
device_del(dev);
}
return ret;
}
......@@ -589,7 +588,8 @@ ccw_device_do_unreg_rereg(void *data)
} else
need_rename = 0;
device_remove_files(&cdev->dev);
device_del(&cdev->dev);
if (test_and_clear_bit(1, &cdev->private->registered))
device_del(&cdev->dev);
if (need_rename)
snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
sch->schib.pmcw.dev);
......@@ -597,8 +597,11 @@ ccw_device_do_unreg_rereg(void *data)
put_device(&cdev->dev);
return;
}
if (device_add_files(&cdev->dev))
device_unregister(&cdev->dev);
set_bit(1, &cdev->private->registered);
if (device_add_files(&cdev->dev)) {
if (test_and_clear_bit(1, &cdev->private->registered))
device_unregister(&cdev->dev);
}
}
static void
......@@ -620,6 +623,7 @@ io_subchannel_register(void *data)
struct ccw_device *cdev;
struct subchannel *sch;
int ret;
unsigned long flags;
cdev = (struct ccw_device *) data;
sch = to_subchannel(cdev->dev.parent);
......@@ -634,10 +638,14 @@ io_subchannel_register(void *data)
printk (KERN_WARNING "%s: could not register %s\n",
__func__, cdev->dev.bus_id);
put_device(&cdev->dev);
sch->dev.driver_data = 0;
spin_lock_irqsave(&sch->lock, flags);
sch->dev.driver_data = NULL;
spin_unlock_irqrestore(&sch->lock, flags);
kfree (cdev->private);
kfree (cdev);
put_device(&sch->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
return;
}
......@@ -650,6 +658,8 @@ io_subchannel_register(void *data)
cdev->private->flags.recog_done = 1;
put_device(&sch->dev);
wake_up(&cdev->private->wait_q);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
void
......@@ -686,9 +696,11 @@ io_subchannel_recog_done(struct ccw_device *cdev)
if (!get_device(&cdev->dev))
break;
sch = to_subchannel(cdev->dev.parent);
INIT_WORK(&cdev->private->kick_work,
ccw_device_call_sch_unregister, (void *) cdev);
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_call_sch_unregister, (void *) cdev);
queue_work(slow_path_wq, &cdev->private->kick_work);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
break;
case DEV_STATE_BOXED:
/* Device did not respond in time. */
......@@ -699,13 +711,11 @@ io_subchannel_recog_done(struct ccw_device *cdev)
*/
if (!get_device(&cdev->dev))
break;
INIT_WORK(&cdev->private->kick_work,
io_subchannel_register, (void *) cdev);
queue_work(ccw_device_work, &cdev->private->kick_work);
PREPARE_WORK(&cdev->private->kick_work,
io_subchannel_register, (void *) cdev);
queue_work(slow_path_wq, &cdev->private->kick_work);
break;
}
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
static int
......@@ -750,6 +760,7 @@ io_subchannel_probe (struct device *pdev)
struct subchannel *sch;
struct ccw_device *cdev;
int rc;
unsigned long flags;
sch = to_subchannel(pdev);
if (sch->dev.driver_data) {
......@@ -790,6 +801,7 @@ io_subchannel_probe (struct device *pdev)
.parent = pdev,
.release = ccw_device_release,
};
INIT_LIST_HEAD(&cdev->private->kick_work.entry);
/* Do first half of device_register. */
device_initialize(&cdev->dev);
......@@ -801,7 +813,9 @@ io_subchannel_probe (struct device *pdev)
rc = io_subchannel_recog(cdev, to_subchannel(pdev));
if (rc) {
sch->dev.driver_data = 0;
spin_lock_irqsave(&sch->lock, flags);
sch->dev.driver_data = NULL;
spin_unlock_irqrestore(&sch->lock, flags);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
}
......@@ -809,24 +823,40 @@ io_subchannel_probe (struct device *pdev)
return rc;
}
static void
ccw_device_unregister(void *data)
{
struct ccw_device *cdev;
cdev = (struct ccw_device *)data;
if (test_and_clear_bit(1, &cdev->private->registered))
device_unregister(&cdev->dev);
put_device(&cdev->dev);
}
static int
io_subchannel_remove (struct device *dev)
{
struct ccw_device *cdev;
unsigned long flags;
if (!dev->driver_data)
return 0;
cdev = dev->driver_data;
/* Set ccw device to not operational and drop reference. */
spin_lock_irqsave(cdev->ccwlock, flags);
dev->driver_data = NULL;
cdev->private->state = DEV_STATE_NOT_OPER;
spin_unlock_irqrestore(cdev->ccwlock, flags);
/*
* Careful here. Our ccw device might be yet unregistered when
* de-registering its subchannel (machine check during device
* recognition). Better look if the subchannel has children.
* Put unregistration on workqueue to avoid livelocks on the css bus
* semaphore.
*/
if (!list_empty(&dev->children))
device_unregister(&cdev->dev);
dev->driver_data = NULL;
if (get_device(&cdev->dev)) {
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_unregister, (void *) cdev);
queue_work(ccw_device_work, &cdev->private->kick_work);
}
return 0;
}
......
......@@ -93,6 +93,18 @@ ccw_device_set_timeout(struct ccw_device *cdev, int expires)
add_timer(&cdev->private->timer);
}
/* Kill any pending timers after machine check. */
void
device_kill_pending_timer(struct subchannel *sch)
{
struct ccw_device *cdev;
if (!sch->dev.driver_data)
return;
cdev = sch->dev.driver_data;
ccw_device_set_timeout(cdev, 0);
}
/*
* Cancel running i/o. This is called repeatedly since halt/clear are
* asynchronous operations. We do one try with cio_cancel, two tries
......@@ -452,7 +464,8 @@ ccw_device_nopath_notify(void *data)
(void *)cdev);
queue_work(ccw_device_work,
&cdev->private->kick_work);
}
} else
put_device(&sch->dev);
}
} else {
cio_disable_subchannel(sch);
......@@ -1190,8 +1203,8 @@ io_subchannel_irq (struct device *pdev)
CIO_TRACE_EVENT (3, "IRQ");
CIO_TRACE_EVENT (3, pdev->bus_id);
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
if (cdev)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
}
EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
......@@ -56,7 +56,7 @@
#include "ioasm.h"
#include "chsc.h"
#define VERSION_QDIO_C "$Revision: 1.88 $"
#define VERSION_QDIO_C "$Revision: 1.89 $"
/****************** MODULE PARAMETER VARIABLES ********************/
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
......@@ -1401,7 +1401,7 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr,
int result=-ENOMEM;
for (i=0;i<no_input_qs;i++) {
q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL|GFP_DMA);
q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL);
if (!q) {
QDIO_PRINT_ERR("kmalloc of q failed!\n");
......@@ -1410,7 +1410,7 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr,
memset(q,0,sizeof(struct qdio_q));
q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL|GFP_DMA);
q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL);
if (!q->slib) {
QDIO_PRINT_ERR("kmalloc of slib failed!\n");
goto out;
......@@ -1420,7 +1420,7 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr,
}
for (i=0;i<no_output_qs;i++) {
q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL|GFP_DMA);
q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL);
if (!q) {
goto out;
......@@ -1428,7 +1428,7 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr,
memset(q,0,sizeof(struct qdio_q));
q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL|GFP_DMA);
q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL);
if (!q->slib) {
QDIO_PRINT_ERR("kmalloc of slib failed!\n");
goto out;
......
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