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

Common i/o layer changes:
 - qdio: Lose the adapter lock for thin interrupts to improve performance
   and do unregister of the adapter interrupt handler with rcu.
 - ccwgroup: Fix error handling when creating a ccwgroup device.
 - Convert the slow crw kernel thread to a single threaded workqueue.
 - Use the slow crw workqueue to unregister a subchannel after it was
   found not operational to serialize it with other possible unregister/
   register events coming in via machine checks.
 - Trigger a rescan of the css via the slow path if a missing channel path
   is found in __recover_lost_chpids.
 - Use saner default levels for the debug feature, add some debugging code.
 - Remove request_irq and free_irq stubs.
 - Remove bogus inlines.

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/s390/cio/airq.c       |   38 ++++-------------
 25-akpm/drivers/s390/cio/ccwgroup.c   |   24 +++++++---
 25-akpm/drivers/s390/cio/chsc.c       |   39 +++++++++--------
 25-akpm/drivers/s390/cio/cio.c        |    8 +--
 25-akpm/drivers/s390/cio/css.c        |   75 +++++++++++++++++-----------------
 25-akpm/drivers/s390/cio/css.h        |    4 +
 25-akpm/drivers/s390/cio/device.c     |   15 ++++--
 25-akpm/drivers/s390/cio/device_fsm.c |   14 ------
 25-akpm/drivers/s390/cio/requestirq.c |   17 -------
 25-akpm/drivers/s390/s390mach.c       |   25 ++---------
 10 files changed, 109 insertions(+), 150 deletions(-)

diff -puN drivers/s390/cio/airq.c~s390-2-4-common-i-o-layer drivers/s390/cio/airq.c
--- 25/drivers/s390/cio/airq.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/airq.c	Wed Jun  2 14:34:30 2004
@@ -2,7 +2,7 @@
  *  drivers/s390/cio/airq.c
  *   S/390 common I/O routines -- support for adapter interruptions
  *
- *   $Revision: 1.11 $
+ *   $Revision: 1.12 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *			      IBM Corporation
@@ -14,11 +14,11 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/rcupdate.h>
 
 #include "cio_debug.h"
 #include "airq.h"
 
-static spinlock_t adapter_lock = SPIN_LOCK_UNLOCKED;
 static adapter_int_handler_t adapter_handler;
 
 /*
@@ -40,23 +40,17 @@ s390_register_adapter_interrupt (adapter
 
 	CIO_TRACE_EVENT (4, "rgaint");
 
-	spin_lock (&adapter_lock);
-
 	if (handler == NULL)
 		ret = -EINVAL;
-	else if (adapter_handler)
-		ret = -EBUSY;
-	else {
-		adapter_handler = handler;
-		ret = 0;
-	}
-
-	spin_unlock (&adapter_lock);
+	else
+		ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0);
+	if (!ret)
+		synchronize_kernel();
 
 	sprintf (dbf_txt, "ret:%d", ret);
 	CIO_TRACE_EVENT (4, dbf_txt);
 
-	return (ret);
+	return ret;
 }
 
 int
@@ -67,38 +61,26 @@ s390_unregister_adapter_interrupt (adapt
 
 	CIO_TRACE_EVENT (4, "urgaint");
 
-	spin_lock (&adapter_lock);
-
 	if (handler == NULL)
 		ret = -EINVAL;
-	else if (handler != adapter_handler)
-		ret = -EINVAL;
 	else {
 		adapter_handler = NULL;
+		synchronize_kernel();
 		ret = 0;
 	}
-
-	spin_unlock (&adapter_lock);
-
 	sprintf (dbf_txt, "ret:%d", ret);
 	CIO_TRACE_EVENT (4, dbf_txt);
 
-	return (ret);
+	return ret;
 }
 
 void
 do_adapter_IO (void)
 {
-	CIO_TRACE_EVENT (4, "doaio");
-
-	spin_lock (&adapter_lock);
+	CIO_TRACE_EVENT (6, "doaio");
 
 	if (adapter_handler)
 		(*adapter_handler) ();
-
-	spin_unlock (&adapter_lock);
-
-	return;
 }
 
 EXPORT_SYMBOL (s390_register_adapter_interrupt);
diff -puN drivers/s390/cio/ccwgroup.c~s390-2-4-common-i-o-layer drivers/s390/cio/ccwgroup.c
--- 25/drivers/s390/cio/ccwgroup.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/ccwgroup.c	Wed Jun  2 14:34:30 2004
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/ccwgroup.c
  *  bus driver for ccwgroup
- *   $Revision: 1.27 $
+ *   $Revision: 1.28 $
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *                       IBM Corporation
@@ -179,12 +179,12 @@ ccwgroup_create(struct device *root,
 		    || gdev->cdev[i]->id.driver_info !=
 		    gdev->cdev[0]->id.driver_info) {
 			rc = -EINVAL;
-			goto error;
+			goto free_dev;
 		}
 		/* Don't allow a device to belong to more than one group. */
 		if (gdev->cdev[i]->dev.driver_data) {
 			rc = -EINVAL;
-			goto error;
+			goto free_dev;
 		}
 	}
 	for (i = 0; i < argc; i++)
