diff mbox series

[v2,01/10] mailbox: Support blocking transfers in atomic context

Message ID 20181112151853.29289-2-thierry.reding@gmail.com (mailing list archive)
State New, archived
Headers show
Series serial: Add Tegra Combined UART driver | expand

Commit Message

Thierry Reding Nov. 12, 2018, 3:18 p.m. UTC
From: Thierry Reding <treding@nvidia.com>

The mailbox framework supports blocking transfers via completions for
clients that can sleep. In order to support blocking transfers in cases
where the transmission is not permitted to sleep, add a new ->flush()
callback that controller drivers can implement to busy loop until the
transmission has been completed. This will automatically be called when
available and interrupts are disabled for clients that request blocking
transfers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/mailbox/mailbox.c          | 8 ++++++++
 include/linux/mailbox_controller.h | 4 ++++
 2 files changed, 12 insertions(+)

Comments

Jassi Brar Nov. 17, 2018, 5:27 p.m. UTC | #1
On Mon, Nov 12, 2018 at 9:18 AM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> From: Thierry Reding <treding@nvidia.com>
>
> The mailbox framework supports blocking transfers via completions for
> clients that can sleep. In order to support blocking transfers in cases
> where the transmission is not permitted to sleep, add a new ->flush()
> callback that controller drivers can implement to busy loop until the
> transmission has been completed. This will automatically be called when
> available and interrupts are disabled for clients that request blocking
> transfers.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/mailbox/mailbox.c          | 8 ++++++++
>  include/linux/mailbox_controller.h | 4 ++++
>  2 files changed, 12 insertions(+)
>
> diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> index 674b35f402f5..0eaf21259874 100644
> --- a/drivers/mailbox/mailbox.c
> +++ b/drivers/mailbox/mailbox.c
> @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
>                 unsigned long wait;
>                 int ret;
>
> +               if (irqs_disabled() && chan->mbox->ops->flush) {
> +                       ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> +                       if (ret < 0)
> +                               tx_tick(chan, ret);
> +
> +                       return ret;
> +               }
> +
This is hacky. I think we can do without busy waiting in atomic
context. You could queue locally while in atomic context and then
transfer in blocking mode. I don't think we should worry about the
'throughput' as there already is no h/w rate control even with
busy-waiting.

Thanks.
Thierry Reding Nov. 20, 2018, 3:29 p.m. UTC | #2
On Sat, Nov 17, 2018 at 11:27:17AM -0600, Jassi Brar wrote:
> On Mon, Nov 12, 2018 at 9:18 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> >
> > From: Thierry Reding <treding@nvidia.com>
> >
> > The mailbox framework supports blocking transfers via completions for
> > clients that can sleep. In order to support blocking transfers in cases
> > where the transmission is not permitted to sleep, add a new ->flush()
> > callback that controller drivers can implement to busy loop until the
> > transmission has been completed. This will automatically be called when
> > available and interrupts are disabled for clients that request blocking
> > transfers.
> >
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/mailbox/mailbox.c          | 8 ++++++++
> >  include/linux/mailbox_controller.h | 4 ++++
> >  2 files changed, 12 insertions(+)
> >
> > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > index 674b35f402f5..0eaf21259874 100644
> > --- a/drivers/mailbox/mailbox.c
> > +++ b/drivers/mailbox/mailbox.c
> > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> >                 unsigned long wait;
> >                 int ret;
> >
> > +               if (irqs_disabled() && chan->mbox->ops->flush) {
> > +                       ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > +                       if (ret < 0)
> > +                               tx_tick(chan, ret);
> > +
> > +                       return ret;
> > +               }
> > +
> This is hacky. I think we can do without busy waiting in atomic
> context. You could queue locally while in atomic context and then
> transfer in blocking mode. I don't think we should worry about the
> 'throughput' as there already is no h/w rate control even with
> busy-waiting.

I actually tried to do that before I added this flushing mechanism. The
problem is, like you said, one of rate control. As mentioned in the
cover letter, the shared mailboxes implemented in tegra-hsp are used as
RX and TX channels for the TCU, which is like a virtual UART. The TTY
driver included as part of this series will use one of the mailboxes to
transmit data that is written to the console. The problem is that if
these transmissions are not rate-limited on the TTY driver side, the
console will just keep writing data and eventually overflow the buffer
that we have in the mailbox subsystem.

The problem is that data comes in at a much higher rate than what we can
output. This is especially true at boot when the TCU console takes over
and the whole log buffer is dumped on it.

So the only way to rate-limit is to either make mbox_send_message()
block, but that can only be done in non-atomic context. The console,
however, will always run in atomic context, so the only way to do rate-
limiting is by busy looping.

Thierry
Thierry Reding Nov. 21, 2018, 2:27 p.m. UTC | #3
On Tue, Nov 20, 2018 at 04:29:07PM +0100, Thierry Reding wrote:
> On Sat, Nov 17, 2018 at 11:27:17AM -0600, Jassi Brar wrote:
> > On Mon, Nov 12, 2018 at 9:18 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > >
> > > From: Thierry Reding <treding@nvidia.com>
> > >
> > > The mailbox framework supports blocking transfers via completions for
> > > clients that can sleep. In order to support blocking transfers in cases
> > > where the transmission is not permitted to sleep, add a new ->flush()
> > > callback that controller drivers can implement to busy loop until the
> > > transmission has been completed. This will automatically be called when
> > > available and interrupts are disabled for clients that request blocking
> > > transfers.
> > >
> > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > ---
> > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > >  include/linux/mailbox_controller.h | 4 ++++
> > >  2 files changed, 12 insertions(+)
> > >
> > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > index 674b35f402f5..0eaf21259874 100644
> > > --- a/drivers/mailbox/mailbox.c
> > > +++ b/drivers/mailbox/mailbox.c
> > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > >                 unsigned long wait;
> > >                 int ret;
> > >
> > > +               if (irqs_disabled() && chan->mbox->ops->flush) {
> > > +                       ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > +                       if (ret < 0)
> > > +                               tx_tick(chan, ret);
> > > +
> > > +                       return ret;
> > > +               }
> > > +
> > This is hacky. I think we can do without busy waiting in atomic
> > context. You could queue locally while in atomic context and then
> > transfer in blocking mode. I don't think we should worry about the
> > 'throughput' as there already is no h/w rate control even with
> > busy-waiting.
> 
> I actually tried to do that before I added this flushing mechanism. The
> problem is, like you said, one of rate control. As mentioned in the
> cover letter, the shared mailboxes implemented in tegra-hsp are used as
> RX and TX channels for the TCU, which is like a virtual UART. The TTY
> driver included as part of this series will use one of the mailboxes to
> transmit data that is written to the console. The problem is that if
> these transmissions are not rate-limited on the TTY driver side, the
> console will just keep writing data and eventually overflow the buffer
> that we have in the mailbox subsystem.
> 
> The problem is that data comes in at a much higher rate than what we can
> output. This is especially true at boot when the TCU console takes over
> and the whole log buffer is dumped on it.
> 
> So the only way to rate-limit is to either make mbox_send_message()
> block, but that can only be done in non-atomic context. The console,
> however, will always run in atomic context, so the only way to do rate-
> limiting is by busy looping.

What I also tried before was to implement busy looping within the
->send_data() callback of the driver so that we didn't have to put this
into the core. Unfortunately, however, the ->send_data() callback is
called under chan->lock, which means that from mbox_send_message() we
don't have a way to mark the transfer as done. In order to do that we'd
have to call mbox_chan_txdone(), but that ends up calling tx_tick() and
that in turn also attempts to take the chan->lock, which would cause a
deadlock.

The explicit flushing is the best alternative that I could come up with.
I think it's not all that hacky, because it's very explicit about what's
going on and it has the nice side-effect that it will allow the mailbox
to work in interrupt driven mode if possible and only resorting to the
busy loop in atomic context.

At this point I think I have explored all other options and I frankly
can't find a more proper way to achieve what we need here. Perhaps you
can think of additional ways to accomplish this?

Thierry
Jassi Brar Nov. 22, 2018, 2:18 a.m. UTC | #4
On Wed, Nov 21, 2018 at 8:27 AM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Tue, Nov 20, 2018 at 04:29:07PM +0100, Thierry Reding wrote:
> > On Sat, Nov 17, 2018 at 11:27:17AM -0600, Jassi Brar wrote:
> > > On Mon, Nov 12, 2018 at 9:18 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > > >
> > > > From: Thierry Reding <treding@nvidia.com>
> > > >
> > > > The mailbox framework supports blocking transfers via completions for
> > > > clients that can sleep. In order to support blocking transfers in cases
> > > > where the transmission is not permitted to sleep, add a new ->flush()
> > > > callback that controller drivers can implement to busy loop until the
> > > > transmission has been completed. This will automatically be called when
> > > > available and interrupts are disabled for clients that request blocking
> > > > transfers.
> > > >
> > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > ---
> > > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > > >  include/linux/mailbox_controller.h | 4 ++++
> > > >  2 files changed, 12 insertions(+)
> > > >
> > > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > > index 674b35f402f5..0eaf21259874 100644
> > > > --- a/drivers/mailbox/mailbox.c
> > > > +++ b/drivers/mailbox/mailbox.c
> > > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > > >                 unsigned long wait;
> > > >                 int ret;
> > > >
> > > > +               if (irqs_disabled() && chan->mbox->ops->flush) {
> > > > +                       ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > > +                       if (ret < 0)
> > > > +                               tx_tick(chan, ret);
> > > > +
> > > > +                       return ret;
> > > > +               }
> > > > +
> > > This is hacky. I think we can do without busy waiting in atomic
> > > context. You could queue locally while in atomic context and then
> > > transfer in blocking mode. I don't think we should worry about the
> > > 'throughput' as there already is no h/w rate control even with
> > > busy-waiting.
> >
> > I actually tried to do that before I added this flushing mechanism. The
> > problem is, like you said, one of rate control. As mentioned in the
> > cover letter, the shared mailboxes implemented in tegra-hsp are used as
> > RX and TX channels for the TCU, which is like a virtual UART. The TTY
> > driver included as part of this series will use one of the mailboxes to
> > transmit data that is written to the console. The problem is that if
> > these transmissions are not rate-limited on the TTY driver side, the
> > console will just keep writing data and eventually overflow the buffer
> > that we have in the mailbox subsystem.
> >
> > The problem is that data comes in at a much higher rate than what we can
> > output. This is especially true at boot when the TCU console takes over
> > and the whole log buffer is dumped on it.
> >
> > So the only way to rate-limit is to either make mbox_send_message()
> > block, but that can only be done in non-atomic context. The console,
> > however, will always run in atomic context, so the only way to do rate-
> > limiting is by busy looping.
>
> What I also tried before was to implement busy looping within the
> ->send_data() callback of the driver so that we didn't have to put this
> into the core. Unfortunately, however, the ->send_data() callback is
> called under chan->lock, which means that from mbox_send_message() we
> don't have a way to mark the transfer as done. In order to do that we'd
> have to call mbox_chan_txdone(), but that ends up calling tx_tick() and
> that in turn also attempts to take the chan->lock, which would cause a
> deadlock.
>
> The explicit flushing is the best alternative that I could come up with.
> I think it's not all that hacky, because it's very explicit about what's
> going on and it has the nice side-effect that it will allow the mailbox
> to work in interrupt driven mode if possible and only resorting to the
> busy loop in atomic context.
>
> At this point I think I have explored all other options and I frankly
> can't find a more proper way to achieve what we need here. Perhaps you
> can think of additional ways to accomplish this?
>
Well, I would have a local ring buffer (array) of enough size to hold
the characters and then have a task consuming data from that ring
buffer by transmitting over mailbox.

