Re: [PATCH] SCSI: Fix refcount breakage with 'echo "1" > scan' when target already present

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 2006-09-05 at 21:39 +0300, Dan Aloni wrote:
> I traced the problem, and figured out that the action above caused the 
> refcount of the kobj in the respective 'struct scsi_target' to be 
> increased without a reason. The bad side effect is that the scsi_host 
> and the scsi_eh_ thread stay alive even after I remove the SCSI driver 
> with rmmod...
> 
> If I do 'echo 1 >" into the 'delete' attr of the target, effectively 
> removing it before I do the scan, the problem doesn't appear (actually,
> it did appear when I did the same with 2.6.16, I guess something was
> fixed along the way).
> 
> I don't think the fix below would be perfect (I think the SCSI layer
> gurus can come up with the something better), but at least it fixes
> the problem for me, so I might as well share it with the world.

There's definitely a problem here.  We have three users of
scsi_alloc_target() in scsi_scan.c; two seem to assume they have to get
the device and the third assumes the device is pre-got.

> It is also possible that I use a broken driver (it's a forward-ported 
> version 3.6.1 of the SATA Marvell driver. It doesn't implement 
> target_alloc, BTW). If you have any idea what needs to be fixed, I'll
> be glad to know.
> 
> Addon: I think there's a missing get_device(parent) in case the
> 'goto retry' branch kicks off. You might want to review it too.

This is spot on.

> Signed-off-by: Dan Aloni <[email protected]>
> 
> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
> index 1bd92b9..fca3a93 100644
> --- a/drivers/scsi/scsi_scan.c
> +++ b/drivers/scsi/scsi_scan.c
> @@ -392,12 +392,14 @@ static struct scsi_target *scsi_alloc_ta
>  	spin_unlock_irqrestore(shost->host_lock, flags);
>  	put_device(parent);
>  	if (found_target->state != STARGET_DEL) {
> +		put_device(&found_target->dev);
>  		kfree(starget);
>  		return found_target;
>  	}
>  	/* Unfortunately, we found a dying target; need to
>  	 * wait until it's dead before we can get a new one */
>  	put_device(&found_target->dev);
> +	get_device(parent);

This would fix it, but a better fix is simply to move the
put_device(parent) within the if().  In general, you never want to drop
and re-acquire a reference if you can avoid it, otherwise you have to
worry about you doing a last put and the device being released before
the get.

How does the attached work (I also corrected a potential reap problem)?

James

diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 31d05ab..325f8c2 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -266,6 +266,18 @@ static struct scsi_target *__scsi_find_t
 	return found_starget;
 }
 
+/**
+ * scsi_alloc_target - allocate a new or find an existing target
+ * @parent:	parent of the target (need not be a scsi host)
+ * @channel:	target channel number (zero if no channels)
+ * @id:		target id number
+ *
+ * Return an existing target if one exists, provided it hasn't already
+ * gone into STARGET_DEL state, otherwise allocate a new target.
+ *
+ * The target is returned with an incremented reference, so the caller 
+ * is responsible for both reaping and doing a last put
+ */
 static struct scsi_target *scsi_alloc_target(struct device *parent,
 					     int channel, uint id)
 {
@@ -331,14 +343,15 @@ static struct scsi_target *scsi_alloc_ta
 			return NULL;
 		}
 	}
+	get_device(dev);
 
 	return starget;
 
  found:
 	found_target->reap_ref++;
 	spin_unlock_irqrestore(shost->host_lock, flags);
-	put_device(parent);
 	if (found_target->state != STARGET_DEL) {
+		put_device(parent);
 		kfree(starget);
 		return found_target;
 	}
@@ -1341,7 +1354,6 @@ struct scsi_device *__scsi_add_device(st
 	if (!starget)
 		return ERR_PTR(-ENOMEM);
 
-	get_device(&starget->dev);
 	mutex_lock(&shost->scan_mutex);
 	if (scsi_host_scan_allowed(shost))
 		scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
@@ -1400,7 +1412,6 @@ static void __scsi_scan_target(struct de
 	if (!starget)
 		return;
 
-	get_device(&starget->dev);
 	if (lun != SCAN_WILD_CARD) {
 		/*
 		 * Scan for a specific host/chan/id/lun.
@@ -1582,7 +1593,8 @@ struct scsi_device *scsi_get_host_dev(st
 	if (sdev) {
 		sdev->sdev_gendev.parent = get_device(&starget->dev);
 		sdev->borken = 0;
-	}
+	} else
+		scsi_target_reap(starget);
 	put_device(&starget->dev);
  out:
 	mutex_unlock(&shost->scan_mutex);


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux