Blob Blame History Raw
From: Hannes Reinecke <hare@suse.de>
Date: Mon, 20 Apr 2015 11:37:35 +0200
Subject: [PATCH 2/2] ch: fixup refcounting imbalance for SCSI devices
References: bsc#1124235
Patch-Mainline: submitted linux-scsi 2019/03/20

The SCSI device is required to be present during 'ch_probe'
and ch_open(), but as we cannot known whether ch_release()
or ch_remove() will be called first we should blank out the
pointer to the SCSI device in ch_destroy(), not in ch_release().

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 drivers/scsi/ch.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
index 8f426903d7e4..08acd896d4e2 100644
--- a/drivers/scsi/ch.c
+++ b/drivers/scsi/ch.c
@@ -568,6 +568,7 @@ static void ch_destroy(struct kref *ref)
 {
 	scsi_changer *ch = container_of(ref, scsi_changer, ref);
 
+	ch->device = NULL;
 	kfree(ch->dt);
 	kfree(ch);
 }
@@ -579,7 +580,6 @@ ch_release(struct inode *inode, struct file *file)
 
 	mutex_lock(&ch_mutex);
 	scsi_device_put(ch->device);
-	ch->device = NULL;
 	file->private_data = NULL;
 	mutex_unlock(&ch_mutex);
 	kref_put(&ch->ref, ch_destroy);
@@ -596,14 +596,17 @@ ch_open(struct inode *inode, struct file *file)
 	spin_lock(&ch_index_lock);
 	ch = idr_find(&ch_index_idr, minor);
 
-	if (NULL == ch || scsi_device_get(ch->device)) {
+	if (NULL == ch || kref_get_unless_zero(&ch->ref)) {
 		spin_unlock(&ch_index_lock);
 		mutex_unlock(&ch_mutex);
 		return -ENXIO;
 	}
-	kref_get(&ch->ref);
 	spin_unlock(&ch_index_lock);
 
+	if (!ch->device || scsi_device_get(ch->device)) {
+		kref_put(&ch->ref, ch_destroy);
+		return -ENXIO;
+	}
 	file->private_data = ch;
 	mutex_unlock(&ch_mutex);
 	return 0;
@@ -976,6 +979,7 @@ static int ch_remove(struct device *dev)
 
 	spin_lock(&ch_index_lock);
 	idr_remove(&ch_index_idr, ch->minor);
+	dev_set_drvdata(dev, NULL);
 	spin_unlock(&ch_index_lock);
 
 	device_destroy(ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR,ch->minor));
-- 
2.16.4