-jassi
Thierry Reding Nov. 22, 2018, 8:47 a.m. UTC | #5
On Wed, Nov 21, 2018 at 08:18:22PM -0600, Jassi Brar wrote:
> On Wed, Nov 21, 2018 at 8:27 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> >
> > On Tue, Nov 20, 2018 at 04:29:07PM +0100, Thierry Reding wrote:
> > > On Sat, Nov 17, 2018 at 11:27:17AM -0600, Jassi Brar wrote:
> > > > On Mon, Nov 12, 2018 at 9:18 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > >
> > > > > From: Thierry Reding <treding@nvidia.com>
> > > > >
> > > > > The mailbox framework supports blocking transfers via completions for
> > > > > clients that can sleep. In order to support blocking transfers in cases
> > > > > where the transmission is not permitted to sleep, add a new ->flush()
> > > > > callback that controller drivers can implement to busy loop until the
> > > > > transmission has been completed. This will automatically be called when
> > > > > available and interrupts are disabled for clients that request blocking
> > > > > transfers.
> > > > >
> > > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > > ---
> > > > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > > > >  include/linux/mailbox_controller.h | 4 ++++
> > > > >  2 files changed, 12 insertions(+)
> > > > >
> > > > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > > > index 674b35f402f5..0eaf21259874 100644
> > > > > --- a/drivers/mailbox/mailbox.c
> > > > > +++ b/drivers/mailbox/mailbox.c
> > > > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > > > >                 unsigned long wait;
> > > > >                 int ret;
> > > > >
> > > > > +               if (irqs_disabled() && chan->mbox->ops->flush) {
> > > > > +                       ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > > > +                       if (ret < 0)
> > > > > +                               tx_tick(chan, ret);
> > > > > +
> > > > > +                       return ret;
> > > > > +               }
> > > > > +
> > > > This is hacky. I think we can do without busy waiting in atomic
> > > > context. You could queue locally while in atomic context and then
> > > > transfer in blocking mode. I don't think we should worry about the
> > > > 'throughput' as there already is no h/w rate control even with
> > > > busy-waiting.
> > >
> > > I actually tried to do that before I added this flushing mechanism. The
> > > problem is, like you said, one of rate control. As mentioned in the
> > > cover letter, the shared mailboxes implemented in tegra-hsp are used as
> > > RX and TX channels for the TCU, which is like a virtual UART. The TTY
> > > driver included as part of this series will use one of the mailboxes to
> > > transmit data that is written to the console. The problem is that if
> > > these transmissions are not rate-limited on the TTY driver side, the
> > > console will just keep writing data and eventually overflow the buffer
> > > that we have in the mailbox subsystem.
> > >
> > > The problem is that data comes in at a much higher rate than what we can
> > > output. This is especially true at boot when the TCU console takes over
> > > and the whole log buffer is dumped on it.
> > >
> > > So the only way to rate-limit is to either make mbox_send_message()
> > > block, but that can only be done in non-atomic context. The console,
> > > however, will always run in atomic context, so the only way to do rate-
> > > limiting is by busy looping.
> >
> > What I also tried before was to implement busy looping within the
> > ->send_data() callback of the driver so that we didn't have to put this
> > into the core. Unfortunately, however, the ->send_data() callback is
> > called under chan->lock, which means that from mbox_send_message() we
> > don't have a way to mark the transfer as done. In order to do that we'd
> > have to call mbox_chan_txdone(), but that ends up calling tx_tick() and
> > that in turn also attempts to take the chan->lock, which would cause a
> > deadlock.
> >
> > The explicit flushing is the best alternative that I could come up with.
> > I think it's not all that hacky, because it's very explicit about what's
> > going on and it has the nice side-effect that it will allow the mailbox
> > to work in interrupt driven mode if possible and only resorting to the
> > busy loop in atomic context.
> >
> > At this point I think I have explored all other options and I frankly
> > can't find a more proper way to achieve what we need here. Perhaps you
> > can think of additional ways to accomplish this?
> >
> Well, I would have a local ring buffer (array) of enough size to hold
> the characters and then have a task consuming data from that ring
> buffer by transmitting over mailbox.

There's already such a ringbuffer in the printk code. To implement what
you suggest would effectively be creating a copy of that buffer because
we'd be allocating the buffer and the console code would just dump each
and every character in the logbuf into that ring buffer without rate-
limitation.

To make matters worse, the ringbuffer would be empty most of the time
after the initial dump of the logbuf, so we'd be wasting all that buffer
space.

It just seems to me like we should be keeping the TCU driver as close as
possible to other UART drivers which also busy loop in order to rate-
limit what the console can write. Given the current mailbox framework it
is not possible to do that (in interrupt context), so an extension seems
like the most sensible option.

Perhaps you'd be less concerned about such a change if it was perhaps
more explicit? Just throwing ideas around, I think something that could
also work is if we explicitly add a mbox_flush() function that would
basically be calling ->flush(). That way users of the mailbox can make
their requirement very explicit. I haven't actually tested that, but I
think it would work. Does that sound more acceptable to you?

Thierry
Jassi Brar Nov. 22, 2018, 4:07 p.m. UTC | #6
Hi Thierry,

On Thu, Nov 22, 2018 at 2:47 AM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Wed, Nov 21, 2018 at 08:18:22PM -0600, Jassi Brar wrote:
> > On Wed, Nov 21, 2018 at 8:27 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > >
> > > On Tue, Nov 20, 2018 at 04:29:07PM +0100, Thierry Reding wrote:
> > > > On Sat, Nov 17, 2018 at 11:27:17AM -0600, Jassi Brar wrote:
> > > > > On Mon, Nov 12, 2018 at 9:18 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > > >
> > > > > > From: Thierry Reding <treding@nvidia.com>
> > > > > >
> > > > > > The mailbox framework supports blocking transfers via completions for
> > > > > > clients that can sleep. In order to support blocking transfers in cases
> > > > > > where the transmission is not permitted to sleep, add a new ->flush()
> > > > > > callback that controller drivers can implement to busy loop until the
> > > > > > transmission has been completed. This will automatically be called when
> > > > > > available and interrupts are disabled for clients that request blocking
> > > > > > transfers.
> > > > > >
> > > > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > > > ---
> > > > > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > > > > >  include/linux/mailbox_controller.h | 4 ++++
> > > > > >  2 files changed, 12 insertions(+)
> > > > > >
> > > > > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > > > > index 674b35f402f5..0eaf21259874 100644
> > > > > > --- a/drivers/mailbox/mailbox.c
> > > > > > +++ b/drivers/mailbox/mailbox.c
> > > > > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > > > > >                 unsigned long wait;
> > > > > >                 int ret;
> > > > > >
> > > > > > +               if (irqs_disabled() && chan->mbox->ops->flush) {
> > > > > > +                       ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > > > > +                       if (ret < 0)
> > > > > > +                               tx_tick(chan, ret);
> > > > > > +
> > > > > > +                       return ret;
> > > > > > +               }
> > > > > > +
> > > > > This is hacky. I think we can do without busy waiting in atomic
> > > > > context. You could queue locally while in atomic context and then
> > > > > transfer in blocking mode. I don't think we should worry about the
> > > > > 'throughput' as there already is no h/w rate control even with
> > > > > busy-waiting.
> > > >
> > > > I actually tried to do that before I added this flushing mechanism. The
> > > > problem is, like you said, one of rate control. As mentioned in the
> > > > cover letter, the shared mailboxes implemented in tegra-hsp are used as
> > > > RX and TX channels for the TCU, which is like a virtual UART. The TTY
> > > > driver included as part of this series will use one of the mailboxes to
> > > > transmit data that is written to the console. The problem is that if
> > > > these transmissions are not rate-limited on the TTY driver side, the
> > > > console will just keep writing data and eventually overflow the buffer
> > > > that we have in the mailbox subsystem.
> > > >
> > > > The problem is that data comes in at a much higher rate than what we can
> > > > output. This is especially true at boot when the TCU console takes over
> > > > and the whole log buffer is dumped on it.
> > > >
> > > > So the only way to rate-limit is to either make mbox_send_message()
> > > > block, but that can only be done in non-atomic context. The console,
> > > > however, will always run in atomic context, so the only way to do rate-
> > > > limiting is by busy looping.
> > >
> > > What I also tried before was to implement busy looping within the
> > > ->send_data() callback of the driver so that we didn't have to put this
> > > into the core. Unfortunately, however, the ->send_data() callback is
> > > called under chan->lock, which means that from mbox_send_message() we
> > > don't have a way to mark the transfer as done. In order to do that we'd
> > > have to call mbox_chan_txdone(), but that ends up calling tx_tick() and
> > > that in turn also attempts to take the chan->lock, which would cause a
> > > deadlock.
> > >
> > > The explicit flushing is the best alternative that I could come up with.
> > > I think it's not all that hacky, because it's very explicit about what's
> > > going on and it has the nice side-effect that it will allow the mailbox
> > > to work in interrupt driven mode if possible and only resorting to the
> > > busy loop in atomic context.
> > >
> > > At this point I think I have explored all other options and I frankly
> > > can't find a more proper way to achieve what we need here. Perhaps you
> > > can think of additional ways to accomplish this?
> > >
> > Well, I would have a local ring buffer (array) of enough size to hold
> > the characters and then have a task consuming data from that ring
> > buffer by transmitting over mailbox.
>
> There's already such a ringbuffer in the printk code. To implement what
> you suggest would effectively be creating a copy of that buffer because
> we'd be allocating the buffer and the console code would just dump each
> and every character in the logbuf into that ring buffer without rate-
> limitation.
>
Well, the console assumes there exists an atomic path to put character
on the bus, But because there isn't in case of tcu, we have to emulate
that. Frankly I prefer the one-off driver jump some hoops, rather than
implement exceptions in the api.
BTW, there is already no rate-limitation because its all virtual -
data is consumed as fast as possible.

> To make matters worse, the ringbuffer would be empty most of the time
> after the initial dump of the logbuf, so we'd be wasting all that buffer
> space.
>
The idea is console and uart-ops both feed into this buffer and the
only consumer thread runs the mailbox.

> It just seems to me like we should be keeping the TCU driver as close as
> possible to other UART drivers which also busy loop in order to rate-
> limit what the console can write. Given the current mailbox framework it
> is not possible to do that (in interrupt context), so an extension seems
> like the most sensible option.
>
> Perhaps you'd be less concerned about such a change if it was perhaps
> more explicit? Just throwing ideas around, I think something that could
> also work is if we explicitly add a mbox_flush() function that would
> basically be calling ->flush(). That way users of the mailbox can make
> their requirement very explicit. I haven't actually tested that, but I
> think it would work. Does that sound more acceptable to you?
>
I am happy to see features and bugfixes added to the api. What I am
not eager about is supporting less than 100% legit and very platform
specific usecases, especially when there is a work around.