@@ -207,8 +207,8 @@ ccwgroup_create(struct device *root,
 	rc = device_register(&gdev->dev);
 	
 	if (rc)
-		goto error;
-
+		goto free_dev;
+	get_device(&gdev->dev);
 	rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
 
 	if (rc) {
@@ -217,20 +217,28 @@ ccwgroup_create(struct device *root,
 	}
 
 	rc = __ccwgroup_create_symlinks(gdev);
-	if (!rc)
+	if (!rc) {
+		put_device(&gdev->dev);
 		return 0;
-
+	}
 	device_remove_file(&gdev->dev, &dev_attr_ungroup);
 	device_unregister(&gdev->dev);
 error:
 	for (i = 0; i < argc; i++)
 		if (gdev->cdev[i]) {
 			put_device(&gdev->cdev[i]->dev);
+			gdev->cdev[i]->dev.driver_data = NULL;
+		}
+	put_device(&gdev->dev);
+	return rc;
+free_dev:
+	for (i = 0; i < argc; i++)
+		if (gdev->cdev[i]) {
+			put_device(&gdev->cdev[i]->dev);
 			if (del_drvdata)
 				gdev->cdev[i]->dev.driver_data = NULL;
 		}
 	kfree(gdev);
-
 	return rc;
 }
 
diff -puN drivers/s390/cio/chsc.c~s390-2-4-common-i-o-layer drivers/s390/cio/chsc.c
--- 25/drivers/s390/cio/chsc.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/chsc.c	Wed Jun  2 14:34:30 2004
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/chsc.c
  *   S/390 common I/O routines -- channel subsystem call
- *   $Revision: 1.110 $
+ *   $Revision: 1.111 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *			      IBM Corporation
@@ -62,11 +62,11 @@ chpid_is_actually_online(int chp)
 	int state;
 
 	state = get_chp_status(chp);
-	if (state < 0)
-		new_channel_path(chp);
-	else
+	if (state < 0) {
+		need_rescan = 1;
+		queue_work(slow_path_wq, &slow_path_work);
+	} else
 		WARN_ON(!state);
-	/* FIXME: should notify other subchannels here */
 }
 
 /* FIXME: this is _always_ called for every subchannel. shouldn't we
@@ -285,8 +285,10 @@ out_unlock:
 out_unreg:
 	spin_unlock(&sch->lock);
 	sch->lpm = 0;
-	/* We can't block here. */
-	device_call_nopath_notify(sch);
+	if (css_enqueue_subchannel_slow(sch->irq)) {
+		css_clear_subchannel_slow_list();
+		need_rescan = 1;
+	}
 	return 0;
 }
 
@@ -303,6 +305,9 @@ s390_set_chpid_offline( __u8 chpid)
 
 	bus_for_each_dev(&css_bus_type, NULL, &chpid,
 			 s390_subchannel_remove_chpid);
