public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
From: Eric Nelson <eric.nelson@boundarydevices.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH V2] net: fec_mxc: allow use with cache enabled
Date: Tue, 06 Mar 2012 14:19:19 -0700	[thread overview]
Message-ID: <4F567F57.1030901@boundarydevices.com> (raw)
In-Reply-To: <201203062045.58890.marex@denx.de>

On 03/06/2012 12:45 PM, Marek Vasut wrote:
> Dear Eric Nelson,
>> On 03/05/2012 01:06 PM, Marek Vasut wrote:
>>> Dear Eric Nelson,
>>>
>>>> 	ensure that transmit and receive buffers are cache-line aligned
>>>> 	
>>>>           invalidate cache after each packet received
>>>>           flush cache before transmitting
>>>> 	
>>>> 	Original patch by Marek:
>>>> 		http://lists.denx.de/pipermail/u-boot/2012-February/117695.html
>>>
>>> Would be cool to Cc me :-p
>>
>> Sorry about that.
>
> It's ok, don't worry about it ;-)
>
> [...]
>
>>>
>>> I think this "writel()" call is bogus and should be removed in subsequent
>>> patch and replaced with simple assignment. It was here probably due to
>>> cache issues on PPC?
>>
>> The RBD has me puzzled. Do we treat them like registers and use
>> readx/writex or like in-RAM data structures...
>
> I'd go for the in-RAM data structures, but such conversion should happen in a
> separate patch only AFTER the cache support is in.
>
> [...]
>

Sounds good.

>>>> +		if (!fec->rbd_base) {
>>>> +			ret = -ENOMEM;
>>>> +			goto err2;
>>>> +		}
>>>> +		memset(fec->rbd_base, 0, size);
>>>> +	}
>>>
>>> We might want to flush the descriptors to memory after they have been
>>> inited?
>>
>> Again, good catch.
>>
>> On this topic (initialization of RBD), I had a bit of a concern
>> regarding the initialization of things.
>>
>> In fec_open, the receive buffer descriptors are initialized and the
>> last one set is to 'wrap'. If this code were to execute when the
>> controller is live, bad things would surely happen.
>>
>> I traced through all of the paths I can see, and I believe that
>> we're safe. It appears that fec_halt() will be called prior to
>> any call to fec_init() and fec_open().
>
> Yes, this will only happen if something went wrong.
>
>>
>> In fec_open() a number of calls to fec_rbd_clean() are made and
>> a single flush_dcache() is made afterwards.
>>
>> While this works and causes less thrashing than per-RBD flushes,
>> I think the code would be simpler if fec_rbd_clean just did the
>> flush itself. This would also remove the need for a separate
>> flush in fec_recv.
>
> Not really, rbd might be (and likely is) smaller than cache line, therefore you
> won't be able to flush single rbd, unless it's cacheline aligned, which wastes
> space.
>
> [...]
>

Yeah. Please disregard my comments. I wrote that before I fully
appreciated what was being done in fec_recv().

