From: Marek Vasut <marex@denx.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH 2/4] net: fec_mxc: allow use with cache enabled
Date: Mon, 5 Mar 2012 02:49:33 +0100 [thread overview]
Message-ID: <201203050249.33765.marex@denx.de> (raw)
In-Reply-To: <4F53F737.4040509@boundarydevices.com>
> On 03/02/2012 04:39 PM, Marek Vasut wrote:
> >> ensure that transmit and receive buffers are cache-line aligned
> >>
> >> invalidate cache after each packet received
> >> flush cache before transmitting
> >>
> >> Signed-off-by: Eric Nelson<eric.nelson@boundarydevices.com>
> >>
> >> ---
> >>
> >> drivers/net/fec_mxc.c | 248
> >>
> >> ++++++++++++++++++++++++++++++++++++------------- drivers/net/fec_mxc.h
> >> |
> >>
> >> 19 +----
> >> 2 files changed, 184 insertions(+), 83 deletions(-)
> >>
> >> diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c
> >> index 1fdd071..f72304b 100644
> >> --- a/drivers/net/fec_mxc.c
> >> +++ b/drivers/net/fec_mxc.c
> >>
> >> <snip>
> >>
> >> /*
> >>
> >> * This routine transmits one frame. This routine only accepts
> >>
> >> @@ -650,15 +708,21 @@ static int fec_send(struct eth_device *dev,
> >> volatile void* packet, int length) }
> >>
> >> /*
> >>
> >> - * Setup the transmit buffer
> >> - * Note: We are always using the first buffer for transmission,
> >> - * the second will be empty and only used to stop the DMA engine
> >> + * Setup the transmit buffer. We are always using the first buffer for
> >> + * transmission, the second will be empty and only used to stop the
> >> DMA + * engine. We also flush the packet to RAM here to avoid cache
> >> trouble.
> >>
> >> */
> >>
> >> #ifdef CONFIG_FEC_MXC_SWAP_PACKET
> >>
> >> swap_packet((uint32_t *)packet, length);
> >>
> >> #endif
> >>
> >> - writew(length,&fec->tbd_base[fec->tbd_index].data_length);
> >> - writel((uint32_t)packet,&fec->tbd_base[fec->tbd_index].data_pointer);
> >> +
> >> + addr = (uint32_t)packet;
> >> + size = roundup(length, CONFIG_FEC_DATA_ALIGNMENT);
> >> + flush_dcache_range(addr, addr + size);
> >
> > What if size of the packet isn't aligned on cacheline boundary? Won't it
> > choke here?
>
> Here's a quandary...
>
> flush_dcache_range() can potentially force writes into unintended areas,
> which could conceivably include areas owned by the ethernet receiver if
> the source object weren't aligned to a cache-line boundary and size.
>
> In practice, it appears that net/net.c only transmits from one of two
> places:
You can also invalidate memory and loose some information. Though I'm not quite
sure this driver can be the case.
> - a dedicated transmit buffer (NetTxPacket), which is guaranteed
> to be aligned to PKTALIGN (32).
> - a receive buffer (ICMP and ARP replies)
>
> The networking API certainly allows for transmits from arbitrary
> areas in memory, but I'm not seeing where or how this can be invoked.
OK
>
> Because there's no way to query how the memory for a packet is
> allocated, the only way around this I can see is to memcpy to
> a dedicated transmit buffer within fec_mxc.c, which would defeat
> any benefit of cache.
Indeed ... a bounce buffer. But such a bounce buffer should be implemented close
to the place where the misalignment originates. If the net core can align
packets everywhere well, we're safe and happy.
>
> AFAICT, all of the other calls to 'flush_dcache_range()' are okay because
> they're dealing with buffers allocated by the driver.
>
I'd love to see someone else review this. This is very important to be made
right.
> >> +
> >> + fec->tbd_base[fec->tbd_index].data_length = length;
> >> + fec->tbd_base[fec->tbd_index].data_pointer = addr;
> >> +
> >>
> >> /*
> >>
> >> * update BD's status now
> >>
> >> * This block:
> >> @@ -667,9 +731,18 @@ static int fec_send(struct eth_device *dev,
> >> volatile void* packet, int length) * - might be the last BD in the
> >> list, so the address counter should * wrap (-> keep the WRAP flag)
> >>
> >> */
> >>
> >> - status = readw(&fec->tbd_base[fec->tbd_index].status)& FEC_TBD_WRAP;
> >> + status = fec->tbd_base[fec->tbd_index].status& FEC_TBD_WRAP;
> >>
> >> status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY;
> >>
> >> - writew(status,&fec->tbd_base[fec->tbd_index].status);
> >> + fec->tbd_base[fec->tbd_index].status = status;
> >> +
> >> + /*
> >> + * Flush data cache. This code flushes both TX descriptors to RAM.
> >> + * After this code this code, the descritors will be safely in RAM
> >> + * and we can start DMA.
> >> + */
> >> + size = roundup(2 * sizeof(struct fec_bd), CONFIG_FEC_DATA_ALIGNMENT);
> >> + addr = (uint32_t)fec->tbd_base;
> >> + flush_dcache_range(addr, addr + size);
> >
> > Same concern here and everywhere else ... I believe aligning it like this
> > is quite unsafe :-(
>
> This one looks safe because you've controlled the allocation of tbd_base.
>
> >> <snip>
> >>
> >> @@ -751,6 +846,13 @@ static int fec_recv(struct eth_device *dev)
> >>
> >> frame = (struct nbuf *)readl(&rbd->data_pointer);
> >> frame_length = readw(&rbd->data_length) - 4;
> >> /*
> >>
> >> + * Invalidate data cache over the buffer
> >> + */
> >> + addr = (uint32_t)frame;
> >> + size = roundup(frame_length, CONFIG_FEC_DATA_ALIGNMENT);
> >> + invalidate_dcache_range(addr, addr + size);
> >
> > DTTO here, frame length might not be aligned properly, or will it be?
> > Network stack must be properly analyzed here.
>
> The hardware won't return an unaligned value here, so this should be good.
Are you sure? You can't receive frame aligned to 8 bytes boundary?
>
> >> <snip>
> >>
> >> @@ -765,11 +867,27 @@ static int fec_recv(struct eth_device *dev)
> >>
> >> (ulong)rbd->data_pointer,
> >> bd_status);
> >>
> >> }
> >>
> >> +
> >>
> >> /*
> >>
> >> - * free the current buffer, restart the engine
> >> - * and move forward to the next buffer
> >> + * Free the current buffer, restart the engine and move forward
> >> + * to the next buffer. Here we check if the whole cacheline of
> >> + * descriptors was already processed and if so, we mark it free
> >> + * as whole.
> >>
> >> */
> >>
> >> - fec_rbd_clean(fec->rbd_index == (FEC_RBD_NUM - 1) ? 1 : 0, rbd);
> >> + size = (CONFIG_FEC_DATA_ALIGNMENT / sizeof(struct fec_bd)) - 1;
> >> + if ((fec->rbd_index& size) == size) {
> >> + i = fec->rbd_index - size;
> >> + addr = (uint32_t)&fec->rbd_base[i];
> >> + for (; i<= fec->rbd_index + size; i++) {
> >> + if (i == (FEC_RBD_NUM - 1))
> >> + fec_rbd_clean(1,&fec->rbd_base[i]);
> >> + else
> >> + fec_rbd_clean(0,&fec->rbd_base[i]);
> >> + }
> >> + flush_dcache_range(addr,
> >> + addr + CONFIG_FEC_DATA_ALIGNMENT);
> >> + }
> >> +
> >
> > This was the worst part. I don't quite remember how and why I did those
> > alignment decisions (well it's obvious here, you flush rxdesc once whole
> > cacheline is done), but some of the pieces are far less obvious now that
> > I read the code.
>
> I'm not grokking this one either. Definitely needs fresher eyes than I have
> at the moment.
Indeed, I'm thinking the same thing. Let's sleep on this!
Thanks for your good work on putting this into shape!
M
next prev parent reply other threads:[~2012-03-05 1:49 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-03-02 23:06 [U-Boot] [PATCH 0/4] Add preliminary cache support to i.MX6 Eric Nelson
2012-03-02 23:06 ` [U-Boot] [PATCH 1/4] i.MX6: define CACHELINE_SIZE Eric Nelson
2012-03-02 23:25 ` Marek Vasut
2012-03-02 23:06 ` [U-Boot] [PATCH 2/4] net: fec_mxc: allow use with cache enabled Eric Nelson
2012-03-02 23:15 ` Eric Nelson
2012-03-02 23:40 ` Marek Vasut
2012-03-02 23:50 ` Eric Nelson
2012-03-02 23:39 ` Marek Vasut
2012-03-04 23:13 ` Eric Nelson
2012-03-05 1:49 ` Marek Vasut [this message]
2012-03-05 13:43 ` Eric Nelson
2012-03-05 15:39 ` Marek Vasut
2012-03-05 15:55 ` Eric Nelson
2012-03-05 17:59 ` Marek Vasut
2012-03-05 18:37 ` Eric Nelson
2012-03-02 23:41 ` Eric Nelson
2012-03-02 23:06 ` [U-Boot] [PATCH 3/4] i.MX6: implement enable_caches() Eric Nelson
2012-03-02 23:26 ` Marek Vasut
2012-03-02 23:06 ` [U-Boot] [PATCH 4/4] i.MX6: mx6qsabrelite: add cache commands if cache is enabled Eric Nelson
2012-03-02 23:26 ` Marek Vasut
2012-03-02 23:21 ` [U-Boot] [PATCH 0/4] Add preliminary cache support to i.MX6 Eric Nelson
2012-03-02 23:25 ` Marek Vasut
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201203050249.33765.marex@denx.de \
--to=marex@denx.de \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.