+
+	if (need_rescan || css_slow_subchannels_exist())
+		queue_work(slow_path_wq, &slow_path_work);
 }
 
 static int
@@ -737,10 +742,12 @@ __s390_subchannel_vary_chpid(struct subc
 			 * can successfully terminate, even using the
 			 * just varied off path. Then kill it.
 			 */
-			if (!__check_for_io_and_kill(sch, chp) && !sch->lpm)
-				/* Get over with it now. */
-				device_call_nopath_notify(sch);
-			else if (sch->driver && sch->driver->verify)
+			if (!__check_for_io_and_kill(sch, chp) && !sch->lpm) {
+				if (css_enqueue_subchannel_slow(sch->irq)) {
+					css_clear_subchannel_slow_list();
+					need_rescan = 1;
+				}
+			} else if (sch->driver && sch->driver->verify)
 				sch->driver->verify(&sch->dev);
 		}
 		break;
@@ -773,11 +780,6 @@ s390_subchannel_vary_chpid_on(struct dev
 	return 0;
 }
 
-extern void css_trigger_slow_path(void);
-typedef void (*workfunc)(void *);
-static DECLARE_WORK(varyonoff_work, (workfunc)css_trigger_slow_path,
-		    NULL);
-
 /*
  * Function: s390_vary_chpid
  * Varies the specified chpid online or offline
@@ -813,7 +815,7 @@ s390_vary_chpid( __u8 chpid, int on)
 			 s390_subchannel_vary_chpid_on :
 			 s390_subchannel_vary_chpid_off);
 	if (!on)
-		return 0;
+		goto out;
 	/* Scan for new devices on varied on path. */
 	for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
 		struct schib schib;
@@ -835,8 +837,9 @@ s390_vary_chpid( __u8 chpid, int on)
 			need_rescan = 1;
 		}
 	}
+out:
 	if (need_rescan || css_slow_subchannels_exist())
-		schedule_work(&varyonoff_work);
+		queue_work(slow_path_wq, &slow_path_work);
 	return 0;
 }
 
diff -puN drivers/s390/cio/cio.c~s390-2-4-common-i-o-layer drivers/s390/cio/cio.c
--- 25/drivers/s390/cio/cio.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/cio.c	Wed Jun  2 14:34:30 2004
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/cio.c
  *   S/390 common I/O routines -- low level i/o calls
- *   $Revision: 1.121 $
+ *   $Revision: 1.123 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *			      IBM Corporation
@@ -67,17 +67,17 @@ cio_debug_init (void)
 	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, 6);
+	debug_set_level (cio_debug_msg_id, 2);
 	cio_debug_trace_id = debug_register ("cio_trace", 4, 4, 8);
 	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, 6);
+	debug_set_level (cio_debug_trace_id, 2);
 	cio_debug_crw_id = debug_register ("cio_crw", 2, 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, 6);
+	debug_set_level (cio_debug_crw_id, 2);
 	pr_debug("debugging initialized\n");
 	return 0;
 
diff -puN drivers/s390/cio/css.c~s390-2-4-common-i-o-layer drivers/s390/cio/css.c
--- 25/drivers/s390/cio/css.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/css.c	Wed Jun  2 14:34:30 2004
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/css.c
  *  driver for channel subsystem
- *   $Revision: 1.74 $
+ *   $Revision: 1.77 $
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *			 IBM Corporation
@@ -166,10 +166,12 @@ css_get_subchannel_status(struct subchan
 	if (sch && sch->schib.pmcw.dnv &&
 	    (schib.pmcw.dev != sch->schib.pmcw.dev))
 		return CIO_REVALIDATE;
+	if (sch && !sch->lpm)
+		return CIO_NO_PATH;
 	return CIO_OPER;
 }
 	
