All of lore.kernel.org
 help / color / mirror / Atom feed
From: Boris Brezillon <boris.brezillon@free-electrons.com>
To: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Richard Weinberger <richard@nod.at>,
	Marek Vasut <marek.vasut@gmail.com>,
	Artem Bityutskiy <artem.bityutskiy@linux.intel.com>,
	Chuanxiao Dong <chuanxiao.dong@intel.com>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	Dinh Nguyen <dinguyen@kernel.org>,
	linux-mtd@lists.infradead.org,
	Masami Hiramatsu <mhiramat@kernel.org>,
	Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>,
	Jassi Brar <jaswinder.singh@linaro.org>,
	Brian Norris <computersforpeace@gmail.com>,
	Enrico Jorns <ejo@pengutronix.de>,
	David Woodhouse <dwmw2@infradead.org>
Subject: Re: [PATCH v5 10/23] mtd: nand: denali: rework interrupt handling
Date: Thu, 8 Jun 2017 17:43:11 +0200	[thread overview]
Message-ID: <20170608174311.4f012cc5@bbrezillon> (raw)
In-Reply-To: <CAK7LNARbaAKyB2ss780o=L=sqqUdz=fix+PaxW_ODDAM63VXOw@mail.gmail.com>

On Thu, 8 Jun 2017 21:58:00 +0900
Masahiro Yamada <yamada.masahiro@socionext.com> wrote:

> Hi Boris,
> 
> 2017-06-08 20:26 GMT+09:00 Boris Brezillon <boris.brezillon@free-electrons.com>:
> > On Thu, 8 Jun 2017 19:41:39 +0900
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
> >  
> >> Hi Boris,
> >>
> >>
> >> 2017-06-08 16:12 GMT+09:00 Boris Brezillon <boris.brezillon@free-electrons.com>:  
> >> > Le Thu, 8 Jun 2017 15:10:18 +0900,
> >> > Masahiro Yamada <yamada.masahiro@socionext.com> a écrit :
> >> >  
> >> >> Hi Boris,
> >> >>
> >> >>
> >> >> 2017-06-07 22:57 GMT+09:00 Boris Brezillon <boris.brezillon@free-electrons.com>:  
> >> >> > On Wed,  7 Jun 2017 20:52:19 +0900
> >> >> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
> >> >> >
> >> >> >  
> >> >> >> -/*
> >> >> >> - * This is the interrupt service routine. It handles all interrupts
> >> >> >> - * sent to this device. Note that on CE4100, this is a shared interrupt.
> >> >> >> - */
> >> >> >> -static irqreturn_t denali_isr(int irq, void *dev_id)
> >> >> >> +static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
> >> >> >> +                                 uint32_t irq_mask)
> >> >> >>  {
> >> >> >> -     struct denali_nand_info *denali = dev_id;
> >> >> >> +     unsigned long time_left, flags;
> >> >> >>       uint32_t irq_status;
> >> >> >> -     irqreturn_t result = IRQ_NONE;
> >> >> >>
> >> >> >> -     spin_lock(&denali->irq_lock);
> >> >> >> +     spin_lock_irqsave(&denali->irq_lock, flags);
> >> >> >>
> >> >> >> -     /* check to see if a valid NAND chip has been selected. */
> >> >> >> -     if (is_flash_bank_valid(denali->flash_bank)) {
> >> >> >> -             /*
> >> >> >> -              * check to see if controller generated the interrupt,
> >> >> >> -              * since this is a shared interrupt
> >> >> >> -              */
> >> >> >> -             irq_status = denali_irq_detected(denali);
> >> >> >> -             if (irq_status != 0) {
> >> >> >> -                     /* handle interrupt */
> >> >> >> -                     /* first acknowledge it */
> >> >> >> -                     clear_interrupt(denali, irq_status);
> >> >> >> -                     /*
> >> >> >> -                      * store the status in the device context for someone
> >> >> >> -                      * to read
> >> >> >> -                      */
> >> >> >> -                     denali->irq_status |= irq_status;
> >> >> >> -                     /* notify anyone who cares that it happened */
> >> >> >> -                     complete(&denali->complete);
> >> >> >> -                     /* tell the OS that we've handled this */
> >> >> >> -                     result = IRQ_HANDLED;
> >> >> >> -             }
> >> >> >> +     irq_status = denali->irq_status;
> >> >> >> +
> >> >> >> +     if (irq_mask & irq_status) {
> >> >> >> +             spin_unlock_irqrestore(&denali->irq_lock, flags);
> >> >> >> +             return irq_status;
> >> >> >>       }
> >> >> >> -     spin_unlock(&denali->irq_lock);
> >> >> >> -     return result;
> >> >> >> +
> >> >> >> +     denali->irq_mask = irq_mask;
> >> >> >> +     reinit_completion(&denali->complete);  
> >> >> >
> >> >> > These 2 instructions should be done before calling
> >> >> > denali_wait_for_irq() (for example in denali_reset_irq()), otherwise
> >> >> > you might loose events if they happen between your irq_status read and
> >> >> > the reinit_completion() call.  
> >> >>
> >> >> No.
> >> >>
> >> >> denali->irq_lock avoids a race between denali_isr() and
> >> >> denali_wait_for_irq().
> >> >>
> >> >>
> >> >> The line
> >> >>      denali->irq_status |= irq_status;
> >> >> in denali_isr() accumulates all events that have happened
> >> >> since denali_reset_irq().
> >> >>
> >> >> If the interested IRQs have already happened
> >> >> before denali_wait_for_irq(), it just return immediately
> >> >> without using completion.
> >> >>
> >> >> I do not mind adding a comment like below
> >> >> if you think my intention is unclear, though.
> >> >>
> >> >>         /* Return immediately if interested IRQs have already happend. */
> >> >>         if (irq_mask & irq_status) {
> >> >>                 spin_unlock_irqrestore(&denali->irq_lock, flags);
> >> >>                 return irq_status;
> >> >>         }
> >> >>
> >> >>  
> >> >
> >> > My bad, I didn't notice you were releasing the lock after calling
> >> > reinit_completion(). I still find this solution more complex than my
> >> > proposal, but I don't care that much.  
> >>
> >>
> >> At first, I implemented exactly like you suggested;
> >>    denali->irq_mask = irq_mask;
> >>    reinit_completion(&denali->complete)
> >> in denali_reset_irq().
> >>
> >>
> >> IIRC, things were like this.
> >>
> >> Some time later, you memtioned to use ->cmd_ctrl
> >> instead of ->cmdfunc.
> >>
> >> Then I had a problem when I needed to implement
> >> denali_check_irq() in
> >> http://patchwork.ozlabs.org/patch/772395/
> >>
> >> denali_wait_for_irq() is blocked until interested IRQ happens.
> >> but ->dev_ready() hook should not be blocked.
> >> It should return if R/B# transition has happened or not.  
> >
> > Nope, it should return whether the NAND is ready or not, not whether a
> > busy -> ready transition occurred or not. It's typically done by
> > reading the NAND STATUS register or by checking the R/B pin status.  
> 
> Checking the R/B pin is probably impossible unless
> the pin is changed into a GPIO port.
> 
> I also considered NAND_CMD_STATUS, but
> I can not recall why I chose the current approach.
> Perhaps I thought returning detected IRQ
> is faster than accessing the chip for NAND_CMD_STATUS.
> 
> I can try NAND_CMD_STATUS approach if you like.

Depends what you're trying to do. IIUC, you use denali_wait_for_irq()
inside your ->reset()/->read/write_{page,oob}[_raw]() methods, which is
perfectly fine (assuming CUSTOM_PAGE_ACCESS is set) since these hooks
are expected to wait for chip readiness before returning.

You could also implement ->waitfunc() using denali_wait_for_irq() if
you're able to detect R/B transitions, but I'm not sure it's worth it,
because you overload almost all the methods using this hook (the only
one remaining is ->onfi_set_features(), and using STATUS polling should
not be an issue in this case).

Implementing ->dev_ready() is not necessary. When not provided, the
core falls back to STATUS polling and you seem to support
NAND_CMD_STATUS in denali_cmdfunc(). Note that even if it's not fully
reliable in the current driver, you're switching to ->cmd_ctrl() at the
end of the series anyway, so we should be good after that.

> 
> 
> 
> 
> 
> >> So, I accumulate IRQ events in denali->irq_status
> >> that have happened since denali_reset_irq().  
> >
> > Yep, I see that.
> >  
> >>
> >>
> >>  
> >> >>
> >> >>
> >> >>  
> >> >> > You should also clear existing interrupts
> >> >> > before launching your operation, otherwise you might wakeup on previous
> >> >> > events.  
> >> >>
> >> >>
> >> >> I do not see a point in your suggestion.
> >> >>
> >> >> denali_isr() reads out IRQ_STATUS(i) and immediately clears IRQ bits.
> >> >>
> >> >> IRQ events triggered by previous events are accumulated in denali->irq_status.
> >> >>
> >> >> denali_reset_irq() clears it.
> >> >>
> >> >>         denali->irq_status = 0;  
> >> >
> >> > Well, it was just a precaution, in case some interrupts weren't cleared
> >> > during the previous test (for example if they were masked before the
> >> > event actually happened, which can occur if you have a timeout, but
> >> > the event is detected afterward).  
> >>
> >> Turning on/off IRQ mask is problematic.
> >> So I did not do that.  
> >
> > I don't see why this is a problem. That's how it usually done.
> >  
> >>
> >> I enable IRQ mask in driver probe.
> >> I think this approach is more robust when we consider race conditions
> >> like you mentioned.  
> >
> > I'd like to hear more about the reasons you think it's more robust
> > than
> >
> > * at-probe-time: mask all IRQs and reset IRQ status
> >
> > * when doing a specific operation:
> > 1/ reset irq status
> > 2/ unmask relevant irqs (based on the operation you're doing)
> > 3/ launch the operation
> > 4/ wait for interrupts
> > 5/ mask irqs and check the wait_for_completion() return code + irq
> >    status
> >
> > This approach shouldn't be racy, because you're resetting+unmasking
> > irqs before starting the real operation (the one supposed to generate
> > such interrupts). By doing that you also get rid of the extra  
> > ->irq_status field, and you don't have to check irq_status before  
> > calling wait_for_completion().  
> 
> 
> IIRC, I was thinking like this:
> 
> One IRQ line may be shared among multiple hardware including Denali.
> denali_pci may do this.
> 
> The Denali IRQ handler need to check irq status
> because it should return IRQ_HANDLED if the event comes from Denali controller.
> Otherwise, the event comes from different hardware, so
> Denali IRQ handler should return IRQ_NONE.

Correct.

> 
> wait_for_completion_timeout() may bail out with timeout error,
> then proceed to denali_reset_irq() for the next operation.

Before calling denali_reset_irq() you should re-mask the irqs you
unmasked in #1. Actually, calling denali_reset_irq() after
wait_for_completion_timeout() is not even needed here because you'll
clear pending irqs before launching the next NAND command.

> Afterwards, the event actually may happen, and invoke IRQ handler.

Not if you masked IRQs after wait_for_completion_timeout() returned.

> 
> denali_reset_irq() and denali_isr() compete to grab the spin lock.
> 
> If denali_reset_irq() wins, it clears INTR_STATUS register
> (if implemented like you suggested first) or changes IRQ mask for the
> next event.
> After that, denali_isr enters the critical section and checks IRQ bit
> but at this moment, the IRQ bit has gone.  So, it assumes this event
> is not for Denali, so returns IRQ_NONE.  Nobody returns IRQ_HANDLED.

Not if you have masked the interrupts.

> 
> Then, kernel will complain "irq *: nobody cared"
> 
> 
> In my opinion, IRQ should be checked and cleared in one place
> (in IRQ handler).
> 
> Enabling/disabling IRQ mask is not problem unless it masks out
> already-asserted IRQ status bits.

Here is a patch to show you what I had in mind [1] (it applies on top
of this patch). AFAICT, there's no races, no interrupt loss, and you
get rid of the ->irq_mask/status/lock fields.

[1]http://code.bulix.org/fufia6-145571

  reply	other threads:[~2017-06-08 15:43 UTC|newest]

Thread overview: 56+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-06-07 11:52 [PATCH v5 00/23] mtd: nand: denali: Denali NAND IP patch bomb Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 01/23] mtd: nand: add generic helpers to check, match, maximize ECC settings Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 02/23] mtd: nand: add a shorthand to generate nand_ecc_caps structure Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 03/23] mtd: nand: denali: avoid hard-coding ECC step, strength, bytes Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 04/23] mtd: nand: denali: remove Toshiba and Hynix specific fixup code Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 05/23] mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants Masahiro Yamada
2017-06-07 19:01   ` Rob Herring
2017-06-07 19:01     ` Rob Herring
2017-06-07 11:52 ` [PATCH v5 06/23] mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS Masahiro Yamada
2017-06-07 13:26   ` Boris Brezillon
2017-06-08  7:32     ` Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 07/23] mtd: nand: denali: do not propagate NAND_STATUS_FAIL to waitfunc() Masahiro Yamada
2017-06-07 13:33   ` Boris Brezillon
2017-06-08  6:11     ` Masahiro Yamada
2017-06-08  7:05       ` Boris Brezillon
2017-06-08  9:43         ` Masahiro Yamada
2017-06-08 10:04           ` Boris Brezillon
2017-06-07 11:52 ` [PATCH v5 08/23] mtd: nand: denali: remove unneeded find_valid_banks() Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 09/23] mtd: nand: denali: handle timing parameters by setup_data_interface() Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 10/23] mtd: nand: denali: rework interrupt handling Masahiro Yamada
2017-06-07 13:57   ` Boris Brezillon
2017-06-08  6:10     ` Masahiro Yamada
2017-06-08  7:12       ` Boris Brezillon
2017-06-08 10:41         ` Masahiro Yamada
2017-06-08 11:26           ` Boris Brezillon
2017-06-08 12:58             ` Masahiro Yamada
2017-06-08 15:43               ` Boris Brezillon [this message]
2017-06-08 17:26                 ` Masahiro Yamada
2017-06-08 17:30                   ` Masahiro Yamada
2017-06-09  7:58                   ` Boris Brezillon
2017-06-13  4:41                     ` Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 11/23] mtd: nand: denali: fix NAND_CMD_STATUS handling Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 12/23] mtd: nand: denali: fix NAND_CMD_PARAM handling Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 13/23] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 14/23] mtd: nand: denali: fix bank reset function to detect the number of chips Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 15/23] mtd: nand: denali: use interrupt instead of polling for bank reset Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 16/23] mtd: nand: denali: propagate page to helpers via function argument Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 17/23] mtd: nand: denali: merge struct nand_buf into struct denali_nand_info Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 18/23] mtd: nand: denali: use flag instead of register macro for direction Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 19/23] mtd: nand: denali: fix raw and oob accessors for syndrome page layout Masahiro Yamada
2017-06-07 14:09   ` Boris Brezillon
2017-06-08 11:22     ` Masahiro Yamada
2017-06-13  4:42       ` Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 20/23] mtd: nand: denali: support hardware-assisted erased page detection Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 21/23] mtd: nand: denali: skip driver internal bounce buffer when possible Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 22/23] mtd: nand: denali: use non-managed kmalloc() for DMA buffer Masahiro Yamada
2017-06-07 11:52 ` [PATCH v5 23/23] mtd: nand: denali: enable bad block table scan Masahiro Yamada
2017-06-08  6:16 ` [PATCH v5 00/23] mtd: nand: denali: Denali NAND IP patch bomb Masahiro Yamada
2017-06-08  6:16   ` Masahiro Yamada
2017-06-08  7:12   ` Masahiro Yamada
2017-06-08  7:18     ` Boris Brezillon
2017-06-08  7:18       ` Boris Brezillon
2017-06-11 20:14     ` Boris Brezillon
2017-06-11 20:14       ` Boris Brezillon
2017-06-08  7:14   ` Boris Brezillon
2017-06-08  7:14     ` Boris Brezillon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170608174311.4f012cc5@bbrezillon \
    --to=boris.brezillon@free-electrons.com \
    --cc=artem.bityutskiy@linux.intel.com \
    --cc=chuanxiao.dong@intel.com \
    --cc=computersforpeace@gmail.com \
    --cc=cyrille.pitchen@wedev4u.fr \
    --cc=dinguyen@kernel.org \
    --cc=dwmw2@infradead.org \
    --cc=ejo@pengutronix.de \
    --cc=jaswinder.singh@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mtd@lists.infradead.org \
    --cc=marek.vasut@gmail.com \
    --cc=mhiramat@kernel.org \
    --cc=richard@nod.at \
    --cc=yamada.masahiro@socionext.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.