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@xxxxxxx> --- 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