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;