Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3092

kernel-2.6.18-194.11.1.el5.src.rpm

From: Hans-Joachim Picht <hpicht@redhat.com>
Date: Wed, 20 Aug 2008 12:55:18 +0200
Subject: [s390] cio: memory leak when ccw devices are discarded
Message-id: 20080820105518.GA14351@redhat.com
O-Subject: [RHEL5 U3 PATCH 1/1] s390 - cio: memory leak when ccw devices are discarded
Bugzilla: 459495
RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com>

A system runs out of memory when chpids are set offline and online
very often or when disconnected ccw devices are set offline.

When ccw devices are discarded because they are found not
operational or because they are set offline in disconnected state,
the memory used by associated data structures is not freed due to
a reference counting imbalance.

The problem has been fixed correcting reference counting for ccw devices
so that the associated data structures are released when they are no longer needed.

Bugzilla
=========

BZ 459495
https://bugzilla.redhat.com/show_bug.cgi?id=459495

Upstream status of the patch:
=============================

The patch has not been posted upstream because the
upstream code base is not affected by this problem.

Test status:
============

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

Please ACK.

With best regards,

	--Hans

diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index a46ba66..5166e67 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -302,11 +302,15 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
 	 * Forced offline in disconnected state means
 	 * 'throw away device'.
 	 */
+	/* Get subchannel reference for local processing. */
+	if (!get_device(cdev->dev.parent))
+		return;
 	sch = to_subchannel(cdev->dev.parent);
 	css_sch_device_unregister(sch);
 	/* Reset intparm to zeroes. */
 	sch->schib.pmcw.intparm = 0;
 	cio_modify(sch);
+	/* Release subchannel reference for local processing. */
 	put_device(&sch->dev);
 }
 
@@ -731,12 +735,17 @@ static void ccw_device_call_sch_unregister(void *data)
 	struct ccw_device *cdev = data;
 	struct subchannel *sch;
 
+	/* Get subchannel reference for local processing. */
+	if (!get_device(cdev->dev.parent))
+		return;
 	sch = to_subchannel(cdev->dev.parent);
 	css_sch_device_unregister(sch);
 	/* Reset intparm to zeroes. */
 	sch->schib.pmcw.intparm = 0;
 	cio_modify(sch);
+	/* Release cdev reference for workqueue processing.*/
 	put_device(&cdev->dev);
+	/* Release subchannel reference for local processing. */
 	put_device(&sch->dev);
 }
 
@@ -762,6 +771,8 @@ io_subchannel_recog_done(struct ccw_device *cdev)
 		PREPARE_WORK(&cdev->private->kick_work,
 			     ccw_device_call_sch_unregister, (void *) cdev);
 		queue_work(slow_path_wq, &cdev->private->kick_work);
+		/* Release subchannel reference for asynchronous recognition. */
+		put_device(&sch->dev);
 		if (atomic_dec_and_test(&ccw_device_init_count))
 			wake_up(&ccw_device_init_wq);
 		break;
@@ -894,7 +905,10 @@ ccw_device_unregister(void *data)
 
 	cdev = (struct ccw_device *)data;
 	if (test_and_clear_bit(1, &cdev->private->registered))
-		device_unregister(&cdev->dev);
+		device_del(&cdev->dev);
+	/* decrement refcount from device_initialize */
+	put_device(&cdev->dev);
+	/* decrement refcount from calling function */
 	put_device(&cdev->dev);
 }
 
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 842bf2c..1b896df 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -737,6 +737,13 @@ ccw_device_offline(struct ccw_device *cdev)
 {
 	struct subchannel *sch;
 
+	/* Allow ccw_device_offline while disconnected. */
+	if (cdev->private->state == DEV_STATE_DISCONNECTED ||
+	    cdev->private->state == DEV_STATE_NOT_OPER) {
+		cdev->private->flags.donotify = 0;
+		ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+		return 0;
+	}
 	sch = to_subchannel(cdev->dev.parent);
 	if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
 		return -ENODEV;