On Thu, 2008-01-10 at 23:31 +0100, Krzysztof Helt wrote:
> On Wed, 09 Jan 2008 17:51:43 -0600
> James Bottomley <[EMAIL PROTECTED]> wrote:
> 
> > > diff -urp linux-ref/drivers/scsi/sym53c8xx_2/sym_glue.c 
> > > linux-new/drivers/scsi/sym53c8xx_2/sym_glue.c
> > > --- linux-ref/drivers/scsi/sym53c8xx_2/sym_glue.c 2007-12-23 
> > > 20:39:44.000000000 +0100
> > > +++ linux-new/drivers/scsi/sym53c8xx_2/sym_glue.c 2008-01-09 
> > > 22:22:30.000000000 +0100
> > > @@ -609,22 +609,22 @@ static int sym_eh_handler(int op, char *
> > >    */
> > >  #define WAIT_FOR_PCI_RECOVERY    35
> > >   if (pci_channel_offline(pdev)) {
> > > -         struct completion *io_reset;
> > >           int finished_reset = 0;
> > >           init_completion(&eh_done);
> > >           spin_lock_irq(shost->host_lock);
> > >           /* Make sure we didn't race */
> > >           if (pci_channel_offline(pdev)) {
> > > -                 if (!sym_data->io_reset)
> > > -                         sym_data->io_reset = &eh_done;
> > > -                 io_reset = sym_data->io_reset;
> > > +                 BUG_ON(!sym_data->io_reset);
> > > +                 sym_data->io_reset = &eh_done;
> > >           } else {
> > >                   finished_reset = 1;
> > >           }
> > >           spin_unlock_irq(shost->host_lock);
> > >           if (!finished_reset)
> > > -                 finished_reset = wait_for_completion_timeout(io_reset,
> > > +                 finished_reset = wait_for_completion_timeout
> > > +                                         (sym_data->io_reset,
> > >                                           WAIT_FOR_PCI_RECOVERY*HZ);
> > > +         sym_data->io_reset = NULL;
> > 
> > This has to be cleared under the host_lock to forestall the (tiny) race
> > where the pci recovery code checks the value of sym_data->io_reset, we
> > change it to null and then the pci recovery code completes a NULL
> > pointer.
> > 
> 
> It is impossible as the io_reset value is not NULL before and during wait
> completion. The case above would happen only if one thread checked the
> io_reset value (under lock) and it was NULL and before setting it (inside
> locked section) another thread checked the io_reset value (still NULL 
> and also inside locked section = impossible). Otherwise, the BUG_ON() 
> kicked in (the value is already not NULL).

Er, no.  Let me be clearer about the sequence

sym2_io_resume() is racing to complete sym_data->io_reset with
sym_eh_handler()s timeout.  Because you don't set it to NULL under the
host_lock, you can get the sequence

sym2_io_resume() acquires the host lock and checks the value of
sym_data->io_reset and finds it to be not NULL.

sym_eh_handler() NULLs sym_data->io_resume *without* acquiring the host
lock. probably because the wait__for_completion_timeout() times out.

sym2_io_resume() calls complete() on sym_data->io_resume which is now a
NULL pointer and boom.

We never get multiple threads into sym_eh_handler() because it's single
threaded from the error handler thread.

James


-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to