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