>>>> +	invalidate_dcache_range(addr, addr + size);
>>>> +
>>>
>>> The idea here is the following (demo uses 32byte long cachelines):
>>>
>>> [DESC1][DESC2][DESC3][DESC4][DESC5][DESC6]
>>> `------- cacheline --------'
>>>
>>> We want to start retrieving contents of DESC3, therefore "addr" points to
>>> DESC1, "size" is size of cacheline (I hope there's no hardware with 8
>>> byte cachelines, but this should be ok here).
>>>
>>> NOTE[5]: Here we can invalidate the whole cacheline, because the
>>> descriptors so far were modified only be hardware, not by us. We are not
>>> writing anything there so we won't loose any information.
>>>
>>> NOTE[6]: This invalidation ensures that we always have a fresh copy of
>>> the cacheline containing all the descriptors, therefore we always have a
>>> fresh status of the descriptors we are about to pick. Since this is a
>>> sequential execution, the cache eviction should not kick in here (or
>>> might it?).
>>
>> Another way to look at this is this:
>>
>> 	After fec_open(), the hardware owns the rbd, and all we should do
>> 	is read it. In order to make sure we don't have a stale copy, we
>> 	need to call invalidate() before looking at the values.
>>
>> Tracing the code to find out whether this is true, the only write I see
>> is within fec_recv() when the last descriptor is full, when the driver
>> takes ownership of **all** of the descriptors, calling fec_rbd_clean()
>> on each.
>>
>> The only thing that looks funky is this:
>>
>> 		size = (CONFIG_FEC_ALIGN / sizeof(struct fec_bd)) - 1;
>> 		if ((fec->rbd_index&  size) == size) {
>>
>> Wouldn't a test of rbd_index against FEC_RBD_NUM be more appropriate?
>> i.e.
>> 		if (fec->rbd_index == FEC_RBD_NUM-1) {
>
> I believe the FEC doesn't always start from rbd_index == 0, and if you were to
> receive more than 64 rbds between open() and close(), this implementation works,
> your would fail.
>

Yep. Disregard that too.

<snip>

>>> The solution is the following:
>>>
>>> 1) Compute how many descriptors are per-cache line
>>> 2) Make sure FEC_RBD_NUM * sizeof(struct fec_bd) is at least 2 *
>>> CONFIG_FEC_DATA_ALIGNMENT in size, see NOTE[11].
>>> 3) Once the last RX buffer in the cacheline is processed, mark them all
>>> clean and flush them all, see NOTE[10].
>>>
>>> NOTE[10]: This is legal, because the hardware won't use RX descriptors
>>> that it marked dirty (which means not picked up by software yet). We
>>> clean the desciptors in an order the hardware would pick them up again
>>> so there's no problem with race condition either. The only possible
>>> issue here is if there was hardware with cacheline size smaller than
>>> descriptor size (we should add a check for this at the begining of the
>>> file).
>>>
>>> NOTE[11]: This is because we want the FEC to overwrite descriptors below
>>> the other cacheline while we're marking the one containing retrieved
>>> descriptors clean.
>>
>> Ahah! Now I see what the size calculation is doing.
>>
>> A well-named constant, maybe "RXDESC_PER_CACHELINE" would be useful here.
>
> Yes
>
>> 	#define RXDESC_PER_CACHELINE	(CONFIG_FEC_ALIGN/sizeof(struct fec_bd))
>>
>>>> -		fec_rbd_clean(fec->rbd_index == (FEC_RBD_NUM - 1) ? 1 : 0, rbd);
>>>> +		size = (CONFIG_FEC_DATA_ALIGNMENT / sizeof(struct fec_bd)) - 1;
>>
>> 		size = RXDESC_PER_CACHELINE-1;
>>
>>>> +		if ((fec->rbd_index&   size) == size) {
>>
>> The line above only works if RXDESC_PER_CACHELINE is a multiple of 2, which
>> is likely to work because sizeof(struct fec_bd) == 8.
>
> Adding such a comment (and maybe CPP check) won't hurt.
 >

I'm struggling getting the CPP to do the work at the moment...

>>
>>>> +			i = fec->rbd_index - size;
>>>> +			addr = (uint32_t)&fec->rbd_base[i];
>>>> +			for (; i<= fec->rbd_index + size; i++) {
>>
>> This flushes too many descriptors! This should be:
>> 			for (; i<= fec->rbd_index; i++) {
>
> Agreed
>

V3 patch forthcoming.

>>> Uh, this was tough.
>>
>> How bad do we want cache?
>
> We're almost there, why do you ask? :-)
>
I was just bein' snarky...

I'm making a couple of other small changes in V3:

- 	change fec_rbd_clean to only have a single
	call to write() and make it clearer that there's
	only one additional flag iff 'last'
-	use comparison to supply 'last' parameter in
	the call to fec_rbd_clean

I think each of these makes the intent clearer.

diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c
index d5d0d5e..f8691d4 100644
--- a/drivers/net/fec_mxc.c
+++ b/drivers/net/fec_mxc.c
@@ -355,16 +355,10 @@ static void fec_tbd_init(struct fec_priv *fec)
   */
  static void fec_rbd_clean(int last, struct fec_bd *pRbd)
  {
-       /*
-        * Reset buffer descriptor as empty
-        */
+       unsigned short flags = FEC_RBD_EMPTY;
         if (last)
-               writew(FEC_RBD_WRAP | FEC_RBD_EMPTY, &pRbd->status);
-       else
-               writew(FEC_RBD_EMPTY, &pRbd->status);
-       /*
-        * no data in it
-        */
+               flags |= FEC_RBD_WRAP;
+       writew(flags, &pRbd->status);
         writew(0, &pRbd->data_length);
  }

@@ -880,10 +874,8 @@ static int fec_recv(struct eth_device *dev)
                         i = fec->rbd_index - size;
                         addr = (uint32_t)&fec->rbd_base[i];
                         for (; i <= fec->rbd_index ; i++) {
-                               if (i == (FEC_RBD_NUM - 1))
-                                       fec_rbd_clean(1, &fec->rbd_base[i]);
-                               else
-                                       fec_rbd_clean(0, &fec->rbd_base[i]);
+                               fec_rbd_clean(i == (FEC_RBD_NUM - 1),
+                                             &fec->rbd_base[i]);
                         }
                         flush_dcache_range(addr,
                                 addr + CONFIG_FEC_ALIGN);

  reply	other threads:[~2012-03-06 21:19 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <http://lists.denx.de/pipermail/u-boot/2012-March/#119217>
2012-03-05 18:36 ` [U-Boot] [PATCH V2 0/1] net: fec_mxc: allow use with cache enabled Eric Nelson
2012-03-05 18:36   ` [U-Boot] [PATCH V2] " Eric Nelson
2012-03-05 20:06     ` Marek Vasut
2012-03-05 20:19       ` Wolfgang Denk
2012-03-05 20:23         ` Marek Vasut
2012-03-06 17:06       ` Eric Nelson
2012-03-06 18:08         ` Eric Nelson
2012-03-06 19:49           ` Marek Vasut
2012-03-06 21:21             ` Eric Nelson
2012-03-06 21:38               ` Marek Vasut
2012-03-06 19:45         ` Marek Vasut
2012-03-06 21:19           ` Eric Nelson [this message]
2012-03-06 21:22             ` Marek Vasut
2012-03-06 21:28               ` Eric Nelson

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=4F567F57.1030901@boundarydevices.com \
    --to=eric.nelson@boundarydevices.com \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox