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 <[email protected]>
---
 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

Reply via email to