Thanks.
Thierry Reding Nov. 22, 2018, 5:34 p.m. UTC | #7
On Thu, Nov 22, 2018 at 10:07:09AM -0600, Jassi Brar wrote:
> Hi Thierry,
> 
> On Thu, Nov 22, 2018 at 2:47 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> >
> > On Wed, Nov 21, 2018 at 08:18:22PM -0600, Jassi Brar wrote:
> > > On Wed, Nov 21, 2018 at 8:27 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > > >
> > > > On Tue, Nov 20, 2018 at 04:29:07PM +0100, Thierry Reding wrote:
> > > > > On Sat, Nov 17, 2018 at 11:27:17AM -0600, Jassi Brar wrote:
> > > > > > On Mon, Nov 12, 2018 at 9:18 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > > > >
> > > > > > > From: Thierry Reding <treding@nvidia.com>
> > > > > > >
> > > > > > > The mailbox framework supports blocking transfers via completions for
> > > > > > > clients that can sleep. In order to support blocking transfers in cases
> > > > > > > where the transmission is not permitted to sleep, add a new ->flush()
> > > > > > > callback that controller drivers can implement to busy loop until the
> > > > > > > transmission has been completed. This will automatically be called when
> > > > > > > available and interrupts are disabled for clients that request blocking
> > > > > > > transfers.
> > > > > > >
> > > > > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > > > > ---
> > > > > > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > > > > > >  include/linux/mailbox_controller.h | 4 ++++
> > > > > > >  2 files changed, 12 insertions(+)
> > > > > > >
> > > > > > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > > > > > index 674b35f402f5..0eaf21259874 100644
> > > > > > > --- a/drivers/mailbox/mailbox.c
> > > > > > > +++ b/drivers/mailbox/mailbox.c
> > > > > > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > > > > > >                 unsigned long wait;
> > > > > > >                 int ret;
> > > > > > >
> > > > > > > +               if (irqs_disabled() && chan->mbox->ops->flush) {
> > > > > > > +                       ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > > > > > +                       if (ret < 0)
> > > > > > > +                               tx_tick(chan, ret);
> > > > > > > +
> > > > > > > +                       return ret;
> > > > > > > +               }
> > > > > > > +
> > > > > > This is hacky. I think we can do without busy waiting in atomic
> > > > > > context. You could queue locally while in atomic context and then
> > > > > > transfer in blocking mode. I don't think we should worry about the
> > > > > > 'throughput' as there already is no h/w rate control even with
> > > > > > busy-waiting.
> > > > >
> > > > > I actually tried to do that before I added this flushing mechanism. The
> > > > > problem is, like you said, one of rate control. As mentioned in the
> > > > > cover letter, the shared mailboxes implemented in tegra-hsp are used as
> > > > > RX and TX channels for the TCU, which is like a virtual UART. The TTY
> > > > > driver included as part of this series will use one of the mailboxes to
> > > > > transmit data that is written to the console. The problem is that if
> > > > > these transmissions are not rate-limited on the TTY driver side, the
> > > > > console will just keep writing data and eventually overflow the buffer
> > > > > that we have in the mailbox subsystem.
> > > > >
> > > > > The problem is that data comes in at a much higher rate than what we can
> > > > > output. This is especially true at boot when the TCU console takes over
> > > > > and the whole log buffer is dumped on it.
> > > > >
> > > > > So the only way to rate-limit is to either make mbox_send_message()
> > > > > block, but that can only be done in non-atomic context. The console,
> > > > > however, will always run in atomic context, so the only way to do rate-
> > > > > limiting is by busy looping.
> > > >
> > > > What I also tried before was to implement busy looping within the
> > > > ->send_data() callback of the driver so that we didn't have to put this
> > > > into the core. Unfortunately, however, the ->send_data() callback is
> > > > called under chan->lock, which means that from mbox_send_message() we
> > > > don't have a way to mark the transfer as done. In order to do that we'd
> > > > have to call mbox_chan_txdone(), but that ends up calling tx_tick() and
> > > > that in turn also attempts to take the chan->lock, which would cause a
> > > > deadlock.
> > > >
> > > > The explicit flushing is the best alternative that I could come up with.
> > > > I think it's not all that hacky, because it's very explicit about what's
> > > > going on and it has the nice side-effect that it will allow the mailbox
> > > > to work in interrupt driven mode if possible and only resorting to the
> > > > busy loop in atomic context.
> > > >
> > > > At this point I think I have explored all other options and I frankly
> > > > can't find a more proper way to achieve what we need here. Perhaps you
> > > > can think of additional ways to accomplish this?
> > > >
> > > Well, I would have a local ring buffer (array) of enough size to hold
> > > the characters and then have a task consuming data from that ring
> > > buffer by transmitting over mailbox.
> >
> > There's already such a ringbuffer in the printk code. To implement what
> > you suggest would effectively be creating a copy of that buffer because
> > we'd be allocating the buffer and the console code would just dump each
> > and every character in the logbuf into that ring buffer without rate-
> > limitation.
> >
> Well, the console assumes there exists an atomic path to put character
> on the bus, But because there isn't in case of tcu, we have to emulate
> that. Frankly I prefer the one-off driver jump some hoops, rather than
> implement exceptions in the api.

I wouldn't have any objections to that if the hoops were reasonable
ones. What you're asking me to do is basically implement a second copy
of the logbuf. I don't call that a reasonable hoop to jump through. It
is also not guaranteed to work properly because we can always end up
with a situation where we produce more data than we can consume. Also,
by providing this additional buffer we make things worse because the
standard mechanisms of the logbuf are side-stepped. Typically the logbuf
code will warn if it overflows. In our case we provide a buffer that the
console can dump into, so instead of the logbuf overflowing and warning
about it, we'd now overflow the mailbox buffer and we'd have to add
extra code to warn about overflows.

The console really only works because it assumes that the output driver
will stall and thereby rate-limit.

> BTW, there is already no rate-limitation because its all virtual -
> data is consumed as fast as possible.

No, that's not true. On the receiving end of the TX mailbox is a micro-
processor that reads out the mailbox data. Once it has read the data it
needs to clear the FULL bit so that the TCU driver can write more data.
Even if the microprocessor on the receiving end did buffering (there is
no indication that it does) it would eventually run out of buffer space
and have to stall until it's clocked all the characters out of the
physical UART. At that point no amount of buffering is going to save us
and the only option is to stall, which, again, can currently only be
done from the mailbox, because it is the only one that knows when it is
busy. But it's also the one place where we can't because the framework
doesn't allow it.

> > To make matters worse, the ringbuffer would be empty most of the time
> > after the initial dump of the logbuf, so we'd be wasting all that buffer
> > space.
> >
> The idea is console and uart-ops both feed into this buffer and the
> only consumer thread runs the mailbox.
> 
> > It just seems to me like we should be keeping the TCU driver as close as
> > possible to other UART drivers which also busy loop in order to rate-
> > limit what the console can write. Given the current mailbox framework it
> > is not possible to do that (in interrupt context), so an extension seems
> > like the most sensible option.
> >
> > Perhaps you'd be less concerned about such a change if it was perhaps
> > more explicit? Just throwing ideas around, I think something that could
> > also work is if we explicitly add a mbox_flush() function that would
> > basically be calling ->flush(). That way users of the mailbox can make
> > their requirement very explicit. I haven't actually tested that, but I
> > think it would work. Does that sound more acceptable to you?
> >
> I am happy to see features and bugfixes added to the api. What I am
> not eager about is supporting less than 100% legit and very platform
> specific usecases, especially when there is a work around.

Look, I'd be willing to push this all into the driver, but the framework
doesn't allow me to do that. If it was possible to run the state machine
outside of mbox_send_message() then I'd be able to just move the busy
loop or flush into the driver. But the locking is such that I can't do
that because it will cause a deadlock.

The ringbuffer workaround would be very brittle and if at all only work
by accident, not to mention that it would require duplicating much of
the logbuf logic.

Also I don't consider usage in atomic context a very platform specific
use-case, and I don't understand why I should be required to resort to
some unreliable workaround rather than find a proper fix that guarantees
proper operation.

Thierry
Thierry Reding Nov. 23, 2018, 11:17 a.m. UTC | #8
On Thu, Nov 22, 2018 at 09:47:12AM +0100, Thierry Reding wrote:
[...]
> Perhaps you'd be less concerned about such a change if it was perhaps
> more explicit? Just throwing ideas around, I think something that could
> also work is if we explicitly add a mbox_flush() function that would
> basically be calling ->flush(). That way users of the mailbox can make
> their requirement very explicit. I haven't actually tested that, but I
> think it would work. Does that sound more acceptable to you?

I tried implementing the explicit flushing on top of this series and it
would look roughly like the below. What do you think?

Thierry

--->8---
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 3e7e2c4358aa..fbdcc82a61ae 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -267,14 +267,6 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
 		unsigned long wait;
 		int ret;
 
-		if (irqs_disabled() && chan->mbox->ops->flush) {
-			ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
-			if (ret < 0)
-				tx_tick(chan, ret);
-
-			return ret;
-		}
-
 		if (!chan->cl->tx_tout) /* wait forever */
 			wait = msecs_to_jiffies(3600000);
 		else
@@ -291,6 +283,34 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
 }
 EXPORT_SYMBOL_GPL(mbox_send_message);
 
+/**
+ * mbox_flush - flush a mailbox channel
+ * @chan: mailbox channel to flush
+ * @timeout: time, in milliseconds, to allow the flush operation to succeed
+ *
+ * Mailbox controllers that need to work in atomic context can implement the
+ * ->flush() callback to busy loop until a transmission has been completed.
+ * The implementation must call mbox_chan_txdone() upon success. Clients can
+ * call the mbox_flush() function at any time after mbox_send_message() to
+ * flush the transmission. After the function returns success, the mailbox
+ * transmission is guaranteed to have completed.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int mbox_flush(struct mbox_chan *chan, unsigned long timeout)
+{
+	int ret;
+
+	if (!chan->mbox->ops->flush)
+		return -ENOTSUPP;
+
+	ret = chan->mbox->ops->flush(chan, timeout);
+	if (ret < 0)
+		tx_tick(chan, ret);
+
+	return ret;
+}
+
 /**
  * mbox_request_channel - Request a mailbox channel.
  * @cl: Identity of the client requesting the channel.
diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
index f99c406cb3cc..e443f6a2ec4b 100644
--- a/drivers/mailbox/tegra-hsp.c
+++ b/drivers/mailbox/tegra-hsp.c
@@ -387,15 +387,13 @@ static int tegra_hsp_mailbox_send_data(struct mbox_chan *chan, void *data)
 
 	tegra_hsp_channel_writel(&mb->channel, value, HSP_SM_SHRD_MBOX);
 
-	if (!irqs_disabled()) {
-		/* enable EMPTY interrupt for the shared mailbox */
-		spin_lock_irqsave(&hsp->lock, flags);
+	/* enable EMPTY interrupt for the shared mailbox */
+	spin_lock_irqsave(&hsp->lock, flags);
 
-		hsp->mask |= BIT(HSP_INT_EMPTY_SHIFT + mb->index);
-		tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq));
+	hsp->mask |= BIT(HSP_INT_EMPTY_SHIFT + mb->index);
+	tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq));
 
-		spin_unlock_irqrestore(&hsp->lock, flags);
-	}
+	spin_unlock_irqrestore(&hsp->lock, flags);
 
 	return 0;
 }
diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c
index 1d360cd03b18..59eaa13e169e 100644
--- a/drivers/tty/serial/tegra-tcu.c
+++ b/drivers/tty/serial/tegra-tcu.c
@@ -57,6 +57,7 @@ static void tegra_tcu_write_one(struct tegra_tcu *tcu, u32 value,
 	value |= TCU_MBOX_NUM_BYTES(count);
 	msg = (void *)(unsigned long)value;
 	mbox_send_message(tcu->tx, msg);
+	mbox_flush(tcu->tx, 1000);
 }
 
 static void tegra_tcu_write(struct tegra_tcu *tcu, const char *s,
@@ -184,9 +185,6 @@ static int tegra_tcu_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	tcu->tx_client.dev = &pdev->dev;
-	tcu->tx_client.tx_block = true;
-	tcu->tx_client.tx_tout = 10000;
-	tcu->rx_client.dev = &pdev->dev;
 	tcu->rx_client.rx_callback = tegra_tcu_receive;
 
 	tcu->tx = mbox_request_channel_byname(&tcu->tx_client, "tx");
diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h
index 44348710953f..faa7da3c9c8b 100644
--- a/include/linux/mailbox_client.h
+++ b/include/linux/mailbox_client.h
@@ -44,6 +44,7 @@ struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
 					      const char *name);
 struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);
 int mbox_send_message(struct mbox_chan *chan, void *mssg);
+int mbox_flush(struct mbox_chan *chan, unsigned long timeout);
 void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */
 bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */
 void mbox_free_channel(struct mbox_chan *chan); /* may sleep */
Thierry Reding Nov. 23, 2018, 11:56 a.m. UTC | #9
On Fri, Nov 23, 2018 at 12:17:00PM +0100, Thierry Reding wrote:
> On Thu, Nov 22, 2018 at 09:47:12AM +0100, Thierry Reding wrote:
> [...]
> > Perhaps you'd be less concerned about such a change if it was perhaps
> > more explicit? Just throwing ideas around, I think something that could
> > also work is if we explicitly add a mbox_flush() function that would
> > basically be calling ->flush(). That way users of the mailbox can make
> > their requirement very explicit. I haven't actually tested that, but I
> > think it would work. Does that sound more acceptable to you?
> 
> I tried implementing the explicit flushing on top of this series and it
> would look roughly like the below. What do you think?
> 
> Thierry
> 
> --->8---
> diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
[...]
> @@ -184,9 +185,6 @@ static int tegra_tcu_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  
>  	tcu->tx_client.dev = &pdev->dev;
> -	tcu->tx_client.tx_block = true;
> -	tcu->tx_client.tx_tout = 10000;
> -	tcu->rx_client.dev = &pdev->dev;

Somehow this line ended up being removed in the diff, but it's actually
required. Only tx_block and tx_tout should be removed in this hunk.

Thierry
Jon Hunter Nov. 28, 2018, 9:43 a.m. UTC | #10
On 12/11/2018 15:18, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> The mailbox framework supports blocking transfers via completions for
> clients that can sleep. In order to support blocking transfers in cases
> where the transmission is not permitted to sleep, add a new ->flush()
> callback that controller drivers can implement to busy loop until the
> transmission has been completed. This will automatically be called when
> available and interrupts are disabled for clients that request blocking
> transfers.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/mailbox/mailbox.c          | 8 ++++++++
>  include/linux/mailbox_controller.h | 4 ++++
>  2 files changed, 12 insertions(+)
> 
> diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> index 674b35f402f5..0eaf21259874 100644
> --- a/drivers/mailbox/mailbox.c
> +++ b/drivers/mailbox/mailbox.c
> @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
>  		unsigned long wait;
>  		int ret;
>  
> +		if (irqs_disabled() && chan->mbox->ops->flush) {
> +			ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> +			if (ret < 0)
> +				tx_tick(chan, ret);
> +
> +			return ret;
> +		}

It seems to me that if mbox_send_message() is called from an atomic
context AND tx_block is true, then if 'flush' is not populated this
should be an error condition as we do not wish to call
wait_for_completion from an atomic context.

I understand that there is some debate about adding such flush support,
but irrespective of the above change, it seems to me that if the
mbox_send_message() can be called from an atomic context (which it
appears to), then it should be detecting if someone is trying to do so
with 'tx_block' set as this should be an error.

Furthermore, if the underlying mailbox driver can support sending a
message from an atomic context and busy wait until it is done, surely
the mailbox framework should provide a means to support this?

Now it could be possible for the underlying mailbox driver to detect we
are in an atomic context and if the 'tx_block' is set do the right thing
by busy waiting until the message is sent. However, the problem with
that is, that for the mbox_send_message() to ensure the right thing is
done, it needs to check that 'tx_done' is set in the case of a blocking
transfer in an atomic context. At that point you may as well add the
flush operator as I think it is more implicit/clear.

Cheers
Jon
Thierry Reding Nov. 28, 2018, 10:08 a.m. UTC | #11
On Wed, Nov 28, 2018 at 09:43:29AM +0000, Jon Hunter wrote:
> 
> On 12/11/2018 15:18, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > The mailbox framework supports blocking transfers via completions for
> > clients that can sleep. In order to support blocking transfers in cases
> > where the transmission is not permitted to sleep, add a new ->flush()
> > callback that controller drivers can implement to busy loop until the
> > transmission has been completed. This will automatically be called when
> > available and interrupts are disabled for clients that request blocking
> > transfers.
> > 
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/mailbox/mailbox.c          | 8 ++++++++
> >  include/linux/mailbox_controller.h | 4 ++++
> >  2 files changed, 12 insertions(+)
> > 
> > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > index 674b35f402f5..0eaf21259874 100644
> > --- a/drivers/mailbox/mailbox.c
> > +++ b/drivers/mailbox/mailbox.c
> > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> >  		unsigned long wait;
> >  		int ret;
> >  
> > +		if (irqs_disabled() && chan->mbox->ops->flush) {
> > +			ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > +			if (ret < 0)
> > +				tx_tick(chan, ret);
> > +
> > +			return ret;
> > +		}
> 
> It seems to me that if mbox_send_message() is called from an atomic
> context AND tx_block is true, then if 'flush' is not populated this
> should be an error condition as we do not wish to call
> wait_for_completion from an atomic context.
> 
> I understand that there is some debate about adding such flush support,
> but irrespective of the above change, it seems to me that if the
> mbox_send_message() can be called from an atomic context (which it
> appears to), then it should be detecting if someone is trying to do so
> with 'tx_block' set as this should be an error.
> 
> Furthermore, if the underlying mailbox driver can support sending a
> message from an atomic context and busy wait until it is done, surely
> the mailbox framework should provide a means to support this?
> 
> Now it could be possible for the underlying mailbox driver to detect we
> are in an atomic context and if the 'tx_block' is set do the right thing
> by busy waiting until the message is sent. However, the problem with
> that is, that for the mbox_send_message() to ensure the right thing is
> done, it needs to check that 'tx_done' is set in the case of a blocking
> transfer in an atomic context. At that point you may as well add the
> flush operator as I think it is more implicit/clear.

Heh, interesting timing, I just sent out v3 of this series with a
slightly different solution. Basically what v3 implements is explicit
flushing of the mailbox via a new function called mbox_flush().

I originally liked the idea of "hiding" the flush operation in the
mbox_send_message() function, but the more I look at the new explicit
flush solution, the more I prefer it. One reason why I prefer it is
because it no longer has the slightly odd "irqs_disabled()" check that
always made me a bit uneasy. The other advantage of the explicit flush
is that it becomes completely opt-in for both mailbox providers and
consumers. Furthermore the consumer should always know if it can be
called in atomic context or not, so none of these code paths should
have to be "clever" and check at runtime whether or not interrupts are
enabled. If the consumer knows that it will be called in atomic context
it can just unconditionally flush the mailbox.

Also, it nicely addresses the concern about erroring out if the provider
doesn't support flushing. The new mbox_flush() API returns -ENOTSUPP if
called on a mailbox channel provided by a driver that doesn't implement
->flush().

Thierry
Jassi Brar Nov. 29, 2018, 5:23 a.m. UTC | #12
On Wed, Nov 28, 2018 at 3:43 AM Jon Hunter <jonathanh@nvidia.com> wrote:
>
>
> On 12/11/2018 15:18, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> >
> > The mailbox framework supports blocking transfers via completions for
> > clients that can sleep. In order to support blocking transfers in cases
> > where the transmission is not permitted to sleep, add a new ->flush()
> > callback that controller drivers can implement to busy loop until the
> > transmission has been completed. This will automatically be called when
> > available and interrupts are disabled for clients that request blocking
> > transfers.
> >
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/mailbox/mailbox.c          | 8 ++++++++
> >  include/linux/mailbox_controller.h | 4 ++++
> >  2 files changed, 12 insertions(+)
> >
> > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > index 674b35f402f5..0eaf21259874 100644
> > --- a/drivers/mailbox/mailbox.c
> > +++ b/drivers/mailbox/mailbox.c
> > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> >               unsigned long wait;
> >               int ret;
> >
> > +             if (irqs_disabled() && chan->mbox->ops->flush) {
> > +                     ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > +                     if (ret < 0)
> > +                             tx_tick(chan, ret);
> > +
> > +                     return ret;
> > +             }
>
> It seems to me that if mbox_send_message() is called from an atomic
> context AND tx_block is true, then if 'flush' is not populated this
> should be an error condition as we do not wish to call
> wait_for_completion from an atomic context.
>
> I understand that there is some debate about adding such flush support,
> but irrespective of the above change, it seems to me that if the
> mbox_send_message() can be called from an atomic context (which it
> appears to), then it should be detecting if someone is trying to do so
> with 'tx_block' set as this should be an error.
>
Layers within kernel space have to trust each other. A badly written
client can break the consumer in so many ways, we can not catch every
possibility.

> Furthermore, if the underlying mailbox driver can support sending a
> message from an atomic context and busy wait until it is done, surely
> the mailbox framework should provide a means to support this?
>
Being able to put the message on bus in atomic context is a feature -
which we do support. But busy-waiting in a loop is not a feature, and
we don't want to encourage that.

Cheers!
Thierry Reding Nov. 29, 2018, 3:23 p.m. UTC | #13
On Wed, Nov 28, 2018 at 11:23:36PM -0600, Jassi Brar wrote:
> On Wed, Nov 28, 2018 at 3:43 AM Jon Hunter <jonathanh@nvidia.com> wrote:
> >
> >
> > On 12/11/2018 15:18, Thierry Reding wrote:
> > > From: Thierry Reding <treding@nvidia.com>
> > >
> > > The mailbox framework supports blocking transfers via completions for
> > > clients that can sleep. In order to support blocking transfers in cases
> > > where the transmission is not permitted to sleep, add a new ->flush()
> > > callback that controller drivers can implement to busy loop until the
> > > transmission has been completed. This will automatically be called when
> > > available and interrupts are disabled for clients that request blocking
> > > transfers.
> > >
> > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > ---
> > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > >  include/linux/mailbox_controller.h | 4 ++++
> > >  2 files changed, 12 insertions(+)
> > >
> > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > index 674b35f402f5..0eaf21259874 100644
> > > --- a/drivers/mailbox/mailbox.c
> > > +++ b/drivers/mailbox/mailbox.c
> > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > >               unsigned long wait;
> > >               int ret;
> > >
> > > +             if (irqs_disabled() && chan->mbox->ops->flush) {
> > > +                     ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > +                     if (ret < 0)
> > > +                             tx_tick(chan, ret);
> > > +
> > > +                     return ret;
> > > +             }
> >
> > It seems to me that if mbox_send_message() is called from an atomic
> > context AND tx_block is true, then if 'flush' is not populated this
> > should be an error condition as we do not wish to call
> > wait_for_completion from an atomic context.
> >
> > I understand that there is some debate about adding such flush support,
> > but irrespective of the above change, it seems to me that if the
> > mbox_send_message() can be called from an atomic context (which it
> > appears to), then it should be detecting if someone is trying to do so
> > with 'tx_block' set as this should be an error.
> >
> Layers within kernel space have to trust each other. A badly written
> client can break the consumer in so many ways, we can not catch every
> possibility.
> 
> > Furthermore, if the underlying mailbox driver can support sending a
> > message from an atomic context and busy wait until it is done, surely
> > the mailbox framework should provide a means to support this?
> >
> Being able to put the message on bus in atomic context is a feature -
> which we do support. But busy-waiting in a loop is not a feature, and
> we don't want to encourage that.

I agree that in generally busy-waiting is a bad idea and shouldn't be
encouraged. However, I also think that an exception proves the rule. If
you look at the console drivers in drivers/tty/serial, all of them will
busy loop prior to or after sending a character. This is pretty much
part of the API and as such busy-looping is very much a feature.

The reason why this is done is because on one hand we have an atomic
context and on the other hand we want to make sure that all characters
actually make it to the console when we print them.

As an example how this can break, I've taken your suggestion to
implement a producer/consumer mode in the TCU driver where the console
write function will just stash characters into a circular buffer and a
work queue will then use mbox_send_message() to drain the circular
buffer. While this does work on the surface, I was able to concern both
of the issues that I was concerned about: 1) it is easy to overflow the
circular buffer by just dumping enough data at once to the console and
2) when a crash happens, everything in the kernel stops, including the
consumer workqueue that is supposed to drain the circular buffer and
flush messages to the TCU. The result is that, in my particular case,
the boot log will just stop at a random place in the middle of messages
from much earlier in the boot because the TCU hasn't caught up yet and
there's a lot of characters still in the circular buffer.

Now, 1) can be mitigated by increasing the circular buffer size. A value
that seems to give reliably good results in 2 << CONFIG_LOG_BUF_SHIFT. I
thought that I could also mitigate 2) by busy looping in the TCU driver,
but it turns out that that doesn't work. The reason is that since we are
in atomic context with all interrupts disabled, the mailbox won't ever
consume any new characters, so the read pointer in the circular buffer
won't increment, leaving me with no condition upon which to loop that
would work.

Given that 2) can't be solved makes this implementation of the TCU
completely useless as a console.

All of that said, have you had a chance to look at my other proposed
solution for this? I sent it out yesterday, but you can also find the
series here:

	http://patchwork.ozlabs.org/project/linux-tegra/list/?series=78477

The difference to the earlier versions is that the flushing is now
explicit. I think this combines the best of both worlds. On one hand it
makes the mechanism completely opt-in, so nothing gets hidden in the
regular functions. On the other hand, it allows clients to make use of
this functionality very explicitly. A client that doesn't call the
mbox_flush() function will just not busy-loop. But clients that need to
make sure messages are flushed in atomic contexts can now do that. Does
that sound like a more acceptable solution to you? We could even go and
add documentation to mbox_flush() that it should only be used under
special circumstances.

If you still think that's a bad idea, do you have any other suggestions
on how to move forward?

