On Fri, 13 Dec 2013, James Bottomley wrote:
> On Fri, 2013-12-13 at 16:06 -0500, Alan Stern wrote:
> > On Fri, 13 Dec 2013, James Bottomley wrote:
> >
> > > Actually, I think I have this figured out. There's a thinko in one of
> > > the scsi_target_reap() cases. The original (and still existing) problem
> > > with targets is that nothing creates them and nothing destroys them, so,
> > > while we could rely on the refcounting of the device model to preserve
> > > the actual target object, we had no idea when to remove it from
> > > visibility. That was the job of the reap reference, to track
> > > visibility. It looks like the reap on device last put is occurring too
> > > late. I think we should reap immediately after doing the sdev
> > > device_del, so does this fix the warn on? (I'm not sure because no-one
> > > has actually posted a backtrace, but it sounds like this is the
> > > problem).
> > >
> > > James
> > >
> > > ---
> > >
> > > diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
> > > index 8ff62c2..98d4eb3 100644
> > > --- a/drivers/scsi/scsi_sysfs.c
> > > +++ b/drivers/scsi/scsi_sysfs.c
> > > @@ -399,8 +399,6 @@ static void
> > > scsi_device_dev_release_usercontext(struct work_struct *work)
> > > /* NULL queue means the device can't be used */
> > > sdev->request_queue = NULL;
> > >
> > > - scsi_target_reap(scsi_target(sdev));
> > > -
> > > kfree(sdev->inquiry);
> > > kfree(sdev);
> > >
> > > @@ -1044,6 +1042,8 @@ void __scsi_remove_device(struct scsi_device *sdev)
> > > } else
> > > put_device(&sdev->sdev_dev);
> > >
> > > + scsi_target_reap(scsi_target(sdev));
> > > +
> > > /*
> > > * Stop accepting new requests and wait until all queuecommand() and
> > > * scsi_run_queue() invocations have finished before tearing down the
> >
> > This is not right. The problem is that you don't keep track explicitly
> > of the number of references to a target; you rely implicitly on
> > starget->devices being non-empty. starget->reap_ref is only a count of
> > local operations that should block removal.
>
> No, it was supposed explicitly to be a visibility counter to answer the
> question when can we delete the target. It's incremented every time we
> add a device to the target (and when we do an operation that may remove
> one to keep an atomic context before we blow it away) and decremented
> every time we remove one.
Sorry, but you're wrong. starget->reap_ref is _not_ incremented every
time we add a device to the target. That's one of the things we need to
fix.
> > Consider, for example, what would happen if there is more than one LUN.
> > What if one of them is removed while the other remains?
>
> Then the reap reference remains above zero and the target stays.
>
> > A more invasive change is needed.
>
> I think you might be right in that we need to kill the list_empty check,
> but I think that should be it.
That, plus a one or two other things. Look over the patch below.
Alan Stern
Index: usb-3.13/drivers/scsi/scsi_scan.c
===================================================================
--- usb-3.13.orig/drivers/scsi/scsi_scan.c
+++ usb-3.13/drivers/scsi/scsi_scan.c
@@ -334,6 +334,7 @@ static void scsi_target_dev_release(stru
struct device *parent = dev->parent;
struct scsi_target *starget = to_scsi_target(dev);
+ WARN_ON(!list_empty(&starget->devices));
kfree(starget);
put_device(parent);
}
@@ -481,7 +482,7 @@ void scsi_target_reap(struct scsi_target
spin_lock_irqsave(shost->host_lock, flags);
state = starget->state;
- if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
+ if (--starget->reap_ref == 0) {
empty = 1;
starget->state = STARGET_DEL;
}
Index: usb-3.13/drivers/scsi/scsi_sysfs.c
===================================================================
--- usb-3.13.orig/drivers/scsi/scsi_sysfs.c
+++ usb-3.13/drivers/scsi/scsi_sysfs.c
@@ -369,17 +369,13 @@ static void scsi_device_dev_release_user
{
struct scsi_device *sdev;
struct device *parent;
- struct scsi_target *starget;
struct list_head *this, *tmp;
unsigned long flags;
sdev = container_of(work, struct scsi_device, ew.work);
-
parent = sdev->sdev_gendev.parent;
- starget = to_scsi_target(parent);
spin_lock_irqsave(sdev->host->host_lock, flags);
- starget->reap_ref++;
list_del(&sdev->siblings);
list_del(&sdev->same_target_siblings);
list_del(&sdev->starved_entry);
@@ -399,13 +395,10 @@ static void scsi_device_dev_release_user
/* NULL queue means the device can't be used */
sdev->request_queue = NULL;
- scsi_target_reap(scsi_target(sdev));
-
kfree(sdev->inquiry);
kfree(sdev);
- if (parent)
- put_device(parent);
+ put_device(parent);
}
static void scsi_device_dev_release(struct device *dev)
@@ -1044,6 +1037,8 @@ void __scsi_remove_device(struct scsi_de
} else
put_device(&sdev->sdev_dev);
+ scsi_target_reap(scsi_target(sdev));
+
/*
* Stop accepting new requests and wait until all queuecommand() and
* scsi_run_queue() invocations have finished before tearing down the
@@ -1200,6 +1195,7 @@ void scsi_sysfs_device_initialize(struct
sdev->scsi_level = starget->scsi_level;
transport_setup_device(&sdev->sdev_gendev);
spin_lock_irqsave(shost->host_lock, flags);
+ ++starget->reap_ref;
list_add_tail(&sdev->same_target_siblings, &starget->devices);
list_add_tail(&sdev->siblings, &shost->__devices);
spin_unlock_irqrestore(shost->host_lock, flags);
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html