On Fri, 13 Dec 2013, James Bottomley wrote:

> This patch eliminates the reap_ref and replaces it with a proper kref.
> On last put of this kref, the target is removed from visibility in
> sysfs.  The final call to scsi_target_reap() for the device is done from
> __scsi_remove_device() and only if the device was made visible.  This
> ensures that the target disappears as soon as the last device is gone
> rather than waiting until final release of the device (which is often
> too long).


> @@ -474,28 +496,11 @@ static void scsi_target_reap_usercontext(struct 
> work_struct *work)
>   */
>  void scsi_target_reap(struct scsi_target *starget)
>  {
> -     struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
> -     unsigned long flags;
> -     enum scsi_target_state state;
> -     int empty = 0;
> -
> -     spin_lock_irqsave(shost->host_lock, flags);
> -     state = starget->state;
> -     if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
> -             empty = 1;
> -             starget->state = STARGET_DEL;
> -     }
> -     spin_unlock_irqrestore(shost->host_lock, flags);
> -
> -     if (!empty)
> -             return;
> -
> -     BUG_ON(state == STARGET_DEL);
> -     if (state == STARGET_CREATED)
> +     BUG_ON(starget->state == STARGET_DEL);
> +     if (starget->state == STARGET_CREATED)
>               scsi_target_destroy(starget);
>       else
> -             execute_in_process_context(scsi_target_reap_usercontext,
> -                                        &starget->ew);
> +             scsi_target_reap_ref_put(starget);

The refcount test and state change race with scsi_alloc_target().  
Maybe the race won't occur in practice, but to be safe you should hold
shost->host_lock throughout that time interval, as the original code
here does.

This means the kref approach won't work so easily.  You might as well
leave reap_ref as an ordinary int.

> @@ -393,7 +393,6 @@ static void scsi_device_dev_release_usercontext(struct 
> work_struct *work)
>       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);

starget is now an unused local variable.  It can be eliminated.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" 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