Thierry
Jassi Brar Dec. 7, 2018, 5:56 a.m. UTC | #14
On Thu, Nov 29, 2018 at 9:23 AM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Wed, Nov 28, 2018 at 11:23:36PM -0600, Jassi Brar wrote:
> > On Wed, Nov 28, 2018 at 3:43 AM Jon Hunter <jonathanh@nvidia.com> wrote:
> > >
> > >
> > > On 12/11/2018 15:18, Thierry Reding wrote:
> > > > From: Thierry Reding <treding@nvidia.com>
> > > >
> > > > The mailbox framework supports blocking transfers via completions for
> > > > clients that can sleep. In order to support blocking transfers in cases
> > > > where the transmission is not permitted to sleep, add a new ->flush()
> > > > callback that controller drivers can implement to busy loop until the
> > > > transmission has been completed. This will automatically be called when
> > > > available and interrupts are disabled for clients that request blocking
> > > > transfers.
> > > >
> > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > ---
> > > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > > >  include/linux/mailbox_controller.h | 4 ++++
> > > >  2 files changed, 12 insertions(+)
> > > >
> > > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > > index 674b35f402f5..0eaf21259874 100644
> > > > --- a/drivers/mailbox/mailbox.c
> > > > +++ b/drivers/mailbox/mailbox.c
> > > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > > >               unsigned long wait;
> > > >               int ret;
> > > >
> > > > +             if (irqs_disabled() && chan->mbox->ops->flush) {
> > > > +                     ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > > +                     if (ret < 0)
> > > > +                             tx_tick(chan, ret);
> > > > +
> > > > +                     return ret;
> > > > +             }
> > >
> > > It seems to me that if mbox_send_message() is called from an atomic
> > > context AND tx_block is true, then if 'flush' is not populated this
> > > should be an error condition as we do not wish to call
> > > wait_for_completion from an atomic context.
> > >
> > > I understand that there is some debate about adding such flush support,
> > > but irrespective of the above change, it seems to me that if the
> > > mbox_send_message() can be called from an atomic context (which it
> > > appears to), then it should be detecting if someone is trying to do so
> > > with 'tx_block' set as this should be an error.
> > >
> > Layers within kernel space have to trust each other. A badly written
> > client can break the consumer in so many ways, we can not catch every
> > possibility.
> >
> > > Furthermore, if the underlying mailbox driver can support sending a
> > > message from an atomic context and busy wait until it is done, surely
> > > the mailbox framework should provide a means to support this?
> > >
> > Being able to put the message on bus in atomic context is a feature -
> > which we do support. But busy-waiting in a loop is not a feature, and
> > we don't want to encourage that.
>
> I agree that in generally busy-waiting is a bad idea and shouldn't be
> encouraged. However, I also think that an exception proves the rule. If
> you look at the console drivers in drivers/tty/serial, all of them will
> busy loop prior to or after sending a character. This is pretty much
> part of the API and as such busy-looping is very much a feature.
>
> The reason why this is done is because on one hand we have an atomic
> context and on the other hand we want to make sure that all characters
> actually make it to the console when we print them.
>
> As an example how this can break, I've taken your suggestion to
> implement a producer/consumer mode in the TCU driver where the console
> write function will just stash characters into a circular buffer and a
> work queue will then use mbox_send_message() to drain the circular
> buffer. While this does work on the surface, I was able to concern both
> of the issues that I was concerned about: 1) it is easy to overflow the
> circular buffer by just dumping enough data at once to the console and
> 2) when a crash happens, everything in the kernel stops, including the
> consumer workqueue that is supposed to drain the circular buffer and
> flush messages to the TCU. The result is that, in my particular case,
> the boot log will just stop at a random place in the middle of messages
> from much earlier in the boot because the TCU hasn't caught up yet and
> there's a lot of characters still in the circular buffer.
>
> Now, 1) can be mitigated by increasing the circular buffer size. A value
> that seems to give reliably good results in 2 << CONFIG_LOG_BUF_SHIFT.
>
Yes please.

> I thought that I could also mitigate 2) by busy looping in the TCU driver,
> but it turns out that that doesn't work. The reason is that since we are
> in atomic context with all interrupts disabled, the mailbox won't ever
> consume any new characters, so the read pointer in the circular buffer
> won't increment, leaving me with no condition upon which to loop that
> would work.
>
So you want to be able to rely on an emulated console (running on a
totally different subsystem) to dump development-phase early-boot
logs? At the cost of legitimizing busy looping in atomic context - one
random driver messing up the api for ever. Maybe you could have the
ring buffer in some shmem and only pass the number of valid characters
in it, to the remote?

>
>         http://patchwork.ozlabs.org/project/linux-tegra/list/?series=78477
>
> The difference to the earlier versions is that the flushing is now
> explicit. I think this combines the best of both worlds. On one hand it
> makes the mechanism completely opt-in, so nothing gets hidden in the
> regular functions. On the other hand, it allows clients to make use of
> this functionality very explicitly. A client that doesn't call the
> mbox_flush() function will just not busy-loop. But clients that need to
> make sure messages are flushed in atomic contexts can now do that. Does
> that sound like a more acceptable solution to you? We could even go and
> add documentation to mbox_flush() that it should only be used under
> special circumstances.
>
> If you still think that's a bad idea, do you have any other suggestions
> on how to move forward?
>
It would help if other maintainers chime in if a subsystem should
support busy-wait in atomic context for a one-off driver.

Regards.
Mikko Perttunen Dec. 7, 2018, 6:19 a.m. UTC | #15
On 07/12/2018 11.26, Jassi Brar wrote:
>> I thought that I could also mitigate 2) by busy looping in the TCU driver,
>> but it turns out that that doesn't work. The reason is that since we are
>> in atomic context with all interrupts disabled, the mailbox won't ever
>> consume any new characters, so the read pointer in the circular buffer
>> won't increment, leaving me with no condition upon which to loop that
>> would work.
>>
> So you want to be able to rely on an emulated console (running on a
> totally different subsystem) to dump development-phase early-boot
> logs? At the cost of legitimizing busy looping in atomic context - one
> random driver messing up the api for ever. Maybe you could have the
> ring buffer in some shmem and only pass the number of valid characters
> in it, to the remote?
> 

I would like to note that this is the one and only console interface 
that exists on these systems, for both development phase and production. 
Other UARTs are not externally accessible on the systems, or they are 
comparatively difficult to access as they aren't intended to be used as 
consoles in the system design. The combination of hardware and firmware 
implementing the console is black box from software's point of view 
(similarly to any other UART HW). The interface has also been fixed at 
an early system design phase, as there are many operating systems 
running on these boards, each with their own drivers.

Cheers,
Mikko
Thierry Reding Dec. 7, 2018, 11:32 a.m. UTC | #16
On Thu, Dec 06, 2018 at 11:56:25PM -0600, Jassi Brar wrote:
> On Thu, Nov 29, 2018 at 9:23 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> >
> > On Wed, Nov 28, 2018 at 11:23:36PM -0600, Jassi Brar wrote:
> > > On Wed, Nov 28, 2018 at 3:43 AM Jon Hunter <jonathanh@nvidia.com> wrote:
> > > >
> > > >
> > > > On 12/11/2018 15:18, Thierry Reding wrote:
> > > > > From: Thierry Reding <treding@nvidia.com>
> > > > >
> > > > > The mailbox framework supports blocking transfers via completions for
> > > > > clients that can sleep. In order to support blocking transfers in cases
> > > > > where the transmission is not permitted to sleep, add a new ->flush()
> > > > > callback that controller drivers can implement to busy loop until the
> > > > > transmission has been completed. This will automatically be called when
> > > > > available and interrupts are disabled for clients that request blocking
> > > > > transfers.
> > > > >
> > > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > > ---
> > > > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > > > >  include/linux/mailbox_controller.h | 4 ++++
> > > > >  2 files changed, 12 insertions(+)
> > > > >
> > > > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > > > index 674b35f402f5..0eaf21259874 100644
> > > > > --- a/drivers/mailbox/mailbox.c
> > > > > +++ b/drivers/mailbox/mailbox.c
> > > > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > > > >               unsigned long wait;
> > > > >               int ret;
> > > > >
> > > > > +             if (irqs_disabled() && chan->mbox->ops->flush) {
> > > > > +                     ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > > > +                     if (ret < 0)
> > > > > +                             tx_tick(chan, ret);
> > > > > +
> > > > > +                     return ret;
> > > > > +             }
> > > >
> > > > It seems to me that if mbox_send_message() is called from an atomic
> > > > context AND tx_block is true, then if 'flush' is not populated this
> > > > should be an error condition as we do not wish to call
> > > > wait_for_completion from an atomic context.
> > > >
> > > > I understand that there is some debate about adding such flush support,
> > > > but irrespective of the above change, it seems to me that if the
> > > > mbox_send_message() can be called from an atomic context (which it
> > > > appears to), then it should be detecting if someone is trying to do so
> > > > with 'tx_block' set as this should be an error.
> > > >
> > > Layers within kernel space have to trust each other. A badly written
> > > client can break the consumer in so many ways, we can not catch every
> > > possibility.
> > >
> > > > Furthermore, if the underlying mailbox driver can support sending a
> > > > message from an atomic context and busy wait until it is done, surely
> > > > the mailbox framework should provide a means to support this?
> > > >
> > > Being able to put the message on bus in atomic context is a feature -
> > > which we do support. But busy-waiting in a loop is not a feature, and
> > > we don't want to encourage that.
> >
> > I agree that in generally busy-waiting is a bad idea and shouldn't be
> > encouraged. However, I also think that an exception proves the rule. If
> > you look at the console drivers in drivers/tty/serial, all of them will
> > busy loop prior to or after sending a character. This is pretty much
> > part of the API and as such busy-looping is very much a feature.
> >
> > The reason why this is done is because on one hand we have an atomic
> > context and on the other hand we want to make sure that all characters
> > actually make it to the console when we print them.
> >
> > As an example how this can break, I've taken your suggestion to
> > implement a producer/consumer mode in the TCU driver where the console
> > write function will just stash characters into a circular buffer and a
> > work queue will then use mbox_send_message() to drain the circular
> > buffer. While this does work on the surface, I was able to concern both
> > of the issues that I was concerned about: 1) it is easy to overflow the
> > circular buffer by just dumping enough data at once to the console and
> > 2) when a crash happens, everything in the kernel stops, including the
> > consumer workqueue that is supposed to drain the circular buffer and
> > flush messages to the TCU. The result is that, in my particular case,
> > the boot log will just stop at a random place in the middle of messages
> > from much earlier in the boot because the TCU hasn't caught up yet and
> > there's a lot of characters still in the circular buffer.
> >
> > Now, 1) can be mitigated by increasing the circular buffer size. A value
> > that seems to give reliably good results in 2 << CONFIG_LOG_BUF_SHIFT.
> >
> Yes please.

As I explained elsewhere, I actually went and implemented this. But
given the nature of buffering, this ends up making the TCU completely
useless as a console because in case of a crash, the system will stop
working with a large number of characters still stuck in the buffer.
And that's especially bad in case of a crash because those last
characters that get stuck in the buffer are the most relevant ones
because they contain the stack dump.

> > I thought that I could also mitigate 2) by busy looping in the TCU driver,
> > but it turns out that that doesn't work. The reason is that since we are
> > in atomic context with all interrupts disabled, the mailbox won't ever
> > consume any new characters, so the read pointer in the circular buffer
> > won't increment, leaving me with no condition upon which to loop that
> > would work.
> >
> So you want to be able to rely on an emulated console (running on a
> totally different subsystem) to dump development-phase early-boot
> logs? At the cost of legitimizing busy looping in atomic context - one
> random driver messing up the api for ever. Maybe you could have the
> ring buffer in some shmem and only pass the number of valid characters
> in it, to the remote?

First of all, this is not about development-phase early-boot messages.
We're talking about the system console here. This is what everyone will
want to be using when developing on this device. Sure at some point you
may end up with a system that works and you can have the console on the
network or an attached display or whatever, but even then you may still
want to attach to the console if you ever run into issues where the
system doesn't come up.

Secondly, I don't understand why you think this is an emulated console.
The way that this works is that there's a microprocessor in the system
that interacts with other microprocessors via shared mailboxes to
receive log messages. That microprocessor collects these log messages
and outputs them on a physical UART via multiplexed streams. The host
system can connect to that physical UART and demultiplex these streams
to get the individual log messages from each of the components in the
system.

