Sophie

Sophie

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

kernel-2.6.18-194.26.1.el5.src.rpm

From: Hendrik Brueckner <brueckner@redhat.com>
Date: Wed, 4 Aug 2010 10:02:59 -0400
Subject: [s390] dasd: allocate fallback cqr for reserve/release
Message-id: <1280916181-27024-7-git-send-email-brueckner@redhat.com>
Patchwork-id: 27375
O-Subject: [RHEL5.6 PATCH 6/8] [s390x] dasd: allocate fallback cqr for
	reserve/release
Bugzilla: 619465
RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com>

Description
-----------
DASD reserve/release ioctls fail with return code ENOMEM

The DASD reserve and release ioctls use the preallocated
memory pool of the respective device to build their CCW
requests. However, when the device is busy, the pool may
already be empty and the ioctl fails.
Usually this can be recovered by calling the ioctl again,
but in a situation in which we need to issue an unconditional
reserve to make a device operational again, this would be
not recoverable.

To avoid a failure due to lack of memory, preallocate enough
memory for a single reserve/release request, which can be
used if normal allocation fails.

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

Upstream status of the patch
----------------------------
The patch will be sent upstream for inclustion into the next kernel
release.

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_eckd.c b/drivers/s390/block/dasd_eckd.c
index 877d62f..7aa2077 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -18,6 +18,7 @@
 #include <linux/bio.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/mutex.h>
 
 #include <asm/debug.h>
 #include <asm/idals.h>
@@ -91,6 +92,14 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
 
 static struct ccw_driver dasd_eckd_driver; /* see below */
 
+/* emergency request for reserve/release */
+static struct {
+	struct dasd_ccw_req cqr;
+	struct ccw1 ccw;
+	char data[32];
+} *dasd_reserve_req;
+static DEFINE_MUTEX(dasd_reserve_mutex);
+
 /* initial attempt at a probe function. this can be simplified once
  * the other detection code is gone */
 static int
@@ -1521,16 +1530,25 @@ dasd_eckd_release(struct dasd_device *device)
 {
 	struct dasd_ccw_req *cqr;
 	int rc;
+	int useglobal;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
 
+	useglobal = 0;
 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
 				   1, 32, device);
 	if (IS_ERR(cqr)) {
-		DEV_MESSAGE(KERN_WARNING, device, "%s",
-			    "Could not allocate initialization request");
-		return PTR_ERR(cqr);
+		mutex_lock(&dasd_reserve_mutex);
+		useglobal = 1;
+		cqr = &dasd_reserve_req->cqr;
+		memset(cqr, 0, sizeof(*cqr));
+		memset(&dasd_reserve_req->ccw, 0,
+		       sizeof(dasd_reserve_req->ccw));
+		cqr->cpaddr = &dasd_reserve_req->ccw;
+		cqr->data = &dasd_reserve_req->data;
+		strncpy((char *) &cqr->magic, dasd_eckd_discipline.name, 4);
+		ASCEBC((char *) &cqr->magic, 4);
 	}
 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RELEASE;
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
@@ -1546,7 +1564,10 @@ dasd_eckd_release(struct dasd_device *device)
 
 	rc = dasd_sleep_on_immediatly(cqr);
 
-	dasd_sfree_request(cqr, cqr->device);
+	if (useglobal)
+		mutex_unlock(&dasd_reserve_mutex);
+	else
+		dasd_sfree_request(cqr, cqr->device);
 	return rc;
 }
 
@@ -1561,16 +1582,25 @@ dasd_eckd_reserve(struct dasd_device *device)
 {
 	struct dasd_ccw_req *cqr;
 	int rc;
+	int useglobal;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
 
+	useglobal = 0;
 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
 				   1, 32, device);
 	if (IS_ERR(cqr)) {
-		DEV_MESSAGE(KERN_WARNING, device, "%s",
-			    "Could not allocate initialization request");
-		return PTR_ERR(cqr);
+		mutex_lock(&dasd_reserve_mutex);
+		useglobal = 1;
+		cqr = &dasd_reserve_req->cqr;
+		memset(cqr, 0, sizeof(*cqr));
+		memset(&dasd_reserve_req->ccw, 0,
+		       sizeof(dasd_reserve_req->ccw));
+		cqr->cpaddr = &dasd_reserve_req->ccw;
+		cqr->data = &dasd_reserve_req->data;
+		strncpy((char *) &cqr->magic, dasd_eckd_discipline.name, 4);
+		ASCEBC((char *) &cqr->magic, 4);
 	}
 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RESERVE;
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
@@ -1586,7 +1616,10 @@ dasd_eckd_reserve(struct dasd_device *device)
 
 	rc = dasd_sleep_on_immediatly(cqr);
 
-	dasd_sfree_request(cqr, cqr->device);
+	if (useglobal)
+		mutex_unlock(&dasd_reserve_mutex);
+	else
+		dasd_sfree_request(cqr, cqr->device);
 	return rc;
 }
 
@@ -1600,16 +1633,25 @@ dasd_eckd_steal_lock(struct dasd_device *device)
 {
 	struct dasd_ccw_req *cqr;
 	int rc;
+	int useglobal;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
 
+	useglobal = 0;
 	cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
 				   1, 32, device);
 	if (IS_ERR(cqr)) {
-		DEV_MESSAGE(KERN_WARNING, device, "%s",
-			    "Could not allocate initialization request");
-		return PTR_ERR(cqr);
+		mutex_lock(&dasd_reserve_mutex);
+		useglobal = 1;
+		cqr = &dasd_reserve_req->cqr;
+		memset(cqr, 0, sizeof(*cqr));
+		memset(&dasd_reserve_req->ccw, 0,
+		       sizeof(dasd_reserve_req->ccw));
+		cqr->cpaddr = &dasd_reserve_req->ccw;
+		cqr->data = &dasd_reserve_req->data;
+		strncpy((char *) &cqr->magic, dasd_eckd_discipline.name, 4);
+		ASCEBC((char *) &cqr->magic, 4);
 	}
 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SLCK;
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
@@ -1625,7 +1667,10 @@ dasd_eckd_steal_lock(struct dasd_device *device)
 
 	rc = dasd_sleep_on_immediatly(cqr);
 
-	dasd_sfree_request(cqr, cqr->device);
+	if (useglobal)
+		mutex_unlock(&dasd_reserve_mutex);
+	else
+		dasd_sfree_request(cqr, cqr->device);
 	return rc;
 }
 
@@ -2030,14 +2075,25 @@ static struct dasd_discipline dasd_eckd_discipline = {
 static int __init
 dasd_eckd_init(void)
 {
+	int ret;
+
 	ASCEBC(dasd_eckd_discipline.ebcname, 4);
-	return ccw_driver_register(&dasd_eckd_driver);
+	dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req),
+				   GFP_KERNEL | GFP_DMA);
+	if (!dasd_reserve_req)
+		return -ENOMEM;
+	ret = ccw_driver_register(&dasd_eckd_driver);
+	if (ret)
+		kfree(dasd_reserve_req);
+	return ret;
+
 }
 
 static void __exit
 dasd_eckd_cleanup(void)
 {
 	ccw_driver_unregister(&dasd_eckd_driver);
+	kfree(dasd_reserve_req);
 }
 
 module_init(dasd_eckd_init);