Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > media > main-src > by-pkgid > d0a35cd31c1125e2132804d68547073d > files > 3169

kernel-2.6.18-194.26.1.el5.src.rpm

From: Hendrik Brueckner <brueckner@redhat.com>
Date: Wed, 26 May 2010 08:54:32 -0400
Subject: [s390] dasd: fix race between tasklet and dasd_sleep_on
Message-id: <20100526085431.GA31075@redhat.com>
Patchwork-id: 25806
O-Subject: [RHEL5.6 PATCH 1/1] [s390x] dasd: fix race between tasklet and
	dasd_sleep_on
Bugzilla: 593756
RH-Acked-by: Jarod Wilson <jarod@redhat.com>
RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com>

Description
-----------
A panic might occur when calling the callback function of an cqr.
The various dasd_sleep_on functions use a global wait queue when waiting
for a cqr.  The wait condition checks the status and devlist fields of
the cqr to determine if it is safe to continue.  This evaluation may
return true, although the tasklet has not finished processing of the cqr
and the callback function has not been called yet.
When the callback is finally called, the data in the cqr may already be
invalid.

The sleep_on wait condition needs a safe way to determine if the tasklet
has finished processing.  Use the callback_data field of the cqr to store
a token, which is set by the callback function itself.

Bugzilla
--------
BZ 593756
https://bugzilla.redhat.com/show_bug.cgi?id=593756

Upstream status of the patch
----------------------------
The patch is upstream as of kernel version 2.6.34
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=1c1e093cbf6d3a7576ba0bd10363362a1c5c74ee

Test status
-----------
The patch has been tested and fixes the problem.
The fix has been verified by the IBM test department.


diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 2ad10fb..9581930 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -32,6 +32,9 @@
  */
 #define DASD_CHANQ_MAX_SIZE 4
 
+#define DASD_SLEEPON_START_TAG  (void *) 1
+#define DASD_SLEEPON_END_TAG    (void *) 2
+
 /*
  * SECTION: exported variables of dasd.c
  */
@@ -1524,7 +1527,10 @@ dasd_add_request_tail(struct dasd_ccw_req *req)
 static void
 dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
 {
-	wake_up((wait_queue_head_t *) data);
+	spin_lock_irq(get_ccwdev_lock(cqr->device->cdev));
+	cqr->callback_data = DASD_SLEEPON_END_TAG;
+	spin_unlock_irq(get_ccwdev_lock(cqr->device->cdev));
+	wake_up(&generic_waitq);
 }
 
 static inline int
@@ -1535,9 +1541,7 @@ _wait_for_wakeup(struct dasd_ccw_req *cqr)
 
 	device = cqr->device;
 	spin_lock_irq(get_ccwdev_lock(device->cdev));
-	rc = ((cqr->status == DASD_CQR_DONE ||
-	       cqr->status == DASD_CQR_FAILED) &&
-	      list_empty(&cqr->list));
+	rc = (cqr->callback_data == DASD_SLEEPON_END_TAG);
 	spin_unlock_irq(get_ccwdev_lock(device->cdev));
 	return rc;
 }
@@ -1555,7 +1559,7 @@ dasd_sleep_on(struct dasd_ccw_req * cqr)
 	spin_lock_irq(get_ccwdev_lock(device->cdev));
 
 	cqr->callback = dasd_wakeup_cb;
-	cqr->callback_data = (void *) &generic_waitq;
+	cqr->callback_data = DASD_SLEEPON_START_TAG;
 	cqr->status = DASD_CQR_QUEUED;
 	list_add_tail(&cqr->list, &device->ccw_queue);
 
@@ -1585,7 +1589,7 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr)
 	spin_lock_irq(get_ccwdev_lock(device->cdev));
 
 	cqr->callback = dasd_wakeup_cb;
-	cqr->callback_data = (void *) &generic_waitq;
+	cqr->callback_data = DASD_SLEEPON_START_TAG;
 	cqr->status = DASD_CQR_QUEUED;
 	list_add_tail(&cqr->list, &device->ccw_queue);
 
@@ -1666,7 +1670,7 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
 	}
 
 	cqr->callback = dasd_wakeup_cb;
-	cqr->callback_data = (void *) &generic_waitq;
+	cqr->callback_data = DASD_SLEEPON_START_TAG;
 	cqr->status = DASD_CQR_QUEUED;
 	list_add(&cqr->list, &device->ccw_queue);