Re: target: Add support for COMPARE_AND_WRITE emulation

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

 



On Wed, 2013-09-11 at 09:43 +0300, Dan Carpenter wrote:
> On Tue, Sep 10, 2013 at 03:51:50PM -0700, Nicholas A. Bellinger wrote:
> > diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
> > index 2572ba7..6c17295 100644
> > --- a/drivers/target/target_core_sbc.c
> > +++ b/drivers/target/target_core_sbc.c
> > @@ -504,12 +504,15 @@ sbc_compare_and_write(struct se_cmd *cmd)
> >          * comparision using SGLs at cmd->t_bidi_data_sg..
> >          */
> >         rc = down_interruptible(&dev->caw_sem);
> > -       if ((rc != 0) || signal_pending(current))
> > +       if ((rc != 0) || signal_pending(current)) {
> > +               cmd->transport_complete_callback = NULL;
> >                 return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
> 
> I think this needs to be separate:
> 
> 	if (rc) {
> 		cmd->transport_complete_callback = NULL;
> 		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
> 	}
> 	if (signal_pending(current)) {
> 		up(&dev->caw_sem);
> 		cmd->transport_complete_callback = NULL;
> 		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
> 	}
> 

After double checking what kernel/semaphore.c:down_common() does, I
don't believe that up() should be called here for either case.

Namely, consider when sem->count == 0 enters __down_interruptible() ->
down_common():

static inline int __sched __down_common(struct semaphore *sem, long state,
                                                                long timeout)
{
        struct task_struct *task = current;
        struct semaphore_waiter waiter;

        list_add_tail(&waiter.list, &sem->wait_list);
        waiter.task = task;
        waiter.up = false;

        for (;;) {
                if (signal_pending_state(state, task))
                        goto interrupted;
                if (unlikely(timeout <= 0))
                        goto timed_out;
                __set_task_state(task, state);
                raw_spin_unlock_irq(&sem->lock);
                timeout = schedule_timeout(timeout);
                raw_spin_lock_irq(&sem->lock);
                if (waiter.up)
                        return 0;
        }

 timed_out:
        list_del(&waiter.list);
        return -ETIME;

 interrupted:
        list_del(&waiter.list);
        return -EINTR;
}

and when a signal causes down_common() to return -EINTR above, the
waiter is deleted from the list.

So for the down_interruptible() failure case in sbc_compare_and_write(),
performing an explicit up() after a non zero rc or signal_pending()
would incorrectly increment sem->count after the (failed) waiter was
already removed from the list in down_common().

--nab

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




[Index of Archives]     [Linux SCSI]     [Kernel Newbies]     [Linux SCSI Target Infrastructure]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Device Mapper]

  Powered by Linux