On Tue, 14 Oct 2014 12:09:14 -0600
Jason Gunthorpe <[email protected]> wrote:

> On Mon, Oct 13, 2014 at 10:23:33PM +0200, Christophe Ricard wrote:
> > The tpm layer already provides a function to wait for a TIS event
> > wait_for_tpm_stat. Exactly like wait_for_serirq_timeout, it can
> > work in polling or interrupt mode.
> > Instead of using a completion struct, we rely on the waitqueue
> > read_queue and int_queue from chip->vendor field.
> 
> 
> >  static int request_locality(struct tpm_chip *chip)
> >  {
> [..]
> 
> >     if (chip->vendor.irq) {
> > -           r = wait_for_serirq_timeout(chip, (check_locality
> > -                                                  (chip) >=
> > 0),
> > -
> > chip->vendor.timeout_a); +again:
> > +           timeout = stop - jiffies;
> > +           if ((long) timeout <= 0)
> > +                   return -1;
> 
> I don't see an enable_irq before this sleep:
I missed that one i guess.

> 
> > +           r =
> > wait_event_interruptible_timeout(chip->vendor.read_queue,
> > +                                   check_locality(chip) >= 0,
> > +                                   timeout);
> 
> Can it use wait_for_stat?
It actually cannot use wait_for_stat because wait_for_stat is waiting
for a mask condition on the status register. Here we are addressing the
TPM_ACCESS register.

> 
> >  static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned
> > long timeout,
> > -                    wait_queue_head_t *queue)
> > +                    wait_queue_head_t *queue, bool
> > check_cancel) {
> 
> So, I didn't audit the driver 100%, but this new version has the flow
> 
> - enable_irq
> - wait for irq
> - clear irq
> 
> Which is conceptually fine (and efficient), but it requires the rest
> of the driver to guarentee that the interrupt is consistent after
> every interation with the TPM. Which for this driver I think means one
> of two states:
>  - No interrupt asserted
>  - Interrupt asserted, and the TPM is ready to accept a command
> Other states will cause longer command execution times, but not
> malfunction.
> 
> For instance, it looks to me like request_locality might not maintain
> that invariant.
> 
> Clearing old pending bits before starting an interaction is certainly
> safer, but costs that extra I2C sequence.
> 
> > +   tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip);
> > +
> > +   if (chip->vendor.irq)
> > +           enable_irq(chip->vendor.irq);
> > +
> > +   r = wait_for_tpm_stat(chip, mask, timeout, queue,
> > check_cancel);
> 
> This seqence is racy, the reason the nuvoton driver has the counter is
> because wake_up_interruptible only wakes sleeping threads, so the
> driver has to use the counter to close the race between the enable_irq
> and the actual sleep:
> 
>               unsigned int cur_intrs = priv->intrs;
>               enable_irq(chip->vendor.irq);
>               rc = wait_event_interruptible_timeout(*queue,
>                                                     cur_intrs !=
> priv->intrs, timeout);
If my understanding is correct the counter prevent the case where the
irq is raised before entering into the wait_event_interruptible_timeout.
wait_for_tpm_stat will check before going into sleep the status
register in order to make sure the condition is not already satisfied
and should fulfill this requirement.

As i could get different kind of interruption i think i cannot avoid an
i2c check here. The other solution would be to activate only the right
interruption in the INT_ENABLE tis register but still with an i2c
transaction.

> 
> Jason

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

Reply via email to