Re: [PATCH v2] ethernet:arc: Fix racing of TX ring buffer

Francois Romieu
2016-05-20T00:31:45Z

Lino Sanfilippo <LinoSanfilippo-Mmb7MZpHnFY@public.gmane.org> :
[...]
> 2. requires a smp_wmb, while 3. requires a rmb(). AFAIK the mb() implies all we need,
> the dma_rmb() for 1., the smp_wmb() for 2. and the rmb() for 3.

A revalidation of tx_dirty is still needed (see below) despite the rmb()
for 3. The rmb() for 3. is close to useless.

> >> 2. On multi processor systems: ensures that txbd_curr is updated (this is paired
> >> with the smp_mb() at the end of tx_clean).
> >
> > Smells like using barrier side-effects to control smp coherency. It isn't
> > the recommended style.
> >
>
> As I wrote above, the mb() implies the smp_wmb() so this is a regular pairing
> of smp_wmb() in xmit and smb_mb() in tx_clean, nothing uncommon.

Since you are quoting Documentation/memory-barriers.txt:
[...]
CPU MEMORY BARRIERS
-------------------
[...]
Mandatory barriers should not be used to control SMP effects, since mandatory
barriers impose unnecessary overhead on both SMP and UP systems.

> >> - if ((info & FOR_EMAC) || !txbd->data || !skb)
> >> + if (info & FOR_EMAC) {
> >> + /* Make sure we see consistent values for info, skb
> >> + * and data.
> >> + */
> >> + smp_rmb();
> >> break;
> >> + }
> >
> > ?
> >
> > smp_rmb should appear before the variables you want coherency for.
>
> I dont think so. Please take a look into the memory barriers documentation.

> There is an example that is pretty close to the situation that we have in this driver:
>
> http://lxr.free-electrons.com/source/Documentation/memory-barriers.txt#L1819
>
> In that example the barrier is also _between_ the variables that are to be
> ordered, not before.

Err, which barrier ?

- dma_rmb() ?
The device (obviously) set the 'status' member of the descriptor.
dma_rmb() ensures that device-initiated DMA is complete for the 'data'
member as well.

- dma_wmb() ?
It ensures that the updated 'data' member will be set before any
DMA resulting from the change of descriptor ownership takes place.

- wmb() ?
It ensures that the previous write to descriptor (coherent memory)
completes before write is posted to I/O mailbox.

None of these is "pretty close" to the "smp_rmb() before return" situation.

> >> - skb_tx_timestamp(skb);
> >> + /* Make sure info is set after data and skb with respect to
> >> + * other tx_clean().
> >> + */
> >> + smp_wmb();
> >>
> >> *info = cpu_to_le32(FOR_EMAC | FIRST_OR_LAST_MASK | len);
> >
> > Afaik smp_wmb() does not imply wmb(). So priv->txbd[*txbd_curr].data and
> > *info (aka priv->txbd[*txbd_curr].info) are not necessarily written in
> > an orderly manner.
>
> Right, as I already wrote above I changed the smp barriers to mandatory ones.
>
> >
> >>
> >> - /* Make sure info word is set */
> >> - wmb();
> >> -
> >> - priv->tx_buff[*txbd_curr].skb = skb;
> >> -
> >> /* Increment index to point to the next BD */
> >> *txbd_curr = (*txbd_curr + 1) % TX_BD_NUM;
> >
> > With this change it's possible that tx_clean() reads new value for
> > tx_curr and old value (0) for *info.
>
> Even if this could happen, what is the problem? I cant see an issue
> that results from such a scenario.

tx_clean() misunderstands the 0 in *info as a descriptor updated by the
device. tx_clean() thus kfrees the skb before the device DMAed from it.

[...]
> > Xmit thread | Clean thread
> >
> > mb();
> >
> > arc_emac_tx_avail() test with old
> > tx_dirty - tx_clean has not issued
> > any mb yet - and new tx_curr
> >
> > smp_mb();
> >
> > if (netif_queue_stopped(ndev) && ...
> > netif_wake_queue(ndev);
> >
> > netif_stop_queue()
> >
> > -> queue stopped.
> >
>
> Again, the mb() we have now implies the smb_mb() we had before. So nothing
> changed except that we can be sure to see the new value for tx_dirty at
> our first attempt.

Nothing changed except you removed the revalidation part !

The smp_mb() we had before wasn't about seeing tx_dirty in the xmit thread
but about balancing the (read) barrier in the cleaning thread so that the
latter stood a chance to see the new (tx thread updated) tx_curr.

Consider the two lines below:

if (!arc_emac_tx_avail(priv))
netif_stop_queue(ndev);

Nothing prevents a complete run of the cleaning thread between these
two lines. It may or it may not happen but there is one thing sure:
mb() before arc_emac_tx_avail() can't tell the future.

[...]
> New patch is below.

The arc_emac_tx_clean() change is wrong.

tx_drity revalidation is still needed in arc_emac_tx after netif_stop_queue.

A barrier is still missing in arc_emac_tx between descriptor release and
tx_curr increase.

--
Ueimor