-static inline int
+static int
 css_evaluate_subchannel(int irq, int slow)
 {
 	int event, ret, disc;
@@ -188,7 +190,11 @@ css_evaluate_subchannel(int irq, int slo
 		return -EAGAIN; /* Will be done on the slow path. */
 	}
 	event = css_get_subchannel_status(sch, irq);
+	CIO_MSG_EVENT(4, "Evaluating schid %04x, event %d, %s, %s path.\n",
+		      irq, event, sch?(disc?"disconnected":"normal"):"unknown",
+		      slow?"slow":"fast");
 	switch (event) {
+	case CIO_NO_PATH:
 	case CIO_GONE:
 		if (!sch) {
 			/* Never used this subchannel. Ignore. */
@@ -196,7 +202,8 @@ css_evaluate_subchannel(int irq, int slo
 			break;
 		}
 		if (sch->driver && sch->driver->notify &&
-		    sch->driver->notify(&sch->dev, CIO_GONE)) {
+		    sch->driver->notify(&sch->dev, event)) {
+			cio_disable_subchannel(sch);
 			device_set_disconnected(sch);
 			ret = 0;
 			break;
@@ -205,6 +212,7 @@ css_evaluate_subchannel(int irq, int slo
 		 * Unregister subchannel.
 		 * The device will be killed automatically.
 		 */
+		cio_disable_subchannel(sch);
 		device_unregister(&sch->dev);
 		/* Reset intparm to zeroes. */
 		sch->schib.pmcw.intparm = 0;
@@ -266,23 +274,44 @@ css_rescan_devices(void)
 	}
 }
 
-static void
-css_evaluate_slow_subchannel(unsigned long schid)
-{
-	css_evaluate_subchannel(schid, 1);
-}
+struct slow_subchannel {
+	struct list_head slow_list;
+	unsigned long schid;
+};
 
-void
+static LIST_HEAD(slow_subchannels_head);
+static spinlock_t slow_subchannel_lock = SPIN_LOCK_UNLOCKED;
+
+static void
 css_trigger_slow_path(void)
 {
+	CIO_TRACE_EVENT(4, "slowpath");
+
 	if (need_rescan) {
 		need_rescan = 0;
 		css_rescan_devices();
 		return;
 	}
-	css_walk_subchannel_slow_list(css_evaluate_slow_subchannel);
+
+	spin_lock_irq(&slow_subchannel_lock);
+	while (!list_empty(&slow_subchannels_head)) {
+		struct slow_subchannel *slow_sch =
+			list_entry(slow_subchannels_head.next,
+				   struct slow_subchannel, slow_list);
+
+		list_del_init(slow_subchannels_head.next);
+		spin_unlock_irq(&slow_subchannel_lock);
+		css_evaluate_subchannel(slow_sch->schid, 1);
+		spin_lock_irq(&slow_subchannel_lock);
+		kfree(slow_sch);
+	}
+	spin_unlock_irq(&slow_subchannel_lock);
 }
 
+typedef void (*workfunc)(void *);
+DECLARE_WORK(slow_path_work, (workfunc)css_trigger_slow_path, NULL);
+struct workqueue_struct *slow_path_wq;
+
 /*
  * Rescan for new devices. FIXME: This is slow.
  * This function is called when we have lost CRWs due to overflows and we have
@@ -443,14 +472,6 @@ s390_root_dev_unregister(struct device *
 		device_unregister(dev);
 }
 
-struct slow_subchannel {
-	struct list_head slow_list;
-	unsigned long schid;
-};
-
-static LIST_HEAD(slow_subchannels_head);
-static spinlock_t slow_subchannel_lock = SPIN_LOCK_UNLOCKED;
-
 int
 css_enqueue_subchannel_slow(unsigned long schid)
 {
@@ -484,25 +505,7 @@ css_clear_subchannel_slow_list(void)
 	spin_unlock_irqrestore(&slow_subchannel_lock, flags);
 }
 
-void
-css_walk_subchannel_slow_list(void (*fn)(unsigned long))
-{
-	unsigned long flags;
 
-	spin_lock_irqsave(&slow_subchannel_lock, flags);
-	while (!list_empty(&slow_subchannels_head)) {
-		struct slow_subchannel *slow_sch =
-			list_entry(slow_subchannels_head.next,
-				   struct slow_subchannel, slow_list);
-
-		list_del_init(slow_subchannels_head.next);
-		spin_unlock_irqrestore(&slow_subchannel_lock, flags);
-		fn(slow_sch->schid);
-		spin_lock_irqsave(&slow_subchannel_lock, flags);
-		kfree(slow_sch);
-	}
-	spin_unlock_irqrestore(&slow_subchannel_lock, flags);
-}
 
 int
 css_slow_subchannels_exist(void)
diff -puN drivers/s390/cio/css.h~s390-2-4-common-i-o-layer drivers/s390/cio/css.h
--- 25/drivers/s390/cio/css.h~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/css.h	Wed Jun  2 14:34:30 2004
@@ -136,7 +136,6 @@ void device_trigger_reprobe(struct subch
 
 /* Helper functions for vary on/off. */
 void device_set_waiting(struct subchannel *);
-void device_call_nopath_notify(struct subchannel *);
 
 /* Helper functions to build lists for the slow path. */
 int css_enqueue_subchannel_slow(unsigned long schid);
@@ -144,4 +143,7 @@ void css_walk_subchannel_slow_list(void 
 void css_clear_subchannel_slow_list(void);
 int css_slow_subchannels_exist(void);
 extern int need_rescan;
+
+extern struct workqueue_struct *slow_path_wq;
+extern struct work_struct slow_path_work;
 #endif
diff -puN drivers/s390/cio/device.c~s390-2-4-common-i-o-layer drivers/s390/cio/device.c
--- 25/drivers/s390/cio/device.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/device.c	Wed Jun  2 14:34:30 2004
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/device.c
  *  bus driver for ccw devices
- *   $Revision: 1.117 $
+ *   $Revision: 1.119 $
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *			 IBM Corporation
@@ -159,6 +159,11 @@ init_ccw_bus_type (void)
 		ret = -ENOMEM; /* FIXME: better errno ? */
 		goto out_err;
 	}
+	slow_path_wq = create_singlethread_workqueue("kslowcrw");
+	if (!slow_path_wq) {
+		ret = -ENOMEM; /* FIXME: better errno ? */
+		goto out_err;
+	}
 	if ((ret = bus_register (&ccw_bus_type)))
 		goto out_err;
 
@@ -174,6 +179,8 @@ out_err:
 		destroy_workqueue(ccw_device_work);
 	if (ccw_device_notify_work)
 		destroy_workqueue(ccw_device_notify_work);
+	if (slow_path_wq)
+		destroy_workqueue(slow_path_wq);
 	return ret;
 }
 
@@ -646,9 +653,7 @@ ccw_device_call_sch_unregister(void *dat
 	struct subchannel *sch;
 
 	sch = to_subchannel(cdev->dev.parent);
-	/* Check if device is registered. */
-	if (!list_empty(&sch->dev.node))
-		device_unregister(&sch->dev);
+	device_unregister(&sch->dev);
 	/* Reset intparm to zeroes. */
 	sch->schib.pmcw.intparm = 0;
 	cio_modify(sch);
@@ -677,7 +682,7 @@ io_subchannel_recog_done(struct ccw_devi
 		sch = to_subchannel(cdev->dev.parent);
 		INIT_WORK(&cdev->private->kick_work,
 			  ccw_device_call_sch_unregister, (void *) cdev);
-		queue_work(ccw_device_work, &cdev->private->kick_work);
+		queue_work(slow_path_wq, &cdev->private->kick_work);
 		break;
 	case DEV_STATE_BOXED:
 		/* Device did not respond in time. */
diff -puN drivers/s390/cio/device_fsm.c~s390-2-4-common-i-o-layer drivers/s390/cio/device_fsm.c
--- 25/drivers/s390/cio/device_fsm.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/device_fsm.c	Wed Jun  2 14:34:30 2004
@@ -459,20 +459,6 @@ ccw_device_nopath_notify(void *data)
 }
 
 void
-device_call_nopath_notify(struct subchannel *sch)
-{
-	struct ccw_device *cdev;
-
-	if (!sch->dev.driver_data)
-		return;
-	cdev = sch->dev.driver_data;
-	PREPARE_WORK(&cdev->private->kick_work,
-		     ccw_device_nopath_notify, (void *)cdev);
-	queue_work(ccw_device_notify_work, &cdev->private->kick_work);
-}
-
-
-void
 ccw_device_verify_done(struct ccw_device *cdev, int err)
 {
 	cdev->private->flags.doverify = 0;
diff -puN drivers/s390/cio/requestirq.c~s390-2-4-common-i-o-layer drivers/s390/cio/requestirq.c
--- 25/drivers/s390/cio/requestirq.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/cio/requestirq.c	Wed Jun  2 14:34:30 2004
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/requestirq.c
  *   S/390 common I/O routines -- enabling and disabling of devices
- *   $Revision: 1.45 $
+ *   $Revision: 1.46 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *			      IBM Corporation
@@ -18,21 +18,6 @@
 
 #include "css.h"
 
-/* for compatiblity only... */
-int
-request_irq (unsigned int irq,
-	     void (*handler) (int, void *, struct pt_regs *),
-	     unsigned long irqflags, const char *devname, void *dev_id)
-{
-	return -EINVAL;
-}
-
-/* for compatiblity only... */
-void
-free_irq (unsigned int irq, void *dev_id)
-{
-}
-
 struct pgid global_pgid;
 EXPORT_SYMBOL_GPL(global_pgid);
 
diff -puN drivers/s390/s390mach.c~s390-2-4-common-i-o-layer drivers/s390/s390mach.c
--- 25/drivers/s390/s390mach.c~s390-2-4-common-i-o-layer	Wed Jun  2 14:34:30 2004
+++ 25-akpm/drivers/s390/s390mach.c	Wed Jun  2 14:34:30 2004
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/workqueue.h>
 
 #include <asm/lowcore.h>
 
@@ -21,13 +22,14 @@
 // #define DBG(args,...) do {} while (0);
 
 static struct semaphore m_sem;
-static struct semaphore s_sem;
 
 extern int css_process_crw(int);
 extern int chsc_process_crw(void);
 extern int chp_process_crw(int, int);
 extern void css_reiterate_subchannels(void);
-extern void css_trigger_slow_path(void);
+
+extern struct workqueue_struct *slow_path_wq;
+extern struct work_struct slow_path_work;
 
 static void
 s390_handle_damage(char *msg)
@@ -39,21 +41,6 @@ s390_handle_damage(char *msg)
 	disabled_wait((unsigned long) __builtin_return_address(0));
 }
 
-static int
-s390_mchk_slow_path(void *param)
-{
-	struct semaphore *sem;
-
-	sem = (struct semaphore *)param;
-	/* Set a nice name. */
-	daemonize("kslowcrw");
-repeat:
-	down_interruptible(sem);
-	css_trigger_slow_path();
-	goto repeat;
-	return 0;
-}
-
 /*
  * Retrieve CRWs and call function to handle event.
  *
@@ -130,7 +117,7 @@ repeat:
 		}
 	}
 	if (slow)
-		up(&s_sem);
+		queue_work(slow_path_wq, &slow_path_work);
 	goto repeat;
 	return 0;
 }
@@ -202,7 +189,6 @@ static int
 machine_check_init(void)
 {
 	init_MUTEX_LOCKED(&m_sem);
-	init_MUTEX_LOCKED( &s_sem );
 	ctl_clear_bit(14, 25);	/* disable damage MCH */
 	ctl_set_bit(14, 26);	/* enable degradation MCH */
 	ctl_set_bit(14, 27);	/* enable system recovery MCH */
@@ -226,7 +212,6 @@ static int __init
 machine_check_crw_init (void)
 {
 	kernel_thread(s390_collect_crw_info, &m_sem, CLONE_FS|CLONE_FILES);
-	kernel_thread(s390_mchk_slow_path, &s_sem, CLONE_FS|CLONE_FILES);
 	ctl_set_bit(14, 28);	/* enable channel report MCH */
 	return 0;
 }
_