Lastly, I don't understand why you think we're messing up the API here.
The proposal in this series doesn't even change any of the API, but it
makes it aware of the state of interrupts internally so that it can do
the right thing depending on the call stack. The other proposal, in v3,
is more explicit in that it adds new API to flush the mailbox. The new
API is completely optional and I even offered to document it as being
discouraged because it involves busy looping. At the same time it does
solve a real problem and it doesn't impact any existing mailbox drivers
nor any of their users (because it is completely opt-in).

While I can somewhat relate to your reluctance to extend the API, I do
not see a way around it. Sure, you could decide that this is something
that Linux just won't support, but that would be a first. I'm not aware
of any cases where a concious decision was ever made not to support a
feature because it didn't fit into any existing frameworks. Typically
we deal with this by improving things so that we can support the
additional use-cases. It's really one of the strong points of Linux.

I'm open to any suggestions on how this could be done better. You had
already suggested the ring buffer and I did go and implement it, but it
only confirmed the concerns that I've had with it all along. I realize
that at this point I may be blind to other obvious solutions, but I've
done my best to come up with alternatives and none of them work. If you
have any other ideas, please do share them and I will investigate.

> >         http://patchwork.ozlabs.org/project/linux-tegra/list/?series=78477
> >
> > The difference to the earlier versions is that the flushing is now
> > explicit. I think this combines the best of both worlds. On one hand it
> > makes the mechanism completely opt-in, so nothing gets hidden in the
> > regular functions. On the other hand, it allows clients to make use of
> > this functionality very explicitly. A client that doesn't call the
> > mbox_flush() function will just not busy-loop. But clients that need to
> > make sure messages are flushed in atomic contexts can now do that. Does
> > that sound like a more acceptable solution to you? We could even go and
> > add documentation to mbox_flush() that it should only be used under
> > special circumstances.
> >
> > If you still think that's a bad idea, do you have any other suggestions
> > on how to move forward?
> >
> It would help if other maintainers chime in if a subsystem should
> support busy-wait in atomic context for a one-off driver.

Just as an additional note: it's not the driver that actually requires
the busy looping, it's the use-case (i.e. the consumer). The driver is
working perfectly fine with interrupts enabled, it's just that in the
particular use-case of the console that we have no way of detecting if
or when an interrupt occurred.

Greg,

any ideas on how we can move forward here? For reasons given elsewhere
in this thread I understand that there is no way to make the console
code run in non-atomic context. Have you ever run into a case where the
console driver couldn't busy-loop? Were there any solutions to this?

I've looked through quite a few drivers and they all end up with a busy
loop, waiting for the transmission FIFO to become empty. There are also
a few implementations for hypervisors that call back into some firmware
in order to send the characters, but I expect those to do the busy
looping in the firmware.

Perhaps the most prominent case that I came across and that is quite
similar to this discussion is netconsole. There's a lot of code in the
network layer that exists only to allow using a network device for
outputting a console. I don't think the changes to the mailbox
framework are anywhere near as complicated.

Thierry
Greg Kroah-Hartman Dec. 7, 2018, 3:39 p.m. UTC | #17
On Fri, Dec 07, 2018 at 12:32:45PM +0100, Thierry Reding wrote:
> Greg,
> 
> any ideas on how we can move forward here? For reasons given elsewhere
> in this thread I understand that there is no way to make the console
> code run in non-atomic context. Have you ever run into a case where the
> console driver couldn't busy-loop? Were there any solutions to this?

I don't know of any such cases, hardware sucks at times, and we just
have to deal with getting it to work by doing stuff like this :(

> I've looked through quite a few drivers and they all end up with a busy
> loop, waiting for the transmission FIFO to become empty. There are also
> a few implementations for hypervisors that call back into some firmware
> in order to send the characters, but I expect those to do the busy
> looping in the firmware.

busy loops are ok here, as you point out.

> Perhaps the most prominent case that I came across and that is quite
> similar to this discussion is netconsole. There's a lot of code in the
> network layer that exists only to allow using a network device for
> outputting a console. I don't think the changes to the mailbox
> framework are anywhere near as complicated.

Neither do I, I have no objection to your changes at all.

thanks,

greg k-h
Jassi Brar Dec. 8, 2018, 5:51 a.m. UTC | #18
On Fri, Dec 7, 2018 at 11:50 AM Mikko Perttunen <cyndis@kapsi.fi> wrote:
>
> On 07/12/2018 11.26, Jassi Brar wrote:
> >> I thought that I could also mitigate 2) by busy looping in the TCU driver,
> >> but it turns out that that doesn't work. The reason is that since we are
> >> in atomic context with all interrupts disabled, the mailbox won't ever
> >> consume any new characters, so the read pointer in the circular buffer
> >> won't increment, leaving me with no condition upon which to loop that
> >> would work.
> >>
> > So you want to be able to rely on an emulated console (running on a
> > totally different subsystem) to dump development-phase early-boot
> > logs? At the cost of legitimizing busy looping in atomic context - one
> > random driver messing up the api for ever. Maybe you could have the
> > ring buffer in some shmem and only pass the number of valid characters
> > in it, to the remote?
> >
>
> I would like to note that this is the one and only console interface
> that exists on these systems, for both development phase and production.
> Other UARTs are not externally accessible on the systems, or they are
> comparatively difficult to access as they aren't intended to be used as
> consoles in the system design.

> The combination of hardware and firmware
> implementing the console is black box from software's point of view
> (similarly to any other UART HW). The interface has also been fixed at
> an early system design phase, as there are many operating systems
> running on these boards, each with their own drivers.
>
That is the saddest part - someone, who writes test cases for the h/w
team and with almost no knowledge of how OSes work, decides how the
firmware is going to work and calls it done. Then the Linux is left to
deal with the mess as we "can't do anything about it".
Jassi Brar Dec. 8, 2018, 6:09 a.m. UTC | #19
On Fri, Dec 7, 2018 at 5:02 PM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Thu, Dec 06, 2018 at 11:56:25PM -0600, Jassi Brar wrote:
> > On Thu, Nov 29, 2018 at 9:23 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > >
> > > On Wed, Nov 28, 2018 at 11:23:36PM -0600, Jassi Brar wrote:
> > > > On Wed, Nov 28, 2018 at 3:43 AM Jon Hunter <jonathanh@nvidia.com> wrote:
> > > > >
> > > > >
> > > > > On 12/11/2018 15:18, Thierry Reding wrote:
> > > > > > From: Thierry Reding <treding@nvidia.com>
> > > > > >
> > > > > > The mailbox framework supports blocking transfers via completions for
> > > > > > clients that can sleep. In order to support blocking transfers in cases
> > > > > > where the transmission is not permitted to sleep, add a new ->flush()
> > > > > > callback that controller drivers can implement to busy loop until the
> > > > > > transmission has been completed. This will automatically be called when
> > > > > > available and interrupts are disabled for clients that request blocking
> > > > > > transfers.
> > > > > >
> > > > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > > > ---
> > > > > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > > > > >  include/linux/mailbox_controller.h | 4 ++++
> > > > > >  2 files changed, 12 insertions(+)
> > > > > >
> > > > > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > > > > index 674b35f402f5..0eaf21259874 100644
> > > > > > --- a/drivers/mailbox/mailbox.c
> > > > > > +++ b/drivers/mailbox/mailbox.c
> > > > > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > > > > >               unsigned long wait;
> > > > > >               int ret;
> > > > > >
> > > > > > +             if (irqs_disabled() && chan->mbox->ops->flush) {
> > > > > > +                     ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > > > > +                     if (ret < 0)
> > > > > > +                             tx_tick(chan, ret);
> > > > > > +
> > > > > > +                     return ret;
> > > > > > +             }
> > > > >
> > > > > It seems to me that if mbox_send_message() is called from an atomic
> > > > > context AND tx_block is true, then if 'flush' is not populated this
> > > > > should be an error condition as we do not wish to call
> > > > > wait_for_completion from an atomic context.
> > > > >
> > > > > I understand that there is some debate about adding such flush support,
> > > > > but irrespective of the above change, it seems to me that if the
> > > > > mbox_send_message() can be called from an atomic context (which it
> > > > > appears to), then it should be detecting if someone is trying to do so
> > > > > with 'tx_block' set as this should be an error.
> > > > >
> > > > Layers within kernel space have to trust each other. A badly written
> > > > client can break the consumer in so many ways, we can not catch every
> > > > possibility.
> > > >
> > > > > Furthermore, if the underlying mailbox driver can support sending a
> > > > > message from an atomic context and busy wait until it is done, surely
> > > > > the mailbox framework should provide a means to support this?
> > > > >
> > > > Being able to put the message on bus in atomic context is a feature -
> > > > which we do support. But busy-waiting in a loop is not a feature, and
> > > > we don't want to encourage that.
> > >
> > > I agree that in generally busy-waiting is a bad idea and shouldn't be
> > > encouraged. However, I also think that an exception proves the rule. If
> > > you look at the console drivers in drivers/tty/serial, all of them will
> > > busy loop prior to or after sending a character. This is pretty much
> > > part of the API and as such busy-looping is very much a feature.
> > >
> > > The reason why this is done is because on one hand we have an atomic
> > > context and on the other hand we want to make sure that all characters
> > > actually make it to the console when we print them.
> > >
> > > As an example how this can break, I've taken your suggestion to
> > > implement a producer/consumer mode in the TCU driver where the console
> > > write function will just stash characters into a circular buffer and a
> > > work queue will then use mbox_send_message() to drain the circular
> > > buffer. While this does work on the surface, I was able to concern both
> > > of the issues that I was concerned about: 1) it is easy to overflow the
> > > circular buffer by just dumping enough data at once to the console and
> > > 2) when a crash happens, everything in the kernel stops, including the
> > > consumer workqueue that is supposed to drain the circular buffer and
> > > flush messages to the TCU. The result is that, in my particular case,
> > > the boot log will just stop at a random place in the middle of messages
> > > from much earlier in the boot because the TCU hasn't caught up yet and
> > > there's a lot of characters still in the circular buffer.
> > >
> > > Now, 1) can be mitigated by increasing the circular buffer size. A value
> > > that seems to give reliably good results in 2 << CONFIG_LOG_BUF_SHIFT.
> > >
> > Yes please.
>
> As I explained elsewhere, I actually went and implemented this. But
> given the nature of buffering, this ends up making the TCU completely
> useless as a console because in case of a crash, the system will stop
> working with a large number of characters still stuck in the buffer.
> And that's especially bad in case of a crash because those last
> characters that get stuck in the buffer are the most relevant ones
> because they contain the stack dump.
>
I don't question the utility of TCU. I just wonder if mailbox api
should provide a backdoor for atomic busy-wait in order to support a
sub-optimal hw+fw design.

> > > I thought that I could also mitigate 2) by busy looping in the TCU driver,
> > > but it turns out that that doesn't work. The reason is that since we are
> > > in atomic context with all interrupts disabled, the mailbox won't ever
> > > consume any new characters, so the read pointer in the circular buffer
> > > won't increment, leaving me with no condition upon which to loop that
> > > would work.
> > >
> > So you want to be able to rely on an emulated console (running on a
> > totally different subsystem) to dump development-phase early-boot
> > logs? At the cost of legitimizing busy looping in atomic context - one
> > random driver messing up the api for ever. Maybe you could have the
> > ring buffer in some shmem and only pass the number of valid characters
> > in it, to the remote?
>
> First of all, this is not about development-phase early-boot messages.
> We're talking about the system console here. This is what everyone will
> want to be using when developing on this device. Sure at some point you
> may end up with a system that works and you can have the console on the
> network or an attached display or whatever, but even then you may still
> want to attach to the console if you ever run into issues where the
> system doesn't come up.
>
> Secondly, I don't understand why you think this is an emulated console.
>
It is emulated/virtual because Linux doesn't put characters on a
physical bus. The data is simply handed forward to a remote entity.

> Lastly, I don't understand why you think we're messing up the API here.
> The proposal in this series doesn't even change any of the API, but it
> makes it aware of the state of interrupts internally so that it can do
> the right thing depending on the call stack. The other proposal, in v3,
> is more explicit in that it adds new API to flush the mailbox. The new
> API is completely optional and I even offered to document it as being
> discouraged because it involves busy looping. At the same time it does
> solve a real problem and it doesn't impact any existing mailbox drivers
> nor any of their users (because it is completely opt-in).
>
The 'flush' api is going to have no use other than implement
busy-waits. I am afraid people are simply going to take it easy and
copy the busy-waits from the test/verification codes.
"discouraging" seldom works because the developer comes with the
failproof excuse "the h/w doesn't provide any other way". Frankly I
would like to push back on such designs.

 Anyways, let us add the new 'flush' api.

