Rune Torgersen wrote:
>Hi
>I have a driver that roughly does something like:
>
>int driver_read(int cs, int addr, void *buf, int len)
>{
> hw_done = 0;
> /* init_hw */
>
> if (!hw_done)
> {
> ret = wait_event_interruptible_timeout(inq, hw_done, TIMEOUT);
> if (ret == 0)
> {
> if (hw_done)
> goto hw_finished;
>
> return -EIO;
> }
> }
>hw_finished:
> return len;
>}
>
>static irqreturn_t myinterrupt(int irq, void * dev_id, struct pt_regs *
>regs)
>{
> hw_done = 1;
> schedule_work(&tqueue);
>
> return IRQ_HANDLED;
>}
>
>static void do_softint(void *private_)
>{
> wake_up_interruptible(&inq);
>
>}
>
>I have a problem however with this, because in about 10% of my cases,
>the interrupt triggers very fast, and ends up being served between the
>check for hw_done and the wait_event call. This cause the wait to
>timeout instead of getting waked up.
>
>Is there a better way of doing this?
>I do not want to do a busy wait, because the hardware can take up to
>several 100's of ms to return, but most often returns within 20us.
>
>
You seem to create an unnecessary race condition by checking the hw_done
variable in the read function. You can avoid this by changing it to
int driver_read(int cs, int addr, void *buf, int len)
{
hw_done = 0;
/* init_hw */
if (wait_event_interruptible_timeout(inq, hw_done, TIMEOUT))
return -ERESTARTSYS;
hw_done = 0;
/* copy the data to user space here */
return len;
}
The wait_event_interruptible_timeout() function checks the variable for
you. You still must make sure the read function does the right thing if
it is called simultaneously by more than one application.