On Thu, 2 Jan 2014, James Bottomley wrote:

> In the highly unusual case where two threads are running concurrently through
> the scanning code scanning the same target, we run into the situation where
> one may allocate the target while the other is still using it.  In this case,
> because the reap checks for STARGET_CREATED and kills the target without
> reference counting, the second thread will do the wrong thing on reap.
> 
> Fix this by reference counting even creates and doing the STARGET_CREATED
> check in the final put.

I'm still concerned about one thing.  The previous patch does this in
scsi_alloc_target():

>   found:
> -     found_target->reap_ref++;
> +     if (!kref_get_unless_zero(&found_target->reap_ref))
> +             /*
> +              * release routine already fired.  Target is dead, but
> +              * STARGET_DEL may not yet be set (set in the release
> +              * routine), so set here as well, just in case
> +              */
> +             found_target->state = STARGET_DEL;
>       spin_unlock_irqrestore(shost->host_lock, flags);

As a result, the two comments in this patch aren't right:

> @@ -384,9 +385,15 @@ static void scsi_target_reap_ref_release(struct kref 
> *kref)
>       struct scsi_target *starget
>               = container_of(kref, struct scsi_target, reap_ref);
>  
> -     transport_remove_device(&starget->dev);
> -     device_del(&starget->dev);
> -     starget->state = STARGET_DEL;
> +     /*
> +      * if we get here and the target is still in the CREATED state that
> +      * means it was allocated but never made visible (because a scan
> +      * turned up no LUNs), so don't call device_del() on it.
> +      */
> +     if (starget->state == STARGET_RUNNING) {
> +             transport_remove_device(&starget->dev);
> +             device_del(&starget->dev);
> +     }

Here the state could already be STARGET_DEL, even though the target is
still visible.

Also, it's a little odd that the comment talks about CREATED but the 
code really checks for RUNNING.  They should be consistent.

> @@ -506,11 +513,13 @@ static struct scsi_target *scsi_alloc_target(struct 
> device *parent,
>   */
>  void scsi_target_reap(struct scsi_target *starget)
>  {
> +     /*
> +      * serious problem if this triggers: STARGET_DEL is only set in the
> +      * kref release routine, so we're doing another final put on an
> +      * already released kref
> +      */
>       BUG_ON(starget->state == STARGET_DEL);

Here the code is okay but the comment is wrong: STARGET_DEL is set in 
_two_ places (but neither of them runs until reap_ref has reached 0).

Would it be better in scsi_alloc_target() to behave as though the state 
were STARGET_DEL without actually setting it?

Alan Stern


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to