Thanks.
Greg Kroah-Hartman Dec. 8, 2018, 8:50 a.m. UTC | #20
On Sat, Dec 08, 2018 at 11:21:41AM +0530, Jassi Brar wrote:
> On Fri, Dec 7, 2018 at 11:50 AM Mikko Perttunen <cyndis@kapsi.fi> wrote:
> >
> > On 07/12/2018 11.26, Jassi Brar wrote:
> > >> I thought that I could also mitigate 2) by busy looping in the TCU driver,
> > >> but it turns out that that doesn't work. The reason is that since we are
> > >> in atomic context with all interrupts disabled, the mailbox won't ever
> > >> consume any new characters, so the read pointer in the circular buffer
> > >> won't increment, leaving me with no condition upon which to loop that
> > >> would work.
> > >>
> > > So you want to be able to rely on an emulated console (running on a
> > > totally different subsystem) to dump development-phase early-boot
> > > logs? At the cost of legitimizing busy looping in atomic context - one
> > > random driver messing up the api for ever. Maybe you could have the
> > > ring buffer in some shmem and only pass the number of valid characters
> > > in it, to the remote?
> > >
> >
> > I would like to note that this is the one and only console interface
> > that exists on these systems, for both development phase and production.
> > Other UARTs are not externally accessible on the systems, or they are
> > comparatively difficult to access as they aren't intended to be used as
> > consoles in the system design.
> 
> > The combination of hardware and firmware
> > implementing the console is black box from software's point of view
> > (similarly to any other UART HW). The interface has also been fixed at
> > an early system design phase, as there are many operating systems
> > running on these boards, each with their own drivers.
> >
> That is the saddest part - someone, who writes test cases for the h/w
> team and with almost no knowledge of how OSes work, decides how the
> firmware is going to work and calls it done. Then the Linux is left to
> deal with the mess as we "can't do anything about it".

That is totally normal, and is how hardware has been almost _always_
been designed and implemented.  Nothing new here at all, it's just the
life us kernel developers get used to very quickly as it is our job to
make that hardware work and appear to userspace as something sane and
universal.

Sorry,

greg k-h
Jassi Brar Dec. 9, 2018, 1:20 a.m. UTC | #21
On Sat, Dec 8, 2018 at 2:50 AM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Sat, Dec 08, 2018 at 11:21:41AM +0530, Jassi Brar wrote:
> > On Fri, Dec 7, 2018 at 11:50 AM Mikko Perttunen <cyndis@kapsi.fi> wrote:
> > >
> > > On 07/12/2018 11.26, Jassi Brar wrote:
> > > >> I thought that I could also mitigate 2) by busy looping in the TCU driver,
> > > >> but it turns out that that doesn't work. The reason is that since we are
> > > >> in atomic context with all interrupts disabled, the mailbox won't ever
> > > >> consume any new characters, so the read pointer in the circular buffer
> > > >> won't increment, leaving me with no condition upon which to loop that
> > > >> would work.
> > > >>
> > > > So you want to be able to rely on an emulated console (running on a
> > > > totally different subsystem) to dump development-phase early-boot
> > > > logs? At the cost of legitimizing busy looping in atomic context - one
> > > > random driver messing up the api for ever. Maybe you could have the
> > > > ring buffer in some shmem and only pass the number of valid characters
> > > > in it, to the remote?
> > > >
> > >
> > > I would like to note that this is the one and only console interface
> > > that exists on these systems, for both development phase and production.
> > > Other UARTs are not externally accessible on the systems, or they are
> > > comparatively difficult to access as they aren't intended to be used as
> > > consoles in the system design.
> >
> > > The combination of hardware and firmware
> > > implementing the console is black box from software's point of view
> > > (similarly to any other UART HW). The interface has also been fixed at
> > > an early system design phase, as there are many operating systems
> > > running on these boards, each with their own drivers.
> > >
> > That is the saddest part - someone, who writes test cases for the h/w
> > team and with almost no knowledge of how OSes work, decides how the
> > firmware is going to work and calls it done. Then the Linux is left to
> > deal with the mess as we "can't do anything about it".
>
> That is totally normal, and is how hardware has been almost _always_
> been designed and implemented.  Nothing new here at all, it's just the
> life us kernel developers get used to very quickly as it is our job to
> make that hardware work and appear to userspace as something sane and
> universal.
>
Hardware yes, we can't really change much after the fact.
In this case the issue arises from the firmware - TCU's mailbox
protocol implementation. Which usually is just another image loaded
onto the remote master during cold-boot, and should actually be not
that hard to fix/change. Even if other OSes (really?) have adapted, a
pushback right now will help fix atleast the next version, otherwise
the api designer will never know what s/he is doing wrong.

Anyways, thanks for chiming in. I will pull the patchset.

Thanks.
Thierry Reding Dec. 10, 2018, 9:52 a.m. UTC | #22
On Sat, Dec 08, 2018 at 11:39:05AM +0530, Jassi Brar wrote:
> On Fri, Dec 7, 2018 at 5:02 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> >
> > On Thu, Dec 06, 2018 at 11:56:25PM -0600, Jassi Brar wrote:
> > > On Thu, Nov 29, 2018 at 9:23 AM Thierry Reding <thierry.reding@gmail.com> wrote:
> > > >
> > > > On Wed, Nov 28, 2018 at 11:23:36PM -0600, Jassi Brar wrote:
> > > > > On Wed, Nov 28, 2018 at 3:43 AM Jon Hunter <jonathanh@nvidia.com> wrote:
> > > > > >
> > > > > >
> > > > > > On 12/11/2018 15:18, Thierry Reding wrote:
> > > > > > > From: Thierry Reding <treding@nvidia.com>
> > > > > > >
> > > > > > > The mailbox framework supports blocking transfers via completions for
> > > > > > > clients that can sleep. In order to support blocking transfers in cases
> > > > > > > where the transmission is not permitted to sleep, add a new ->flush()
> > > > > > > callback that controller drivers can implement to busy loop until the
> > > > > > > transmission has been completed. This will automatically be called when
> > > > > > > available and interrupts are disabled for clients that request blocking
> > > > > > > transfers.
> > > > > > >
> > > > > > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > > > > > ---
> > > > > > >  drivers/mailbox/mailbox.c          | 8 ++++++++
> > > > > > >  include/linux/mailbox_controller.h | 4 ++++
> > > > > > >  2 files changed, 12 insertions(+)
> > > > > > >
> > > > > > > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > > > > > > index 674b35f402f5..0eaf21259874 100644
> > > > > > > --- a/drivers/mailbox/mailbox.c
> > > > > > > +++ b/drivers/mailbox/mailbox.c
> > > > > > > @@ -267,6 +267,14 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > > > > > >               unsigned long wait;
> > > > > > >               int ret;
> > > > > > >
> > > > > > > +             if (irqs_disabled() && chan->mbox->ops->flush) {
> > > > > > > +                     ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
> > > > > > > +                     if (ret < 0)
> > > > > > > +                             tx_tick(chan, ret);
> > > > > > > +
> > > > > > > +                     return ret;
> > > > > > > +             }
> > > > > >
> > > > > > It seems to me that if mbox_send_message() is called from an atomic
> > > > > > context AND tx_block is true, then if 'flush' is not populated this
> > > > > > should be an error condition as we do not wish to call
> > > > > > wait_for_completion from an atomic context.
> > > > > >
> > > > > > I understand that there is some debate about adding such flush support,
> > > > > > but irrespective of the above change, it seems to me that if the
> > > > > > mbox_send_message() can be called from an atomic context (which it
> > > > > > appears to), then it should be detecting if someone is trying to do so
> > > > > > with 'tx_block' set as this should be an error.
> > > > > >
> > > > > Layers within kernel space have to trust each other. A badly written
> > > > > client can break the consumer in so many ways, we can not catch every
> > > > > possibility.
> > > > >
> > > > > > Furthermore, if the underlying mailbox driver can support sending a
> > > > > > message from an atomic context and busy wait until it is done, surely
> > > > > > the mailbox framework should provide a means to support this?
> > > > > >
> > > > > Being able to put the message on bus in atomic context is a feature -
> > > > > which we do support. But busy-waiting in a loop is not a feature, and
> > > > > we don't want to encourage that.
> > > >
> > > > I agree that in generally busy-waiting is a bad idea and shouldn't be
> > > > encouraged. However, I also think that an exception proves the rule. If
> > > > you look at the console drivers in drivers/tty/serial, all of them will
> > > > busy loop prior to or after sending a character. This is pretty much
> > > > part of the API and as such busy-looping is very much a feature.
> > > >
> > > > The reason why this is done is because on one hand we have an atomic
> > > > context and on the other hand we want to make sure that all characters
> > > > actually make it to the console when we print them.
> > > >
> > > > As an example how this can break, I've taken your suggestion to
> > > > implement a producer/consumer mode in the TCU driver where the console
> > > > write function will just stash characters into a circular buffer and a
> > > > work queue will then use mbox_send_message() to drain the circular
> > > > buffer. While this does work on the surface, I was able to concern both
> > > > of the issues that I was concerned about: 1) it is easy to overflow the
> > > > circular buffer by just dumping enough data at once to the console and
> > > > 2) when a crash happens, everything in the kernel stops, including the
> > > > consumer workqueue that is supposed to drain the circular buffer and
> > > > flush messages to the TCU. The result is that, in my particular case,
> > > > the boot log will just stop at a random place in the middle of messages
> > > > from much earlier in the boot because the TCU hasn't caught up yet and
> > > > there's a lot of characters still in the circular buffer.
> > > >
> > > > Now, 1) can be mitigated by increasing the circular buffer size. A value
> > > > that seems to give reliably good results in 2 << CONFIG_LOG_BUF_SHIFT.
> > > >
> > > Yes please.
> >
> > As I explained elsewhere, I actually went and implemented this. But
> > given the nature of buffering, this ends up making the TCU completely
> > useless as a console because in case of a crash, the system will stop
> > working with a large number of characters still stuck in the buffer.
> > And that's especially bad in case of a crash because those last
> > characters that get stuck in the buffer are the most relevant ones
> > because they contain the stack dump.
> >
> I don't question the utility of TCU. I just wonder if mailbox api
> should provide a backdoor for atomic busy-wait in order to support a
> sub-optimal hw+fw design.
> 
> > > > I thought that I could also mitigate 2) by busy looping in the TCU driver,
> > > > but it turns out that that doesn't work. The reason is that since we are
> > > > in atomic context with all interrupts disabled, the mailbox won't ever
> > > > consume any new characters, so the read pointer in the circular buffer
> > > > won't increment, leaving me with no condition upon which to loop that
> > > > would work.
> > > >
> > > So you want to be able to rely on an emulated console (running on a
> > > totally different subsystem) to dump development-phase early-boot
> > > logs? At the cost of legitimizing busy looping in atomic context - one
> > > random driver messing up the api for ever. Maybe you could have the
> > > ring buffer in some shmem and only pass the number of valid characters
> > > in it, to the remote?
> >
> > First of all, this is not about development-phase early-boot messages.
> > We're talking about the system console here. This is what everyone will
> > want to be using when developing on this device. Sure at some point you
> > may end up with a system that works and you can have the console on the
> > network or an attached display or whatever, but even then you may still
> > want to attach to the console if you ever run into issues where the
> > system doesn't come up.
> >
> > Secondly, I don't understand why you think this is an emulated console.
> >
> It is emulated/virtual because Linux doesn't put characters on a
> physical bus. The data is simply handed forward to a remote entity.

Okay, I understand. However, from a kernel point of view there's no
difference between the TCU and any physical UART. Once the data is
handed to the TCU TX mailbox, it's out of control of the TCU driver.
Whether there's a real UART behind that or some remote processor that
reads the data isn't really relevant.

> > Lastly, I don't understand why you think we're messing up the API here.
> > The proposal in this series doesn't even change any of the API, but it
> > makes it aware of the state of interrupts internally so that it can do
> > the right thing depending on the call stack. The other proposal, in v3,
> > is more explicit in that it adds new API to flush the mailbox. The new
> > API is completely optional and I even offered to document it as being
> > discouraged because it involves busy looping. At the same time it does
> > solve a real problem and it doesn't impact any existing mailbox drivers
> > nor any of their users (because it is completely opt-in).
> >
> The 'flush' api is going to have no use other than implement
> busy-waits. I am afraid people are simply going to take it easy and
> copy the busy-waits from the test/verification codes.
> "discouraging" seldom works because the developer comes with the
> failproof excuse "the h/w doesn't provide any other way". Frankly I
> would like to push back on such designs.

I certainly approve of that stance.

However, I'd like to reiterate that the TCU design does in fact support
"another way". If you look at the mailbox driver implementation (in the
drivers/mailbox/tegra-hsp.c driver), you'll see that it does support an
interrupt driven mode. After you had reviewed Mikko's earliest proposal
that was indeed implementing busy looping exclusively we both set out
to properly implement interrupt support. This took quite a while to do
because of some gaps in the documentation, but we eventually got it to
work properly. And then it was discovered that it was all a waste of
effort because the console driver couldn't make use of it anyway. Well,
I should say "waste of effort" because I'm still happy that the proper
implementation exists and we can use it under the right circumstances.

So, at least in this particular case, it is not the hardware or firmware
design that's flawed or was taking any shortcuts. It's really just the
particular use-case of the console that doesn't allow us to make use of
the right implementation, which is why we have to resort to the inferior
method of busy-looping.

>  Anyways, let us add the new 'flush' api.

Great, thanks!

Thierry
Jassi Brar Dec. 10, 2018, 8:30 p.m. UTC | #23
On Mon, Dec 10, 2018 at 3:22 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> On Sat, Dec 08, 2018 at 11:39:05AM +0530, Jassi Brar wrote:
> > On Fri, Dec 7, 2018 at 5:02 PM Thierry Reding <thierry.reding@gmail.com> wrote:

> > >
> > >
> > > Secondly, I don't understand why you think this is an emulated console.
> > >
> > It is emulated/virtual because Linux doesn't put characters on a
> > physical bus. The data is simply handed forward to a remote entity.
>
> Okay, I understand. However, from a kernel point of view there's no
> difference between the TCU and any physical UART. Once the data is
> handed to the TCU TX mailbox, it's out of control of the TCU driver.
> Whether there's a real UART behind that or some remote processor that
> reads the data isn't really relevant.
>
That way there can be no virtual/emulated device ?

> >
> > The 'flush' api is going to have no use other than implement
> > busy-waits. I am afraid people are simply going to take it easy and
> > copy the busy-waits from the test/verification codes.
> > "discouraging" seldom works because the developer comes with the
> > failproof excuse "the h/w doesn't provide any other way". Frankly I
> > would like to push back on such designs.
>
> I certainly approve of that stance.
>
> However, I'd like to reiterate that the TCU design does in fact support
> "another way". If you look at the mailbox driver implementation (in the
> drivers/mailbox/tegra-hsp.c driver), you'll see that it does support an
> interrupt driven mode. After you had reviewed Mikko's earliest proposal
> that was indeed implementing busy looping exclusively we both set out
> to properly implement interrupt support. This took quite a while to do
> because of some gaps in the documentation, but we eventually got it to
> work properly. And then it was discovered that it was all a waste of
> effort because the console driver couldn't make use of it anyway. Well,
> I should say "waste of effort" because I'm still happy that the proper
> implementation exists and we can use it under the right circumstances.
>
> So, at least in this particular case, it is not the hardware or firmware
> design that's flawed or was taking any shortcuts. It's really just the
> particular use-case of the console that doesn't allow us to make use of
> the right implementation, which is why we have to resort to the inferior
> method of busy-looping.
>
I am not complaining about the hardware, but the firmware.
It is essential we dump logs during early boot but the platform(Linux)
doesn't have access to serial port. All the firmware allows is 24bits
per transfer!! We could do better.
A smarter option could have been Linux simply placing characters in
the shmem ring-buffer, while the remote consuming (and then poisoning)
the ring buffer upon 'hint' from Linux.

-Jassi
Thierry Reding Dec. 10, 2018, 8:45 p.m. UTC | #24
On Tue, Dec 11, 2018 at 02:00:48AM +0530, Jassi Brar wrote:
> On Mon, Dec 10, 2018 at 3:22 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> > On Sat, Dec 08, 2018 at 11:39:05AM +0530, Jassi Brar wrote:
> > > On Fri, Dec 7, 2018 at 5:02 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > > >
> > > >
> > > > Secondly, I don't understand why you think this is an emulated console.
> > > >
> > > It is emulated/virtual because Linux doesn't put characters on a
> > > physical bus. The data is simply handed forward to a remote entity.
> >
> > Okay, I understand. However, from a kernel point of view there's no
> > difference between the TCU and any physical UART. Once the data is
> > handed to the TCU TX mailbox, it's out of control of the TCU driver.
> > Whether there's a real UART behind that or some remote processor that
> > reads the data isn't really relevant.
> >
> That way there can be no virtual/emulated device ?

Virtual/emulated I interpret as purely implemented in software. That is
within the Linux kernel. Once you leave the domain of the kernel it's
no longer under the complete control of the kernel. So where exactly it
goes doesn't matter, it's pretty much the same as a hardware device from
the kernel's perspective.

But I guess you could define this differently.

> > >
> > > The 'flush' api is going to have no use other than implement
> > > busy-waits. I am afraid people are simply going to take it easy and
> > > copy the busy-waits from the test/verification codes.
> > > "discouraging" seldom works because the developer comes with the
> > > failproof excuse "the h/w doesn't provide any other way". Frankly I
> > > would like to push back on such designs.
> >
> > I certainly approve of that stance.
> >
> > However, I'd like to reiterate that the TCU design does in fact support
> > "another way". If you look at the mailbox driver implementation (in the
> > drivers/mailbox/tegra-hsp.c driver), you'll see that it does support an
> > interrupt driven mode. After you had reviewed Mikko's earliest proposal
> > that was indeed implementing busy looping exclusively we both set out
> > to properly implement interrupt support. This took quite a while to do
> > because of some gaps in the documentation, but we eventually got it to
> > work properly. And then it was discovered that it was all a waste of
> > effort because the console driver couldn't make use of it anyway. Well,
> > I should say "waste of effort" because I'm still happy that the proper
> > implementation exists and we can use it under the right circumstances.
> >
> > So, at least in this particular case, it is not the hardware or firmware
> > design that's flawed or was taking any shortcuts. It's really just the
> > particular use-case of the console that doesn't allow us to make use of
> > the right implementation, which is why we have to resort to the inferior
> > method of busy-looping.
> >
> I am not complaining about the hardware, but the firmware.
> It is essential we dump logs during early boot but the platform(Linux)
> doesn't have access to serial port. All the firmware allows is 24bits
> per transfer!! We could do better.

Hardware UARTs don't usually have much more FIFO space than that either.

> A smarter option could have been Linux simply placing characters in
> the shmem ring-buffer, while the remote consuming (and then poisoning)
> the ring buffer upon 'hint' from Linux.

I don't think that would've been much smarter, especially not in this
case. As we discussed earlier, no matter how large you make the ring-
buffer you can always run into situations where you overflow it. The
ring-buffer implementation is also a lot more complicated and error-
prone. Plus there is the fact that in this particular case we actually
don't want buffering because the buffer may hide important information
in case of a crash.

Thierry
Jassi Brar Dec. 10, 2018, 9:32 p.m. UTC | #25
On Tue, Dec 11, 2018 at 2:15 AM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Tue, Dec 11, 2018 at 02:00:48AM +0530, Jassi Brar wrote:
> > On Mon, Dec 10, 2018 at 3:22 PM Thierry Reding <thierry.reding@gmail.com> wrote:

> > > >
> > > > The 'flush' api is going to have no use other than implement
> > > > busy-waits. I am afraid people are simply going to take it easy and
> > > > copy the busy-waits from the test/verification codes.
> > > > "discouraging" seldom works because the developer comes with the
> > > > failproof excuse "the h/w doesn't provide any other way". Frankly I
> > > > would like to push back on such designs.
> > >
> > > I certainly approve of that stance.
> > >
> > > However, I'd like to reiterate that the TCU design does in fact support
> > > "another way". If you look at the mailbox driver implementation (in the
> > > drivers/mailbox/tegra-hsp.c driver), you'll see that it does support an
> > > interrupt driven mode. After you had reviewed Mikko's earliest proposal
> > > that was indeed implementing busy looping exclusively we both set out
> > > to properly implement interrupt support. This took quite a while to do
> > > because of some gaps in the documentation, but we eventually got it to
> > > work properly. And then it was discovered that it was all a waste of
> > > effort because the console driver couldn't make use of it anyway. Well,
> > > I should say "waste of effort" because I'm still happy that the proper
> > > implementation exists and we can use it under the right circumstances.
> > >
> > > So, at least in this particular case, it is not the hardware or firmware
> > > design that's flawed or was taking any shortcuts. It's really just the
> > > particular use-case of the console that doesn't allow us to make use of
> > > the right implementation, which is why we have to resort to the inferior
> > > method of busy-looping.
> > >
> > I am not complaining about the hardware, but the firmware.
> > It is essential we dump logs during early boot but the platform(Linux)
> > doesn't have access to serial port. All the firmware allows is 24bits
> > per transfer!! We could do better.
>
> Hardware UARTs don't usually have much more FIFO space than that either.
>
> > A smarter option could have been Linux simply placing characters in
> > the shmem ring-buffer, while the remote consuming (and then poisoning)
> > the ring buffer upon 'hint' from Linux.
>
> I don't think that would've been much smarter, especially not in this
> case. As we discussed earlier, no matter how large you make the ring-
> buffer you can always run into situations where you overflow it. The
> ring-buffer implementation is also a lot more complicated and error-
> prone.
>
Please think about it again.
The ring buffer becomes the effective h/w fifo. And you don't have to
wait at all for the mailbox register to clear .... you could simply
overwrite it when Linux puts some data in the ring-buffer (the data
written will just be a 'hint' command for remote to go look into the
ring-buffer for new data).

> Plus there is the fact that in this particular case we actually
> don't want buffering because the buffer may hide important information
> in case of a crash.
>
Even if the Linux crashes, whatever data is placed in the ring-buffer
will eventually be printed by the still-alive remote.

Anyways once we have the flush api, I don't really care how broken your f/w is.
diff mbox series

Patch

diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 674b35f402f5..0eaf21259874 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -267,6 +267,14 @@  int mbox_send_message(struct mbox_chan *chan, void *mssg)
 		unsigned long wait;
 		int ret;
 
+		if (irqs_disabled() && chan->mbox->ops->flush) {
+			ret = chan->mbox->ops->flush(chan, chan->cl->tx_tout);
+			if (ret < 0)
+				tx_tick(chan, ret);
+
+			return ret;
+		}
+
 		if (!chan->cl->tx_tout) /* wait forever */
 			wait = msecs_to_jiffies(3600000);
 		else
diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
index 74deadb42d76..2a07d93f781a 100644
--- a/include/linux/mailbox_controller.h
+++ b/include/linux/mailbox_controller.h
@@ -24,6 +24,9 @@  struct mbox_chan;
  *		transmission of data is reported by the controller via
  *		mbox_chan_txdone (if it has some TX ACK irq). It must not
  *		sleep.
+ * @flush:	Called when a client requests transmissions to be blocking but
+ *		the context doesn't allow sleeping. Typically the controller
+ *		will implement a busy loop waiting for the data to flush out.
  * @startup:	Called when a client requests the chan. The controller
  *		could ask clients for additional parameters of communication
  *		to be provided via client's chan_data. This call may
@@ -46,6 +49,7 @@  struct mbox_chan;
  */
 struct mbox_chan_ops {
 	int (*send_data)(struct mbox_chan *chan, void *data);
+	int (*flush)(struct mbox_chan *chan, unsigned long timeout);
 	int (*startup)(struct mbox_chan *chan);
 	void (*shutdown)(struct mbox_chan *chan);
 	bool (*last_tx_done)(struct mbox_chan *chan);