Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v6] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Po-Yu Chuang @ 2011-03-01  5:20 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, linux-kernel, bhutchings, joe, dilinger, mirqus, davem,
	Po-Yu Chuang
In-Reply-To: <1298634040.2659.32.camel@edumazet-laptop>

Hi Eric,

On Fri, Feb 25, 2011 at 7:40 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> Le vendredi 25 février 2011 à 17:57 +0800, Po-Yu Chuang a écrit :
>> From: Po-Yu Chuang <ratbert@faraday-tech.com>
>>
>> FTMAC100 Ethernet Media Access Controller supports 10/100 Mbps and
>> MII.  This driver has been working on some ARM/NDS32 SoC's including
>> Faraday A320 and Andes AG101.
>>
>> Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
>
> It seems fine to me, but I have somes questions
>
> 1) On V5, the receive function ftmac100_rx_packet() was able to process
> several segments per skb. On V6 you process one frag only.
>
> Isnt this NIC able to handle large MTU (say... 9000) ?

No, it isn't. Its max supported packet size is 0x7ff (2047 bytes).

I wasn't sure when will get multi-segment packet. After confirmed with
HW designer two weeks ago, he told me that only if the rx buffer described
by the first descriptor is not big enough, another rx buffer will be used.

That means, since every rx buffer is large enough, it is impossible to
get a multi-segment packets.

So I simplified ftmac100_rx_packet().

>
> 2) ftmac100_alloc_rx_page() is called and allocate a full page for a
> rxdes.
>
> 128*4K -> 512 Kbytes of memory for RX ring
>
> In V5, you were using half pages only, so 256 kbytes of memory.
>
> If you look at other drivers (NIU, BENET), they are able to use exactly
> 128*2 kbytes (for a rxring of 128 slots, and 2Kbytes per slot)

Both NIU and BENET refill rx ring after all packets receive work done,
but I allocate rx buffer right after a packet is received. I think it
is difficult
to use a page for two rx buffers in my driver now. Can we just keep it
the way it is? I can study how to achieve that after current driver is accepted.

best regards,
Po-Yu Chuang

^ permalink raw reply

* Re: [PATCH v6] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Eric Dumazet @ 2011-03-01  5:26 UTC (permalink / raw)
  To: Po-Yu Chuang
  Cc: netdev, linux-kernel, bhutchings, joe, dilinger, mirqus, davem,
	Po-Yu Chuang
In-Reply-To: <AANLkTimeDR27Q7fcBmB0ix7Q_pRT_GDpc7S3LO21-fh-@mail.gmail.com>

Le mardi 01 mars 2011 à 13:20 +0800, Po-Yu Chuang a écrit :
> Hi Eric,
> 
> On Fri, Feb 25, 2011 at 7:40 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:

> Both NIU and BENET refill rx ring after all packets receive work done,
> but I allocate rx buffer right after a packet is received. I think it
> is difficult
> to use a page for two rx buffers in my driver now. Can we just keep it
> the way it is? I can study how to achieve that after current driver is accepted.
> 

No problem, you are the author, I am only a reviewer and ask questions
after all ;)

Thanks !

^ permalink raw reply

* Re: [PATCH] sched: QFQ - quick fair queue scheduler (v2)
From: Eric Dumazet @ 2011-03-01  5:28 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: David Miller, Fabio Checconi, Luigi Rizzo, netdev
In-Reply-To: <20110228171738.2cc8c9a0@nehalam>

Le lundi 28 février 2011 à 17:17 -0800, Stephen Hemminger a écrit :
> This is an implementation of the Quick Fair Queue scheduler developed
> by Fabio Checconi. The same algorithm is already implemented in ipfw
> in FreeBSD. Fabio had an earlier version developed on Linux, I just
> cleaned it up and tested it. All bugs are mine.
> 
> This version is still experimental, do not use for production.
> 
> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>

Seems very complex stuff ;)

Reviewed-by: Eric Dumazet <eric.dumazet@gmail.com>

I'll perform some tests today with QFQ, thanks !




^ permalink raw reply

* Re: [PATCH 4/5] udp: Add lockless transmit path
From: Eric Dumazet @ 2011-03-01  5:30 UTC (permalink / raw)
  To: Herbert Xu
  Cc: David Miller, rick.jones2, therbert, wsommerfeld, daniel.baluta,
	netdev, Thomas Graf
In-Reply-To: <E1Pu1TJ-0005RA-DV@gondolin.me.apana.org.au>

Le lundi 28 février 2011 à 19:41 +0800, Herbert Xu a écrit :
> udp: Add lockless transmit path
> 
> The UDP transmit path has been running under the socket lock
> for a long time because of the corking feature.  This means that
> transmitting to the same socket in multiple threads does not
> scale at all.
> 
> However, as most users don't actually use corking, the locking
> can be removed in the common case.
> 
> This patch creates a lockless fast path where corking is not used.
> 
> Please note that this does create a slight inaccuracy in the
> enforcement of socket send buffer limits.  In particular, we
> may exceed the socket limit by up to (number of CPUs) * (packet
> size) because of the way the limit is computed.
> 
> As the primary purpose of socket buffers is to indicate congestion,
> this should not be a great problem for now.
> 
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
> ---

So far I found no obvious problem, and got pretty impressive results.

Acked-by: Eric Dumazet <eric.dumazet@gmail.com>




^ permalink raw reply

* Re: [PATCH 1/5] net: Remove unused sk_sndmsg_* from UFO
From: Eric Dumazet @ 2011-03-01  5:31 UTC (permalink / raw)
  To: Herbert Xu
  Cc: David Miller, rick.jones2, therbert, wsommerfeld, daniel.baluta,
	netdev, Thomas Graf
In-Reply-To: <E1Pu1TI-0005Qa-KA@gondolin.me.apana.org.au>

Le lundi 28 février 2011 à 19:41 +0800, Herbert Xu a écrit :
> net: Remove unused sk_sndmsg_* from UFO
> 
> UFO doesn't really use the sk_sndmsg_* parameters so touching
> them is pointless.  It can't use them anyway since the whole
> point of UFO is to use the original pages without copying.
> 
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
> ---

Acked-by: Eric Dumazet <eric.dumazet@gmail.com>



^ permalink raw reply

* Re: [PATCH 2/5] net: Remove explicit write references to sk/inet in ip_append_data
From: Eric Dumazet @ 2011-03-01  5:31 UTC (permalink / raw)
  To: Herbert Xu
  Cc: David Miller, rick.jones2, therbert, wsommerfeld, daniel.baluta,
	netdev, Thomas Graf
In-Reply-To: <E1Pu1TI-0005Qj-T7@gondolin.me.apana.org.au>

Le lundi 28 février 2011 à 19:41 +0800, Herbert Xu a écrit :
> net: Remove explicit write references to sk/inet in ip_append_data
> 
> In order to allow simultaneous calls to ip_append_data on the same
> socket, it must not modify any shared state in sk or inet (other
> than those that are designed to allow that such as atomic counters).
> 
> This patch abstracts out write references to sk and inet_sk in
> ip_append_data and its friends so that we may use the underlying
> code in parallel.
> 
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
> ---
> 
>  include/net/inet_sock.h |   23 ++--
>  net/ipv4/ip_output.c    |  238 ++++++++++++++++++++++++++++--------------------
>  2 files changed, 154 insertions(+), 107 deletions(-)

Acked-by: Eric Dumazet <eric.dumazet@gmail.com>




^ permalink raw reply

* Re: [PATCH 3/5] inet: Add ip_make_skb and ip_send_skb
From: Eric Dumazet @ 2011-03-01  5:31 UTC (permalink / raw)
  To: Herbert Xu
  Cc: David Miller, rick.jones2, therbert, wsommerfeld, daniel.baluta,
	netdev, Thomas Graf
In-Reply-To: <E1Pu1TJ-0005Qw-5I@gondolin.me.apana.org.au>

Le lundi 28 février 2011 à 19:41 +0800, Herbert Xu a écrit :
> inet: Add ip_make_skb and ip_send_skb
> 
> This patch adds the helper ip_make_skb which is like ip_append_data
> and ip_push_pending_frames all rolled into one, except that it does
> not send the skb produced.  The sending part is carried out by
> ip_send_skb, which the transport protocol can call after it has
> tweaked the skb.
> 
> It is meant to be called in cases where corking is not used should
> have a one-to-one correspondence to sendmsg.
> 
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Acked-by: Eric Dumazet <eric.dumazet@gmail.com>




^ permalink raw reply

* Re: SO_REUSEPORT - can it be done in kernel?
From: Eric Dumazet @ 2011-03-01  5:33 UTC (permalink / raw)
  To: Herbert Xu
  Cc: David Miller, rick.jones2, therbert, wsommerfeld, daniel.baluta,
	netdev
In-Reply-To: <20110228113659.GA20726@gondor.apana.org.au>

Le lundi 28 février 2011 à 19:36 +0800, Herbert Xu a écrit :

> Here are the patches I used.  Please don't them yet as I intend
> to clean them up quite a bit.
> 

I assume you mean "please dont commit them" ;)

> But please do test them heavily, especially if you have an AMD
> NUMA machine as that's where scalability problems really show
> up.  Intel tends to be a lot more forgiving.  My last AMD machine
> blew up years ago :)

Same here, My only AMD machine is a desktop class machine, not a server.




^ permalink raw reply

* Re: txqueuelen has wrong units; should be time
From: Eric Dumazet @ 2011-03-01  5:36 UTC (permalink / raw)
  To: Albert Cahalan
  Cc: John Heffner, John W. Linville, Jussi Kivilinna,
	Mikael Abrahamsson, linux-kernel, netdev
In-Reply-To: <1298955683.2676.2.camel@edumazet-laptop>

Le mardi 01 mars 2011 à 06:01 +0100, Eric Dumazet a écrit :
> Le lundi 28 février 2011 à 23:11 -0500, Albert Cahalan a écrit :
> 
> > It sounds like you need a callback or similar, so that TCP can be
> > informed later that the drop has occurred.
> 
> There is the thing called skb destructor / skb_orphan() mess, that is
> not stackable... Might extend this to something more clever, and be able
> to call functions (into TCP stack for example) giving a status of skb :
> Sent, or dropped somewhere in the stack...
> 

One problem of such schem is the huge extra cost involved, extra
locking, extra memory allocations, extra atomic operations...

^ permalink raw reply

* Re: [PATCH ref0] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Po-Yu Chuang @ 2011-03-01  5:45 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David Miller, netdev, linux-kernel, bhutchings, joe, dilinger,
	mirqus, ratbert, Ajit Khaparde
In-Reply-To: <1298659663.2659.104.camel@edumazet-laptop>

Hi Eric,

On Sat, Feb 26, 2011 at 2:47 AM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> Le vendredi 25 février 2011 à 19:45 +0100, Eric Dumazet a écrit :
>> Le vendredi 25 février 2011 à 10:34 -0800, David Miller a écrit :
>> > From: Eric Dumazet <eric.dumazet@gmail.com>
>> > Date: Fri, 25 Feb 2011 11:52:07 +0100
>> >
>> > > Le vendredi 25 février 2011 à 17:45 +0800, Po-Yu Chuang a écrit :
>> > >
>> > >> It's a little faster than v5 now. Thanks.
>> > >> I will submit the current version later.
>> > >>
>> > >> One more question just curious, why 128 bytes?
>> > >
>> > > Probably its best for NIU hardware specs
>> > >
>> > > You could try 64, as it should be enough for most IP/TCP/UDP processing.
>> >
>> > IPV6.
>>
>> drivers/net/benet/be.h:70:#define BE_HDR_LEN            64
>>
>> Maybe we should have a comment somewhere.
>>
>> CC Ajit Khaparde <ajit.khaparde@emulex.com>
>>
>
>
> A compromise would be to use 128 for the allocation, but only copy 64
> bytes.

I will use this way and submit v7 later.

Thanks,
Po-Yu Chuang

^ permalink raw reply

* Re: [PATCH v6] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Eric Dumazet @ 2011-03-01  5:45 UTC (permalink / raw)
  To: Po-Yu Chuang
  Cc: netdev, linux-kernel, bhutchings, joe, dilinger, mirqus, davem,
	Po-Yu Chuang
In-Reply-To: <AANLkTimeDR27Q7fcBmB0ix7Q_pRT_GDpc7S3LO21-fh-@mail.gmail.com>

Le mardi 01 mars 2011 à 13:20 +0800, Po-Yu Chuang a écrit :

> That means, since every rx buffer is large enough, it is impossible to
> get a multi-segment packets.
> 
> So I simplified ftmac100_rx_packet().

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

I am only wondering then if not using fragments would be faster then
(eventually doing copybreaks for small frames like tg3)

But if you plan to get new hw soon, you have the infrastructure...

^ permalink raw reply

* Re: [PATCH net-2.6] bonding: drop frames received with master's source MAC
From: Jay Vosburgh @ 2011-03-01  5:46 UTC (permalink / raw)
  To: Andy Gospodarek
  Cc: Nicolas de =?iso-8859-1?Q?Peslo=FCan?=, netdev, David Miller,
	Herbert Xu, Jiri Pirko
In-Reply-To: <20110301023525.GK11864@gospo.rdu.redhat.com>

Andy Gospodarek <andy@greyhouse.net> wrote:

>On Mon, Feb 28, 2011 at 10:45:08PM +0100, Nicolas de Pesloüan wrote:
>> Le 28/02/2011 17:32, Andy Gospodarek a écrit :
>>> On Sat, Feb 26, 2011 at 12:08:03AM +0100, Nicolas de Pesloüan wrote:
>>>> Le 25/02/2011 23:24, Andy Gospodarek a écrit :
>>> [...]
>>>>>
>>>>> I confirmed your suspicion, this breaks ARP monitoring.  I would still
>>>>> welcome other opinions though as I think it would be nice to fix this as
>>>>> low as possible.
>>>>
>>>> Why do you want to fix it earlier that in ndisc_recv_ns drop? Your
>>>> original idea of silently dropping the frame there seems perfect to me.
>>>>
>>>
>>> Maybe it's just me, but I cannot understand why we want a bunch of extra
>>> packets floating up into the stack when they may only create issues for
>>> the recipients of these duplicate frames.
>>>
>>> Clearly my original patch needs to be refined so ARP monitoring still
>>> works, but I would rather fix the issue there than in a higher layer.
>>
>> Jay explained that the current implementation should already trap those 
>> frames, on inactive slaves, in modes where inactive slaves exist. I agree 
>> with him.
>>
>> What mode are you seeing this problem in? If the current "should drop" 
>> logic is leaking, then yes, we should fix it. But we currently don't see 
>> where it is leaking.
>>
>
>Use round-robin.  To reproduce just take the interface down and bring it
>back up.  You should see the messages right away.

	What is the bond connected to?  The -rr and -xor modes are meant
to interoperate with switch ports configured for Etherchannel (or an
equivalent).  In that case, I wouldn't expect the switch to turn the
broadcasts / multicasts around and send them back out to a member of the
channel group they originated from.

	If the switch isn't configured properly, then I'd expect it to
complain about MAC flapping or the like.  Unless it's an unmanaged
switch that doesn't do Etherchannel (etc), or you're setting up an
unusual topology.

>I've been doing some more testing on a new patch and should have
>something ready tomorrow.  My latest patch actually replaces the final
>'return 0' with a call to a function that will return true if the sender
>was the device that received it.  This will hopefully prevent some of
>the failures with the first patch I posted.  I'll know a bit more
>tomorrow if this approach seems reasonable.

	How do you figure that out?  In -rr mode, all of the slaves
should have the same MAC address, and the slaves shouldn't be running
IPv6 addrconf separately from the master anyway.  Something magic just
for NA/NS packets?

	-J

---
	-Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com

^ permalink raw reply

* Re: [PATCH v6] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Po-Yu Chuang @ 2011-03-01  5:51 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, linux-kernel, bhutchings, joe, dilinger, mirqus, davem,
	Po-Yu Chuang
In-Reply-To: <1298958336.2676.21.camel@edumazet-laptop>

Hi Eric,

On Tue, Mar 1, 2011 at 1:45 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> Le mardi 01 mars 2011 à 13:20 +0800, Po-Yu Chuang a écrit :
>
>> That means, since every rx buffer is large enough, it is impossible to
>> get a multi-segment packets.
>>
>> So I simplified ftmac100_rx_packet().
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Ah, I just want to submit a v7.
Could you resign again later, please? :-)

> I am only wondering then if not using fragments would be faster then
> (eventually doing copybreaks for small frames like tg3)

Although not many circumstances are tested.
iperf shows that it is a little faster to use fragments than memcpy, so...

> But if you plan to get new hw soon, you have the infrastructure...

best regards,
Po-Yu Chuang

^ permalink raw reply

* Re: [PATCH ref0] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Eric Dumazet @ 2011-03-01  5:53 UTC (permalink / raw)
  To: Po-Yu Chuang
  Cc: David Miller, netdev, linux-kernel, bhutchings, joe, dilinger,
	mirqus, ratbert, Ajit Khaparde
In-Reply-To: <AANLkTi=MQoEKY5OLLN6f8-N7eD--Q6hL99HFFTAGU_Y4@mail.gmail.com>

Le mardi 01 mars 2011 à 13:45 +0800, Po-Yu Chuang a écrit :
> Hi Eric,
> 
> On Sat, Feb 26, 2011 at 2:47 AM, Eric Dumazet <eric.dumazet@gmail.com> wrote:

> >
> > A compromise would be to use 128 for the allocation, but only copy 64
> > bytes.
> 
> I will use this way and submit v7 later.
> 

BTW, even with IPV6, the first 64 bytes contain "RPS" header

We changed NET_SKB_PAD a while back and added this comment in
include/linux/skbuff.h

NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8)

^ permalink raw reply

* Re: [PATCH v6] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Eric Dumazet @ 2011-03-01  5:54 UTC (permalink / raw)
  To: Po-Yu Chuang
  Cc: netdev, linux-kernel, bhutchings, joe, dilinger, mirqus, davem,
	Po-Yu Chuang
In-Reply-To: <AANLkTi=Sp-mAV1556pZvCvHzQ=MQNHUzjo0ap2DhKwWe@mail.gmail.com>

Le mardi 01 mars 2011 à 13:51 +0800, Po-Yu Chuang a écrit :

> Ah, I just want to submit a v7.
> Could you resign again later, please? :-)

Sure

^ permalink raw reply

* Re: [PATCH v6] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Eric Dumazet @ 2011-03-01  5:59 UTC (permalink / raw)
  To: Po-Yu Chuang
  Cc: netdev, linux-kernel, bhutchings, joe, dilinger, mirqus, davem,
	Po-Yu Chuang
In-Reply-To: <AANLkTi=Sp-mAV1556pZvCvHzQ=MQNHUzjo0ap2DhKwWe@mail.gmail.com>

Le mardi 01 mars 2011 à 13:51 +0800, Po-Yu Chuang a écrit :
> On Tue, Mar 1, 2011 at 1:45 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:

> > I am only wondering then if not using fragments would be faster then
> > (eventually doing copybreaks for small frames like tg3)
> 
> Although not many circumstances are tested.
> iperf shows that it is a little faster to use fragments than memcpy, so...

This might be a side effect of skb->truesize being smaller with
fragments than "regular packets" and socket backlog congestion.

If a full page was really accounted for in skb->truesize, instead of
used length, performance might be the same or lower :(

-	skb->truesize += length;
+	skb->truesize += PAGE_SIZE;

This has nothing to do with your driver, but a core implementation
detail.

^ permalink raw reply

* Re: dccp: null-pointer dereference on close
From: Gerrit Renker @ 2011-03-01  5:59 UTC (permalink / raw)
  To: Johan Hovold; +Cc: dccp, netdev
In-Reply-To: <20110226174505.GB3609@localhost>

[-- Attachment #1: Type: text/plain, Size: 2899 bytes --]

Johan,

thanks a lot for the detailed description.

I think I have found the cause of the dccp timewait problem: in the 
mainline tree there is a path

 dccp_v4_do_rcv() 
	|
	| state other than OPEN
	v
 dccp_rcv_state_process()
	|
	| DCCP_PKT_RESET
	v
 dccp_rcv_reset()
	|
	v
 dccp_time_wait()

In the backtrace dccp_close() had been called, hence dccp_set_state() has
destroyed inet_csk(sk)->icsk_bind_hash, which then subsequently in the
misplaced dccp_time_wait() caused the NULL pointer exception.

I have just checked, this problem seems to not be possible in the test
tree, since it checks first in dccp_rcv_state_process() if DCCP_CLOSED
has been entered (if it receives a packet in this state, it sends a 
Reset with code 3, "No Connection").

I am attaching the relevant patch from the test tree - would it be possible
for you to test it with the same setup? (The relevant passage is right in 
the first hunk, where it tests for state == DCCP_CLOSED).

Will submit this patch subsequently also.

Thanks again,
Gerrit


| root@overo:~# [84140.128631] ------------[ cut here ]------------
| [84140.133575] WARNING: at net/ipv4/inet_timewait_sock.c:141 __inet_twsk_hashdance+0x48/0x128()
| [84140.142517] Modules linked in: arc4 ecb carl9170 rt2870sta(C) mac80211 r8712u(C) crc_ccitt ah
| [84140.151794] [<c0038850>] (unwind_backtrace+0x0/0xec) from [<c0055364>] (warn_slowpath_common)
| [84140.161743] [<c0055364>] (warn_slowpath_common+0x4c/0x64) from [<c0055398>] (warn_slowpath_n)
| [84140.171966] [<c0055398>] (warn_slowpath_null+0x1c/0x24) from [<c02b72d0>] (__inet_twsk_hashd)
| [84140.182373] [<c02b72d0>] (__inet_twsk_hashdance+0x48/0x128) from [<c031caa0>] (dccp_time_wai)
| [84140.192413] [<c031caa0>] (dccp_time_wait+0x40/0xc8) from [<c031c15c>] (dccp_rcv_state_proces)
| [84140.202636] [<c031c15c>] (dccp_rcv_state_process+0x120/0x538) from [<c032609c>] (dccp_v4_do_)
| [84140.213043] [<c032609c>] (dccp_v4_do_rcv+0x11c/0x14c) from [<c0286594>] (release_sock+0xac/0)
| [84140.222442] [<c0286594>] (release_sock+0xac/0x110) from [<c031fd34>] (dccp_close+0x28c/0x380)
| [84140.231475] [<c031fd34>] (dccp_close+0x28c/0x380) from [<c02d9a78>] (inet_release+0x64/0x70)
| [84140.240386] [<c02d9a78>] (inet_release+0x64/0x70) from [<c0284ddc>] (sock_release+0x24/0xb8)
| [84140.249328] [<c0284ddc>] (sock_release+0x24/0xb8) from [<c0284e94>] (sock_close+0x24/0x34)
| [84140.258087] [<c0284e94>] (sock_close+0x24/0x34) from [<c00c2e4c>] (fput+0x108/0x1f4)
| [84140.266296] [<c00c2e4c>] (fput+0x108/0x1f4) from [<c00c0104>] (filp_close+0x70/0x7c)
| [84140.274505] [<c00c0104>] (filp_close+0x70/0x7c) from [<c00c01c4>] (sys_close+0xb4/0x10c)
| [84140.283081] [<c00c01c4>] (sys_close+0xb4/0x10c) from [<c0033a80>] (ret_fast_syscall+0x0/0x30)
| [84140.292114] ---[ end trace b8877ec9d542c32e ]---
| [84140.296997] Unable to handle kernel NULL pointer dereference at virtual address 00000010


[-- Attachment #2: DCCP_Reorder-Input-Processing.diff --]
[-- Type: text/x-diff, Size: 5694 bytes --]

dccp: Clean up slow-path input processing

This patch rearranges the order of statements of the slow-path input processing
(i.e. any other state than OPEN), to resolve the following issues.

 1. Dependencies: the order of statements now better matches RFC 4340, 8.5, i.e.
    step 7 is before step 9 (previously 9 was before 7), and parsing options in
    step 8 (which can consume resources) now comes after step 7.
 2. Bug-fix: in state CLOSED, there should not be any sequence number checking
    or option processing. This is why the test for CLOSED has been moved after
    the test for LISTEN.
 3. As before sequence number checks are omitted if in state LISTEN/REQUEST, due
    to the note underneath the table in RFC 4340, 7.5.3.
 4. Packets are now passed on to Ack Vector / CCID processing only after
    - step 7  (receive unexpected packets), 
    - step 9  (receive Reset),
    - step 13 (receive CloseReq),
    - step 14 (receive Close)
    and only if the state is PARTOPEN. This simplifies CCID processing:
    - in LISTEN/CLOSED the CCIDs are non-existent;
    - in RESPOND/REQUEST the CCIDs have not yet been negotiated;
    - in CLOSEREQ and active-CLOSING the node has already closed this socket;
    - in passive-CLOSING the client is waiting for its Reset.
    In the last case, RFC 4340, 8.3 leaves it open to ignore further incoming
    data, which is the approach taken here.

As a result of (3), CCID processing is now indeed confined to OPEN/PARTOPEN
states, i.e. congestion control is performed only on the flow of data packets. 

This avoids pathological cases of doing congestion control on those messages
which set up and terminate the connection. 

I have done some checks to see if this creates a problem in other parts of
the code. This seems not to be the case; even if there were one, it would be
better to fix it than to perform congestion control on Close/Request/Response
messages. Similarly for Ack Vectors (as they depend on the negotiated CCID).

Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
---
 net/dccp/input.c |   68 ++++++++++++++++++++++++++-----------------------------
 1 file changed, 33 insertions(+), 35 deletions(-)

--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -614,22 +614,36 @@ int dccp_rcv_state_process(struct sock *
 		/* Caller (dccp_v4_do_rcv) will send Reset */
 		dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION;
 		return 1;
+	} else if (sk->sk_state == DCCP_CLOSED) {
+		dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION;
+		return 1;
 	}
 
-	if (sk->sk_state != DCCP_REQUESTING && sk->sk_state != DCCP_RESPOND) {
-		if (dccp_check_seqno(sk, skb))
-			goto discard;
-
-		/*
-		 * Step 8: Process options and mark acknowledgeable
-		 */
-		if (dccp_parse_options(sk, NULL, skb))
-			return 1;
+	/* Step 6: Check sequence numbers (omitted in LISTEN/REQUEST state) */
+	if (sk->sk_state != DCCP_REQUESTING && dccp_check_seqno(sk, skb))
+		goto discard;
 
-		dccp_handle_ackvec_processing(sk, skb);
-		dccp_deliver_input_to_ccids(sk, skb);
+	/*
+	 *   Step 7: Check for unexpected packet types
+	 *      If (S.is_server and P.type == Response)
+	 *	    or (S.is_client and P.type == Request)
+	 *	    or (S.state == RESPOND and P.type == Data),
+	 *	  Send Sync packet acknowledging P.seqno
+	 *	  Drop packet and return
+	 */
+	if ((dp->dccps_role != DCCP_ROLE_CLIENT &&
+	     dh->dccph_type == DCCP_PKT_RESPONSE) ||
+	    (dp->dccps_role == DCCP_ROLE_CLIENT &&
+	     dh->dccph_type == DCCP_PKT_REQUEST) ||
+	    (sk->sk_state == DCCP_RESPOND && dh->dccph_type == DCCP_PKT_DATA)) {
+		dccp_send_sync(sk, dcb->dccpd_seq, DCCP_PKT_SYNC);
+		goto discard;
 	}
 
+	/*  Step 8: Process options */
+	if (dccp_parse_options(sk, NULL, skb))
+		return 1;
+
 	/*
 	 *  Step 9: Process Reset
 	 *	If P.type == Reset,
@@ -637,41 +651,21 @@ int dccp_rcv_state_process(struct sock *
 	 *		S.state := TIMEWAIT
 	 *		Set TIMEWAIT timer
 	 *		Drop packet and return
-	*/
+	 */
 	if (dh->dccph_type == DCCP_PKT_RESET) {
 		dccp_rcv_reset(sk, skb);
 		return 0;
-		/*
-		 *   Step 7: Check for unexpected packet types
-		 *      If (S.is_server and P.type == Response)
-		 *	    or (S.is_client and P.type == Request)
-		 *	    or (S.state == RESPOND and P.type == Data),
-		 *	  Send Sync packet acknowledging P.seqno
-		 *	  Drop packet and return
-		 */
-	} else if ((dp->dccps_role != DCCP_ROLE_CLIENT &&
-		    dh->dccph_type == DCCP_PKT_RESPONSE) ||
-		    (dp->dccps_role == DCCP_ROLE_CLIENT &&
-		     dh->dccph_type == DCCP_PKT_REQUEST) ||
-		    (sk->sk_state == DCCP_RESPOND &&
-		     dh->dccph_type == DCCP_PKT_DATA)) {
-		dccp_send_sync(sk, dcb->dccpd_seq, DCCP_PKT_SYNC);
-		goto discard;
-	} else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) {
+	} else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) {	/* Step 13 */
 		if (dccp_rcv_closereq(sk, skb))
 			return 0;
 		goto discard;
-	} else if (dh->dccph_type == DCCP_PKT_CLOSE) {
+	} else if (dh->dccph_type == DCCP_PKT_CLOSE) {		/* Step 14 */
 		if (dccp_rcv_close(sk, skb))
 			return 0;
 		goto discard;
 	}
 
 	switch (sk->sk_state) {
-	case DCCP_CLOSED:
-		dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION;
-		return 1;
-
 	case DCCP_REQUESTING:
 		queued = dccp_rcv_request_sent_state_process(sk, skb, dh, len);
 		if (queued >= 0)
@@ -680,8 +674,12 @@ int dccp_rcv_state_process(struct sock *
 		__kfree_skb(skb);
 		return 0;
 
-	case DCCP_RESPOND:
 	case DCCP_PARTOPEN:
+		/* Step 8: if using Ack Vectors, mark packet acknowledgeable */
+		dccp_handle_ackvec_processing(sk, skb);
+		dccp_deliver_input_to_ccids(sk, skb);
+		/* fall through */
+	case DCCP_RESPOND:
 		queued = dccp_rcv_respond_partopen_state_process(sk, skb,
 								 dh, len);
 		break;

^ permalink raw reply

* Re: [patch net-next-2.6 4/4] bridge: implement slave management operations
From: Jiri Pirko @ 2011-03-01  6:19 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: Stephen Hemminger, netdev, davem, kaber, fubar
In-Reply-To: <20110228144516.0174a9c8@nehalam>

Mon, Feb 28, 2011 at 11:45:16PM CET, shemminger@vyatta.com wrote:
>On Mon, 28 Feb 2011 14:17:29 -0800
>Stephen Hemminger <shemminger@linux-foundation.org> wrote:
>
>> On Fri, 11 Feb 2011 16:23:47 +0100
>> Jiri Pirko <jpirko@redhat.com> wrote:
>> 
>> > 
>> > Signed-off-by: Jiri Pirko <jpirko@redhat.com>
>> 
>
>Never mind, this is good. I was confusing slave with bridge initialization
>and netdevice ops with ethtool ops.
>
>Ethtool ops seems to be growing like weeds lately...


This was replaced by:
bridge: implement [add/del]_slave ops


never intended to be in ethtool
>
>
>
>-- 

^ permalink raw reply

* [patch net-next-2.6] bonding: remove skb_share_check in handle_frame
From: Jiri Pirko @ 2011-03-01  6:22 UTC (permalink / raw)
  To: netdev; +Cc: davem, fubar, eric.dumazet, nicolas.2p.debian, andy

No need to do share check here since call to netif_rx was removed and
skb is passed back.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 drivers/net/bonding/bond_main.c |    4 ----
 1 files changed, 0 insertions(+), 4 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index f3e5d7f..e8c0f95 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1503,10 +1503,6 @@ static struct sk_buff *bond_handle_frame(struct sk_buff *skb)
 	if (unlikely(!slave))
 		return skb;
 
-	skb = skb_share_check(skb, GFP_ATOMIC);
-	if (unlikely(!skb))
-		return NULL;
-
 	bond_dev = ACCESS_ONCE(skb->dev->master);
 	if (unlikely(!bond_dev))
 		return skb;
-- 
1.7.3.4


^ permalink raw reply related

* net: allow handlers to be processed for orig_dev
From: Jiri Pirko @ 2011-03-01  6:26 UTC (permalink / raw)
  To: netdev
  Cc: davem, shemminger, kaber, fubar, eric.dumazet, nicolas.2p.debian,
	andy

This was there before, I forgot about this. Allows deliveries to
ptype_base handlers registered for orig_dev. I presume this is still
desired.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
---
 net/core/dev.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index 30440e7..9f66de9 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3208,7 +3208,8 @@ ncls:
 	list_for_each_entry_rcu(ptype,
 			&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
 		if (ptype->type == type &&
-		    (ptype->dev == null_or_dev || ptype->dev == skb->dev)) {
+		    (ptype->dev == null_or_dev || ptype->dev == skb->dev ||
+		     ptype->dev == orig_dev)) {
 			if (pt_prev)
 				ret = deliver_skb(skb, pt_prev, orig_dev);
 			pt_prev = ptype;
-- 
1.7.3.4


^ permalink raw reply related

* Re: [Open-FCoE] [PATCH] fcoe: correct checking for bonding
From: Jiri Pirko @ 2011-03-01  6:37 UTC (permalink / raw)
  To: Joe Eykholt; +Cc: Jay Vosburgh, James.Bottomley, netdev, devel, linux-scsi
In-Reply-To: <4D6BE155.7050109@gmail.com>

Mon, Feb 28, 2011 at 06:54:29PM CET, joe.eykholt@gmail.com wrote:
>On 2/28/11 9:15 AM, Jay Vosburgh wrote:
>>Jiri Pirko<jpirko@redhat.com>  wrote:
>>
>>>Check for IFF_BONDING as this flag is set-up for all bonding devices.
>>>
>>>Signed-off-by: Jiri Pirko<jpirko@redhat.com>
>>>---
>>>drivers/scsi/fcoe/fcoe.c |    4 +---
>>>1 files changed, 1 insertions(+), 3 deletions(-)
>>>
>>>diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
>>>index 9f9600b..67714a4 100644
>>>--- a/drivers/scsi/fcoe/fcoe.c
>>>+++ b/drivers/scsi/fcoe/fcoe.c
>>>@@ -285,9 +285,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
>>>	}
>>>
>>>	/* Do not support for bonding device */
>>>-	if ((netdev->priv_flags&  IFF_MASTER_ALB) ||
>>>-	    (netdev->priv_flags&  IFF_SLAVE_INACTIVE) ||
>>>-	    (netdev->priv_flags&  IFF_MASTER_8023AD)) {
>>>+	if (netdev->priv_flags&  IFF_BONDING) {
>>>		FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n");
>>>		return -EOPNOTSUPP;
>>>	}
>>
>>	Based on past discussions, I believe the intent of the code is
>>to permit FCOE over bonding only for active-backup mode, and possibly
>>for -xor/-rr as well.
>>
>>	I'm not sure if the slave or the master is what's being tested
>>here, so I'm not sure what the right thing to do is.  I suspect it's the
>>master, as I recall discussion of one configuration involving
>>active-backup mode balancing FCOE traffic over both the active and
>>inactive slaves.  FCOE uses the "orig_dev" logic in __netif_receive_skb
>>to have the packets delivered even on the nominally inactive slave.
>>
>>	-J
>>
>>---
>>	-Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com
>
>Right.  That was the intent.  It should work on the physical dev, but probably
>not on the master of the bond.
>
>If you have a master/slave bond for IPv4 between eth1 and eth2, say,
>and they are going to two different DCE (FCoE) switches, presumably on
>different VSANs but with ultimate access to the same disks,
>then you want to split the FCoE traffic in active/active
>mode using separate FCoE instances on eth1 and eth2 even though IP
>is using active/standby on bond0.  This should work.  But, putting fcoe
>on bond0 isn't going to do what you want.
>
>However, it seems like the check above shouldn't be checking
>IFF_SLAVE_INACTIVE.   I can't test this.

OK. So I guess the right check should be for:
(netdev->priv_flags & IFF_BONDING && netdev->flags & IFF_MASTER)

This would disable adding all bond devices (like bond0 etc) and allows
to use enslaved physdevs.

Note that checking for mode is irrelevant here. Mode could be easily
changed later without fcoe knowing that.

Jirka
>
>	Joe
>

^ permalink raw reply

* Re: net: allow handlers to be processed for orig_dev
From: Jiri Pirko @ 2011-03-01  6:43 UTC (permalink / raw)
  To: netdev; +Cc: davem
In-Reply-To: <20110301062631.GC2855@psychotron.redhat.com>

ou, forgot [PATCH net-next-2.6] - sorry

Tue, Mar 01, 2011 at 07:26:31AM CET, jpirko@redhat.com wrote:
>This was there before, I forgot about this. Allows deliveries to
>ptype_base handlers registered for orig_dev. I presume this is still
>desired.
>
>Signed-off-by: Jiri Pirko <jpirko@redhat.com>
>---
> net/core/dev.c |    3 ++-
> 1 files changed, 2 insertions(+), 1 deletions(-)
>
>diff --git a/net/core/dev.c b/net/core/dev.c
>index 30440e7..9f66de9 100644
>--- a/net/core/dev.c
>+++ b/net/core/dev.c
>@@ -3208,7 +3208,8 @@ ncls:
> 	list_for_each_entry_rcu(ptype,
> 			&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
> 		if (ptype->type == type &&
>-		    (ptype->dev == null_or_dev || ptype->dev == skb->dev)) {
>+		    (ptype->dev == null_or_dev || ptype->dev == skb->dev ||
>+		     ptype->dev == orig_dev)) {
> 			if (pt_prev)
> 				ret = deliver_skb(skb, pt_prev, orig_dev);
> 			pt_prev = ptype;
>-- 
>1.7.3.4
>

^ permalink raw reply

* [PATCH] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Po-Yu Chuang @ 2011-03-01  6:48 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, bhutchings, eric.dumazet, joe, dilinger, mirqus,
	davem, Po-Yu Chuang
In-Reply-To: <1298627845-1583-1-git-send-email-ratbert.chuang@gmail.com>

From: Po-Yu Chuang <ratbert@faraday-tech.com>

FTMAC100 Ethernet Media Access Controller supports 10/100 Mbps and
MII.  This driver has been working on some ARM/NDS32 SoC's including
Faraday A320 and Andes AG101.

Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
---
v2:
always use NAPI
do not use our own net_device_stats structure
don't set trans_start and last_rx
stats.rx_packets and stats.rx_bytes include dropped packets
add missed netif_napi_del()
initialize spinlocks in probe function
remove rx_lock and hw_lock
use netdev_[err/info/dbg] instead of dev_* ones
use netdev_alloc_skb_ip_align()
remove ftmac100_get_stats()
use is_valid_ether_addr() instead of is_zero_ether_addr()
add const to ftmac100_ethtool_ops and ftmac100_netdev_ops
use net_ratelimit() instead of printk_ratelimit()
no explicit inline
use %pM to print MAC address
add comment before wmb
use napi poll() to handle all interrupts

v3:
undo "stats.rx_packets and stats.rx_bytes include dropped packets"
ftmac100_mdio_read() returns 0 if error
fix comment typos
use pr_fmt and pr_info
define INT_MASK_ALL_ENABLED
define MACCR_ENABLE_ALL
do not count length error many times
use bool/true/false
use cpu_to_le32/le32_to_cpu to access descriptors
indent style fix

v4:
should not access skb after netif_receive_skb()
use resource_size()
better way to use cpu_to_le32/le32_to_cpu
use spin_lock() for tx_lock
combine all netdev_info() together in ftmac100_poll()

v5:
use dev_kfree_skb() in ftmac100_tx_complete_packet()
cpu_to_le32/le32_to_cpu usage fix
drop GFP_DMA

v6:
cpu_to_le32/le32_to_cpu usage fix
remove "tx queue full" message
reduce critical section protected by tx_lock
add check of MAX_PKT_SIZE and RX_BUF_SIZE
add __exit to ftmac100_remove()
simplify ftmac100_rx_packet()
zero copy - use skb_fill_page_desc() and __pskb_pull_tail().
pull more data to skb head to include tcp/ip header

v7:
allocate 128 bytes skb and pull 64 bytes only

 drivers/net/Kconfig    |    9 +
 drivers/net/Makefile   |    1 +
 drivers/net/ftmac100.c | 1196 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/ftmac100.h |  180 ++++++++
 4 files changed, 1386 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/ftmac100.c
 create mode 100644 drivers/net/ftmac100.h

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 4f1755b..6b12274 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2014,6 +2014,15 @@ config BCM63XX_ENET
 	  This driver supports the ethernet MACs in the Broadcom 63xx
 	  MIPS chipset family (BCM63XX).
 
+config FTMAC100
+	tristate "Faraday FTMAC100 10/100 Ethernet support"
+	depends on ARM
+	select MII
+	help
+	  This driver supports the FTMAC100 10/100 Ethernet controller
+	  from Faraday. It is used on Faraday A320, Andes AG101 and some
+	  other ARM/NDS32 SoC's.
+
 source "drivers/net/fs_enet/Kconfig"
 
 source "drivers/net/octeon/Kconfig"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90738d..7c21711 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -147,6 +147,7 @@ obj-$(CONFIG_FORCEDETH) += forcedeth.o
 obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
 obj-$(CONFIG_AX88796) += ax88796.o
 obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
+obj-$(CONFIG_FTMAC100) += ftmac100.o
 
 obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
 obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c
new file mode 100644
index 0000000..df70368
--- /dev/null
+++ b/drivers/net/ftmac100.c
@@ -0,0 +1,1196 @@
+/*
+ * Faraday FTMAC100 10/100 Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+
+#include "ftmac100.h"
+
+#define DRV_NAME	"ftmac100"
+#define DRV_VERSION	"0.2"
+
+#define RX_QUEUE_ENTRIES	128	/* must be power of 2 */
+#define TX_QUEUE_ENTRIES	16	/* must be power of 2 */
+
+#define MAX_PKT_SIZE		1518
+#define RX_BUF_SIZE		2044	/* must be smaller than 0x7ff */
+
+#if MAX_PKT_SIZE > 0x7ff
+#error invalid MAX_PKT_SIZE
+#endif
+
+#if RX_BUF_SIZE > 0x7ff || RX_BUF_SIZE > PAGE_SIZE
+#error invalid RX_BUF_SIZE
+#endif
+
+/******************************************************************************
+ * private data
+ *****************************************************************************/
+struct ftmac100_descs {
+	struct ftmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
+	struct ftmac100_txdes txdes[TX_QUEUE_ENTRIES];
+};
+
+struct ftmac100 {
+	struct resource *res;
+	void __iomem *base;
+	int irq;
+
+	struct ftmac100_descs *descs;
+	dma_addr_t descs_dma_addr;
+
+	unsigned int rx_pointer;
+	unsigned int tx_clean_pointer;
+	unsigned int tx_pointer;
+	unsigned int tx_pending;
+
+	spinlock_t tx_lock;
+
+	struct net_device *netdev;
+	struct device *dev;
+	struct napi_struct napi;
+
+	struct mii_if_info mii;
+};
+
+static int ftmac100_alloc_rx_page(struct ftmac100 *priv, struct ftmac100_rxdes *rxdes);
+
+/******************************************************************************
+ * internal functions (hardware register access)
+ *****************************************************************************/
+#define INT_MASK_ALL_ENABLED	(FTMAC100_INT_RPKT_FINISH	| \
+				 FTMAC100_INT_NORXBUF		| \
+				 FTMAC100_INT_XPKT_OK		| \
+				 FTMAC100_INT_XPKT_LOST		| \
+				 FTMAC100_INT_RPKT_LOST		| \
+				 FTMAC100_INT_AHB_ERR		| \
+				 FTMAC100_INT_PHYSTS_CHG)
+
+#define INT_MASK_ALL_DISABLED	0
+
+static void ftmac100_enable_all_int(struct ftmac100 *priv)
+{
+	iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTMAC100_OFFSET_IMR);
+}
+
+static void ftmac100_disable_all_int(struct ftmac100 *priv)
+{
+	iowrite32(INT_MASK_ALL_DISABLED, priv->base + FTMAC100_OFFSET_IMR);
+}
+
+static void ftmac100_set_rx_ring_base(struct ftmac100 *priv, dma_addr_t addr)
+{
+	iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR);
+}
+
+static void ftmac100_set_tx_ring_base(struct ftmac100 *priv, dma_addr_t addr)
+{
+	iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR);
+}
+
+static void ftmac100_txdma_start_polling(struct ftmac100 *priv)
+{
+	iowrite32(1, priv->base + FTMAC100_OFFSET_TXPD);
+}
+
+static int ftmac100_reset(struct ftmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	int i;
+
+	/* NOTE: reset clears all registers */
+	iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR);
+
+	for (i = 0; i < 5; i++) {
+		unsigned int maccr;
+
+		maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
+		if (!(maccr & FTMAC100_MACCR_SW_RST)) {
+			/*
+			 * FTMAC100_MACCR_SW_RST cleared does not indicate
+			 * that hardware reset completed (what the f*ck).
+			 * We still need to wait for a while.
+			 */
+			usleep_range(500, 1000);
+			return 0;
+		}
+
+		usleep_range(1000, 10000);
+	}
+
+	netdev_err(netdev, "software reset failed\n");
+	return -EIO;
+}
+
+static void ftmac100_set_mac(struct ftmac100 *priv, const unsigned char *mac)
+{
+	unsigned int maddr = mac[0] << 8 | mac[1];
+	unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+
+	iowrite32(maddr, priv->base + FTMAC100_OFFSET_MAC_MADR);
+	iowrite32(laddr, priv->base + FTMAC100_OFFSET_MAC_LADR);
+}
+
+#define MACCR_ENABLE_ALL	(FTMAC100_MACCR_XMT_EN	| \
+				 FTMAC100_MACCR_RCV_EN	| \
+				 FTMAC100_MACCR_XDMA_EN	| \
+				 FTMAC100_MACCR_RDMA_EN	| \
+				 FTMAC100_MACCR_CRC_APD	| \
+				 FTMAC100_MACCR_FULLDUP	| \
+				 FTMAC100_MACCR_RX_RUNT	| \
+				 FTMAC100_MACCR_RX_BROADPKT)
+
+static int ftmac100_start_hw(struct ftmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+
+	if (ftmac100_reset(priv))
+		return -EIO;
+
+	/* setup ring buffer base registers */
+	ftmac100_set_rx_ring_base(priv,
+				  priv->descs_dma_addr +
+				  offsetof(struct ftmac100_descs, rxdes));
+	ftmac100_set_tx_ring_base(priv,
+				  priv->descs_dma_addr +
+				  offsetof(struct ftmac100_descs, txdes));
+
+	iowrite32(FTMAC100_APTC_RXPOLL_CNT(1), priv->base + FTMAC100_OFFSET_APTC);
+
+	ftmac100_set_mac(priv, netdev->dev_addr);
+
+	iowrite32(MACCR_ENABLE_ALL, priv->base + FTMAC100_OFFSET_MACCR);
+	return 0;
+}
+
+static void ftmac100_stop_hw(struct ftmac100 *priv)
+{
+	iowrite32(0, priv->base + FTMAC100_OFFSET_MACCR);
+}
+
+/******************************************************************************
+ * internal functions (receive descriptor)
+ *****************************************************************************/
+static bool ftmac100_rxdes_first_segment(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FRS);
+}
+
+static bool ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_LRS);
+}
+
+static bool ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN);
+}
+
+static void ftmac100_rxdes_set_dma_own(struct ftmac100_rxdes *rxdes)
+{
+	/* clear status bits */
+	rxdes->rxdes0 = cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN);
+}
+
+static bool ftmac100_rxdes_rx_error(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ERR);
+}
+
+static bool ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_CRC_ERR);
+}
+
+static bool ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FTL);
+}
+
+static bool ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RUNT);
+}
+
+static bool ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ODD_NB);
+}
+
+static unsigned int ftmac100_rxdes_frame_length(struct ftmac100_rxdes *rxdes)
+{
+	return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RFL;
+}
+
+static bool ftmac100_rxdes_multicast(struct ftmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_MULTICAST);
+}
+
+static void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes,
+					   unsigned int size)
+{
+	rxdes->rxdes1 &= cpu_to_le32(FTMAC100_RXDES1_EDORR);
+	rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_RXBUF_SIZE(size));
+}
+
+static void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes)
+{
+	rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_EDORR);
+}
+
+static void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes,
+					dma_addr_t addr)
+{
+	rxdes->rxdes2 = cpu_to_le32(addr);
+}
+
+static dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes)
+{
+	return le32_to_cpu(rxdes->rxdes2);
+}
+
+/*
+ * rxdes3 is not used by hardware. We use it to keep track of page.
+ * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
+ */
+static void ftmac100_rxdes_set_page(struct ftmac100_rxdes *rxdes, struct page *page)
+{
+	rxdes->rxdes3 = (unsigned int)page;
+}
+
+static struct page *ftmac100_rxdes_get_page(struct ftmac100_rxdes *rxdes)
+{
+	return (struct page *)rxdes->rxdes3;
+}
+
+/******************************************************************************
+ * internal functions (receive)
+ *****************************************************************************/
+static int ftmac100_next_rx_pointer(int pointer)
+{
+	return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
+}
+
+static void ftmac100_rx_pointer_advance(struct ftmac100 *priv)
+{
+	priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer);
+}
+
+static struct ftmac100_rxdes *ftmac100_current_rxdes(struct ftmac100 *priv)
+{
+	return &priv->descs->rxdes[priv->rx_pointer];
+}
+
+static struct ftmac100_rxdes *
+ftmac100_rx_locate_first_segment(struct ftmac100 *priv)
+{
+	struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
+
+	while (!ftmac100_rxdes_owned_by_dma(rxdes)) {
+		if (ftmac100_rxdes_first_segment(rxdes))
+			return rxdes;
+
+		ftmac100_rxdes_set_dma_own(rxdes);
+		ftmac100_rx_pointer_advance(priv);
+		rxdes = ftmac100_current_rxdes(priv);
+	}
+
+	return NULL;
+}
+
+static bool ftmac100_rx_packet_error(struct ftmac100 *priv,
+				     struct ftmac100_rxdes *rxdes)
+{
+	struct net_device *netdev = priv->netdev;
+	bool error = false;
+
+	if (unlikely(ftmac100_rxdes_rx_error(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx err\n");
+
+		netdev->stats.rx_errors++;
+		error = true;
+	}
+
+	if (unlikely(ftmac100_rxdes_crc_error(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx crc err\n");
+
+		netdev->stats.rx_crc_errors++;
+		error = true;
+	}
+
+	if (unlikely(ftmac100_rxdes_frame_too_long(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx frame too long\n");
+
+		netdev->stats.rx_length_errors++;
+		error = true;
+	} else if (unlikely(ftmac100_rxdes_runt(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx runt\n");
+
+		netdev->stats.rx_length_errors++;
+		error = true;
+	} else if (unlikely(ftmac100_rxdes_odd_nibble(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx odd nibble\n");
+
+		netdev->stats.rx_length_errors++;
+		error = true;
+	}
+
+	return error;
+}
+
+static void ftmac100_rx_drop_packet(struct ftmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
+	bool done = false;
+
+	if (net_ratelimit())
+		netdev_dbg(netdev, "drop packet %p\n", rxdes);
+
+	do {
+		if (ftmac100_rxdes_last_segment(rxdes))
+			done = true;
+
+		ftmac100_rxdes_set_dma_own(rxdes);
+		ftmac100_rx_pointer_advance(priv);
+		rxdes = ftmac100_current_rxdes(priv);
+	} while (!done && !ftmac100_rxdes_owned_by_dma(rxdes));
+
+	netdev->stats.rx_dropped++;
+}
+
+static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftmac100_rxdes *rxdes;
+	struct sk_buff *skb;
+	struct page *page;
+	dma_addr_t map;
+	int length;
+
+	rxdes = ftmac100_rx_locate_first_segment(priv);
+	if (!rxdes)
+		return false;
+
+	if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) {
+		ftmac100_rx_drop_packet(priv);
+		return true;
+	}
+
+	/*
+	 * It is impossible to get multi-segment packets
+	 * because we always provide big enough receive buffers.
+	 */
+	if (unlikely(!ftmac100_rxdes_last_segment(rxdes)))
+		BUG();
+
+	/* start processing */
+	skb = netdev_alloc_skb_ip_align(netdev, 128);
+	if (unlikely(!skb)) {
+		if (net_ratelimit())
+			netdev_err(netdev, "rx skb alloc failed\n");
+
+		ftmac100_rx_drop_packet(priv);
+		return true;
+	}
+
+	if (unlikely(ftmac100_rxdes_multicast(rxdes)))
+		netdev->stats.multicast++;
+
+	map = ftmac100_rxdes_get_dma_addr(rxdes);
+	dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+
+	length = ftmac100_rxdes_frame_length(rxdes);
+	page = ftmac100_rxdes_get_page(rxdes);
+	skb_fill_page_desc(skb, 0, page, 0, length);
+	skb->len += length;
+	skb->data_len += length;
+	skb->truesize += length;
+	__pskb_pull_tail(skb, min(length, 64));
+
+	ftmac100_alloc_rx_page(priv, rxdes);
+
+	ftmac100_rx_pointer_advance(priv);
+
+	skb->protocol = eth_type_trans(skb, netdev);
+
+	netdev->stats.rx_packets++;
+	netdev->stats.rx_bytes += skb->len;
+
+	/* push packet to protocol stack */
+	netif_receive_skb(skb);
+
+	(*processed)++;
+	return true;
+}
+
+/******************************************************************************
+ * internal functions (transmit descriptor)
+ *****************************************************************************/
+static void ftmac100_txdes_reset(struct ftmac100_txdes *txdes)
+{
+	/* clear all except end of ring bit */
+	txdes->txdes0 = 0;
+	txdes->txdes1 &= cpu_to_le32(FTMAC100_TXDES1_EDOTR);
+	txdes->txdes2 = 0;
+	txdes->txdes3 = 0;
+}
+
+static bool ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes)
+{
+	return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN);
+}
+
+static void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes)
+{
+	/*
+	 * Make sure dma own bit will not be set before any other
+	 * descriptor fields.
+	 */
+	wmb();
+	txdes->txdes0 |= cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN);
+}
+
+static bool ftmac100_txdes_excessive_collision(struct ftmac100_txdes *txdes)
+{
+	return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_EXSCOL);
+}
+
+static bool ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes)
+{
+	return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_LATECOL);
+}
+
+static void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes)
+{
+	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_EDOTR);
+}
+
+static void ftmac100_txdes_set_first_segment(struct ftmac100_txdes *txdes)
+{
+	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_FTS);
+}
+
+static void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes)
+{
+	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_LTS);
+}
+
+static void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes)
+{
+	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXIC);
+}
+
+static void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes,
+					   unsigned int len)
+{
+	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXBUF_SIZE(len));
+}
+
+static void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes,
+					dma_addr_t addr)
+{
+	txdes->txdes2 = cpu_to_le32(addr);
+}
+
+static dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes)
+{
+	return le32_to_cpu(txdes->txdes2);
+}
+
+/*
+ * txdes3 is not used by hardware. We use it to keep track of socket buffer.
+ * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
+ */
+static void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes, struct sk_buff *skb)
+{
+	txdes->txdes3 = (unsigned int)skb;
+}
+
+static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes)
+{
+	return (struct sk_buff *)txdes->txdes3;
+}
+
+/******************************************************************************
+ * internal functions (transmit)
+ *****************************************************************************/
+static int ftmac100_next_tx_pointer(int pointer)
+{
+	return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
+}
+
+static void ftmac100_tx_pointer_advance(struct ftmac100 *priv)
+{
+	priv->tx_pointer = ftmac100_next_tx_pointer(priv->tx_pointer);
+}
+
+static void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv)
+{
+	priv->tx_clean_pointer = ftmac100_next_tx_pointer(priv->tx_clean_pointer);
+}
+
+static struct ftmac100_txdes *ftmac100_current_txdes(struct ftmac100 *priv)
+{
+	return &priv->descs->txdes[priv->tx_pointer];
+}
+
+static struct ftmac100_txdes *ftmac100_current_clean_txdes(struct ftmac100 *priv)
+{
+	return &priv->descs->txdes[priv->tx_clean_pointer];
+}
+
+static bool ftmac100_tx_complete_packet(struct ftmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftmac100_txdes *txdes;
+	struct sk_buff *skb;
+	dma_addr_t map;
+
+	if (priv->tx_pending == 0)
+		return false;
+
+	txdes = ftmac100_current_clean_txdes(priv);
+
+	if (ftmac100_txdes_owned_by_dma(txdes))
+		return false;
+
+	skb = ftmac100_txdes_get_skb(txdes);
+	map = ftmac100_txdes_get_dma_addr(txdes);
+
+	if (unlikely(ftmac100_txdes_excessive_collision(txdes) ||
+		     ftmac100_txdes_late_collision(txdes))) {
+		/*
+		 * packet transmitted to ethernet lost due to late collision
+		 * or excessive collision
+		 */
+		netdev->stats.tx_aborted_errors++;
+	} else {
+		netdev->stats.tx_packets++;
+		netdev->stats.tx_bytes += skb->len;
+	}
+
+	dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
+	dev_kfree_skb(skb);
+
+	ftmac100_txdes_reset(txdes);
+
+	ftmac100_tx_clean_pointer_advance(priv);
+
+	spin_lock(&priv->tx_lock);
+	priv->tx_pending--;
+	spin_unlock(&priv->tx_lock);
+	netif_wake_queue(netdev);
+
+	return true;
+}
+
+static void ftmac100_tx_complete(struct ftmac100 *priv)
+{
+	while (ftmac100_tx_complete_packet(priv))
+		;
+}
+
+static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
+			 dma_addr_t map)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftmac100_txdes *txdes;
+	unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+
+	txdes = ftmac100_current_txdes(priv);
+	ftmac100_tx_pointer_advance(priv);
+
+	/* setup TX descriptor */
+	ftmac100_txdes_set_skb(txdes, skb);
+	ftmac100_txdes_set_dma_addr(txdes, map);
+
+	ftmac100_txdes_set_first_segment(txdes);
+	ftmac100_txdes_set_last_segment(txdes);
+	ftmac100_txdes_set_txint(txdes);
+	ftmac100_txdes_set_buffer_size(txdes, len);
+
+	spin_lock(&priv->tx_lock);
+	priv->tx_pending++;
+	if (priv->tx_pending == TX_QUEUE_ENTRIES)
+		netif_stop_queue(netdev);
+
+	/* start transmit */
+	ftmac100_txdes_set_dma_own(txdes);
+	spin_unlock(&priv->tx_lock);
+
+	ftmac100_txdma_start_polling(priv);
+	return NETDEV_TX_OK;
+}
+
+/******************************************************************************
+ * internal functions (buffer)
+ *****************************************************************************/
+static int ftmac100_alloc_rx_page(struct ftmac100 *priv, struct ftmac100_rxdes *rxdes)
+{
+	struct net_device *netdev = priv->netdev;
+	struct page *page;
+	dma_addr_t map;
+
+	page = alloc_page(GFP_KERNEL);
+	if (!page) {
+		if (net_ratelimit())
+			netdev_err(netdev, "failed to allocate rx page\n");
+		return -ENOMEM;
+	}
+
+	map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE);
+	if (unlikely(dma_mapping_error(priv->dev, map))) {
+		if (net_ratelimit())
+			netdev_err(netdev, "failed to map rx page\n");
+		__free_page(page);
+		return -ENOMEM;
+	}
+
+	ftmac100_rxdes_set_page(rxdes, page);
+	ftmac100_rxdes_set_dma_addr(rxdes, map);
+	ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE);
+	ftmac100_rxdes_set_dma_own(rxdes);
+	return 0;
+}
+
+static void ftmac100_free_buffers(struct ftmac100 *priv)
+{
+	int i;
+
+	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+		struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+		struct page *page = ftmac100_rxdes_get_page(rxdes);
+		dma_addr_t map = ftmac100_rxdes_get_dma_addr(rxdes);
+
+		if (!page)
+			continue;
+
+		dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+		__free_page(page);
+	}
+
+	for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
+		struct ftmac100_txdes *txdes = &priv->descs->txdes[i];
+		struct sk_buff *skb = ftmac100_txdes_get_skb(txdes);
+		dma_addr_t map = ftmac100_txdes_get_dma_addr(txdes);
+
+		if (!skb)
+			continue;
+
+		dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
+		dev_kfree_skb(skb);
+	}
+
+	dma_free_coherent(priv->dev, sizeof(struct ftmac100_descs),
+			  priv->descs, priv->descs_dma_addr);
+}
+
+static int ftmac100_alloc_buffers(struct ftmac100 *priv)
+{
+	int i;
+
+	priv->descs = dma_alloc_coherent(priv->dev, sizeof(struct ftmac100_descs),
+					 &priv->descs_dma_addr, GFP_KERNEL);
+	if (!priv->descs)
+		return -ENOMEM;
+
+	memset(priv->descs, 0, sizeof(struct ftmac100_descs));
+
+	/* initialize RX ring */
+	ftmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
+
+	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+		struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+
+		if (ftmac100_alloc_rx_page(priv, rxdes))
+			goto err;
+	}
+
+	/* initialize TX ring */
+	ftmac100_txdes_set_end_of_ring(&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
+	return 0;
+
+err:
+	ftmac100_free_buffers(priv);
+	return -ENOMEM;
+}
+
+/******************************************************************************
+ * struct mii_if_info functions
+ *****************************************************************************/
+static int ftmac100_mdio_read(struct net_device *netdev, int phy_id, int reg)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	unsigned int phycr;
+	int i;
+
+	phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
+		FTMAC100_PHYCR_REGAD(reg) |
+		FTMAC100_PHYCR_MIIRD;
+
+	iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
+
+	for (i = 0; i < 10; i++) {
+		phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
+
+		if ((phycr & FTMAC100_PHYCR_MIIRD) == 0)
+			return phycr & FTMAC100_PHYCR_MIIRDATA;
+
+		usleep_range(100, 1000);
+	}
+
+	netdev_err(netdev, "mdio read timed out\n");
+	return 0;
+}
+
+static void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg,
+				int data)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	unsigned int phycr;
+	int i;
+
+	phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
+		FTMAC100_PHYCR_REGAD(reg) |
+		FTMAC100_PHYCR_MIIWR;
+
+	data = FTMAC100_PHYWDATA_MIIWDATA(data);
+
+	iowrite32(data, priv->base + FTMAC100_OFFSET_PHYWDATA);
+	iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
+
+	for (i = 0; i < 10; i++) {
+		phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
+
+		if ((phycr & FTMAC100_PHYCR_MIIWR) == 0)
+			return;
+
+		usleep_range(100, 1000);
+	}
+
+	netdev_err(netdev, "mdio write timed out\n");
+}
+
+/******************************************************************************
+ * struct ethtool_ops functions
+ *****************************************************************************/
+static void ftmac100_get_drvinfo(struct net_device *netdev,
+				 struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	strcpy(info->bus_info, dev_name(&netdev->dev));
+}
+
+static int ftmac100_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return mii_ethtool_gset(&priv->mii, cmd);
+}
+
+static int ftmac100_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return mii_ethtool_sset(&priv->mii, cmd);
+}
+
+static int ftmac100_nway_reset(struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return mii_nway_restart(&priv->mii);
+}
+
+static u32 ftmac100_get_link(struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	return mii_link_ok(&priv->mii);
+}
+
+static const struct ethtool_ops ftmac100_ethtool_ops = {
+	.set_settings		= ftmac100_set_settings,
+	.get_settings		= ftmac100_get_settings,
+	.get_drvinfo		= ftmac100_get_drvinfo,
+	.nway_reset		= ftmac100_nway_reset,
+	.get_link		= ftmac100_get_link,
+};
+
+/******************************************************************************
+ * interrupt handler
+ *****************************************************************************/
+static irqreturn_t ftmac100_interrupt(int irq, void *dev_id)
+{
+	struct net_device *netdev = dev_id;
+	struct ftmac100 *priv = netdev_priv(netdev);
+
+	if (likely(netif_running(netdev))) {
+		/* Disable interrupts for polling */
+		ftmac100_disable_all_int(priv);
+		napi_schedule(&priv->napi);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/******************************************************************************
+ * struct napi_struct functions
+ *****************************************************************************/
+static int ftmac100_poll(struct napi_struct *napi, int budget)
+{
+	struct ftmac100 *priv = container_of(napi, struct ftmac100, napi);
+	struct net_device *netdev = priv->netdev;
+	unsigned int status;
+	bool completed = true;
+	int rx = 0;
+
+	status = ioread32(priv->base + FTMAC100_OFFSET_ISR);
+
+	if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) {
+		/*
+		 * FTMAC100_INT_RPKT_FINISH:
+		 *	RX DMA has received packets into RX buffer successfully
+		 *
+		 * FTMAC100_INT_NORXBUF:
+		 *	RX buffer unavailable
+		 */
+		bool retry;
+
+		do {
+			retry = ftmac100_rx_packet(priv, &rx);
+		} while (retry && rx < budget);
+
+		if (retry && rx == budget)
+			completed = false;
+	}
+
+	if (status & (FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST)) {
+		/*
+		 * FTMAC100_INT_XPKT_OK:
+		 *	packet transmitted to ethernet successfully
+		 *
+		 * FTMAC100_INT_XPKT_LOST:
+		 *	packet transmitted to ethernet lost due to late
+		 *	collision or excessive collision
+		 */
+		ftmac100_tx_complete(priv);
+	}
+
+	if (status & (FTMAC100_INT_NORXBUF | FTMAC100_INT_RPKT_LOST |
+		      FTMAC100_INT_AHB_ERR | FTMAC100_INT_PHYSTS_CHG)) {
+		if (net_ratelimit())
+			netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status,
+				    status & FTMAC100_INT_NORXBUF ? "NORXBUF " : "",
+				    status & FTMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "",
+				    status & FTMAC100_INT_AHB_ERR ? "AHB_ERR " : "",
+				    status & FTMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : "");
+
+		if (status & FTMAC100_INT_NORXBUF) {
+			/* RX buffer unavailable */
+			netdev->stats.rx_over_errors++;
+		}
+
+		if (status & FTMAC100_INT_RPKT_LOST) {
+			/* received packet lost due to RX FIFO full */
+			netdev->stats.rx_fifo_errors++;
+		}
+
+		if (status & FTMAC100_INT_PHYSTS_CHG) {
+			/* PHY link status change */
+			mii_check_link(&priv->mii);
+		}
+	}
+
+	if (completed) {
+		/* stop polling */
+		napi_complete(napi);
+		ftmac100_enable_all_int(priv);
+	}
+
+	return rx;
+}
+
+/******************************************************************************
+ * struct net_device_ops functions
+ *****************************************************************************/
+static int ftmac100_open(struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	int err;
+
+	err = ftmac100_alloc_buffers(priv);
+	if (err) {
+		netdev_err(netdev, "failed to allocate buffers\n");
+		goto err_alloc;
+	}
+
+	err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name, netdev);
+	if (err) {
+		netdev_err(netdev, "failed to request irq %d\n", priv->irq);
+		goto err_irq;
+	}
+
+	priv->rx_pointer = 0;
+	priv->tx_clean_pointer = 0;
+	priv->tx_pointer = 0;
+	priv->tx_pending = 0;
+
+	err = ftmac100_start_hw(priv);
+	if (err)
+		goto err_hw;
+
+	napi_enable(&priv->napi);
+	netif_start_queue(netdev);
+
+	ftmac100_enable_all_int(priv);
+
+	return 0;
+
+err_hw:
+	free_irq(priv->irq, netdev);
+err_irq:
+	ftmac100_free_buffers(priv);
+err_alloc:
+	return err;
+}
+
+static int ftmac100_stop(struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+
+	ftmac100_disable_all_int(priv);
+	netif_stop_queue(netdev);
+	napi_disable(&priv->napi);
+	ftmac100_stop_hw(priv);
+	free_irq(priv->irq, netdev);
+	ftmac100_free_buffers(priv);
+
+	return 0;
+}
+
+static int ftmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	dma_addr_t map;
+
+	if (unlikely(skb->len > MAX_PKT_SIZE)) {
+		if (net_ratelimit())
+			netdev_dbg(netdev, "tx packet too big\n");
+
+		netdev->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
+	if (unlikely(dma_mapping_error(priv->dev, map))) {
+		/* drop packet */
+		if (net_ratelimit())
+			netdev_err(netdev, "map socket buffer failed\n");
+
+		netdev->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	return ftmac100_xmit(priv, skb, map);
+}
+
+/* optional */
+static int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+	struct ftmac100 *priv = netdev_priv(netdev);
+	struct mii_ioctl_data *data = if_mii(ifr);
+
+	return generic_mii_ioctl(&priv->mii, data, cmd, NULL);
+}
+
+static const struct net_device_ops ftmac100_netdev_ops = {
+	.ndo_open		= ftmac100_open,
+	.ndo_stop		= ftmac100_stop,
+	.ndo_start_xmit		= ftmac100_hard_start_xmit,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= ftmac100_do_ioctl,
+};
+
+/******************************************************************************
+ * struct platform_driver functions
+ *****************************************************************************/
+static int ftmac100_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int irq;
+	struct net_device *netdev;
+	struct ftmac100 *priv;
+	int err;
+
+	if (!pdev)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	/* setup net_device */
+	netdev = alloc_etherdev(sizeof(*priv));
+	if (!netdev) {
+		err = -ENOMEM;
+		goto err_alloc_etherdev;
+	}
+
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+	SET_ETHTOOL_OPS(netdev, &ftmac100_ethtool_ops);
+	netdev->netdev_ops = &ftmac100_netdev_ops;
+
+	platform_set_drvdata(pdev, netdev);
+
+	/* setup private data */
+	priv = netdev_priv(netdev);
+	priv->netdev = netdev;
+	priv->dev = &pdev->dev;
+
+	spin_lock_init(&priv->tx_lock);
+
+	/* initialize NAPI */
+	netif_napi_add(netdev, &priv->napi, ftmac100_poll, 64);
+
+	/* map io memory */
+	priv->res = request_mem_region(res->start, resource_size(res),
+				       dev_name(&pdev->dev));
+	if (!priv->res) {
+		dev_err(&pdev->dev, "Could not reserve memory region\n");
+		err = -ENOMEM;
+		goto err_req_mem;
+	}
+
+	priv->base = ioremap(res->start, res->end - res->start);
+	if (!priv->base) {
+		dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
+		err = -EIO;
+		goto err_ioremap;
+	}
+
+	priv->irq = irq;
+
+	/* initialize struct mii_if_info */
+	priv->mii.phy_id	= 0;
+	priv->mii.phy_id_mask	= 0x1f;
+	priv->mii.reg_num_mask	= 0x1f;
+	priv->mii.dev		= netdev;
+	priv->mii.mdio_read	= ftmac100_mdio_read;
+	priv->mii.mdio_write	= ftmac100_mdio_write;
+
+	/* register network device */
+	err = register_netdev(netdev);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to register netdev\n");
+		goto err_register_netdev;
+	}
+
+	netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
+
+	if (!is_valid_ether_addr(netdev->dev_addr)) {
+		random_ether_addr(netdev->dev_addr);
+		netdev_info(netdev, "generated random MAC address %pM\n",
+			    netdev->dev_addr);
+	}
+
+	return 0;
+
+err_register_netdev:
+	iounmap(priv->base);
+err_ioremap:
+	release_resource(priv->res);
+err_req_mem:
+	netif_napi_del(&priv->napi);
+	platform_set_drvdata(pdev, NULL);
+	free_netdev(netdev);
+err_alloc_etherdev:
+	return err;
+}
+
+static int __exit ftmac100_remove(struct platform_device *pdev)
+{
+	struct net_device *netdev;
+	struct ftmac100 *priv;
+
+	netdev = platform_get_drvdata(pdev);
+	priv = netdev_priv(netdev);
+
+	unregister_netdev(netdev);
+
+	iounmap(priv->base);
+	release_resource(priv->res);
+
+	netif_napi_del(&priv->napi);
+	platform_set_drvdata(pdev, NULL);
+	free_netdev(netdev);
+	return 0;
+}
+
+static struct platform_driver ftmac100_driver = {
+	.probe		= ftmac100_probe,
+	.remove		= __exit_p(ftmac100_remove),
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+/******************************************************************************
+ * initialization / finalization
+ *****************************************************************************/
+static int __init ftmac100_init(void)
+{
+	pr_info("Loading version " DRV_VERSION " ...\n");
+	return platform_driver_register(&ftmac100_driver);
+}
+
+static void __exit ftmac100_exit(void)
+{
+	platform_driver_unregister(&ftmac100_driver);
+}
+
+module_init(ftmac100_init);
+module_exit(ftmac100_exit);
+
+MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
+MODULE_DESCRIPTION("FTMAC100 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ftmac100.h b/drivers/net/ftmac100.h
new file mode 100644
index 0000000..46a0c47
--- /dev/null
+++ b/drivers/net/ftmac100.h
@@ -0,0 +1,180 @@
+/*
+ * Faraday FTMAC100 10/100 Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __FTMAC100_H
+#define __FTMAC100_H
+
+#define	FTMAC100_OFFSET_ISR		0x00
+#define	FTMAC100_OFFSET_IMR		0x04
+#define	FTMAC100_OFFSET_MAC_MADR	0x08
+#define	FTMAC100_OFFSET_MAC_LADR	0x0c
+#define	FTMAC100_OFFSET_MAHT0		0x10
+#define	FTMAC100_OFFSET_MAHT1		0x14
+#define	FTMAC100_OFFSET_TXPD		0x18
+#define	FTMAC100_OFFSET_RXPD		0x1c
+#define	FTMAC100_OFFSET_TXR_BADR	0x20
+#define	FTMAC100_OFFSET_RXR_BADR	0x24
+#define	FTMAC100_OFFSET_ITC		0x28
+#define	FTMAC100_OFFSET_APTC		0x2c
+#define	FTMAC100_OFFSET_DBLAC		0x30
+#define	FTMAC100_OFFSET_MACCR		0x88
+#define	FTMAC100_OFFSET_MACSR		0x8c
+#define	FTMAC100_OFFSET_PHYCR		0x90
+#define	FTMAC100_OFFSET_PHYWDATA	0x94
+#define	FTMAC100_OFFSET_FCR		0x98
+#define	FTMAC100_OFFSET_BPR		0x9c
+#define	FTMAC100_OFFSET_TS		0xc4
+#define	FTMAC100_OFFSET_DMAFIFOS	0xc8
+#define	FTMAC100_OFFSET_TM		0xcc
+#define	FTMAC100_OFFSET_TX_MCOL_SCOL	0xd4
+#define	FTMAC100_OFFSET_RPF_AEP		0xd8
+#define	FTMAC100_OFFSET_XM_PG		0xdc
+#define	FTMAC100_OFFSET_RUNT_TLCC	0xe0
+#define	FTMAC100_OFFSET_CRCER_FTL	0xe4
+#define	FTMAC100_OFFSET_RLC_RCC		0xe8
+#define	FTMAC100_OFFSET_BROC		0xec
+#define	FTMAC100_OFFSET_MULCA		0xf0
+#define	FTMAC100_OFFSET_RP		0xf4
+#define	FTMAC100_OFFSET_XP		0xf8
+
+/*
+ * Interrupt status register & interrupt mask register
+ */
+#define	FTMAC100_INT_RPKT_FINISH	(1 << 0)
+#define	FTMAC100_INT_NORXBUF		(1 << 1)
+#define	FTMAC100_INT_XPKT_FINISH	(1 << 2)
+#define	FTMAC100_INT_NOTXBUF		(1 << 3)
+#define	FTMAC100_INT_XPKT_OK		(1 << 4)
+#define	FTMAC100_INT_XPKT_LOST		(1 << 5)
+#define	FTMAC100_INT_RPKT_SAV		(1 << 6)
+#define	FTMAC100_INT_RPKT_LOST		(1 << 7)
+#define	FTMAC100_INT_AHB_ERR		(1 << 8)
+#define	FTMAC100_INT_PHYSTS_CHG		(1 << 9)
+
+/*
+ * Interrupt timer control register
+ */
+#define FTMAC100_ITC_RXINT_CNT(x)	(((x) & 0xf) << 0)
+#define FTMAC100_ITC_RXINT_THR(x)	(((x) & 0x7) << 4)
+#define FTMAC100_ITC_RXINT_TIME_SEL	(1 << 7)
+#define FTMAC100_ITC_TXINT_CNT(x)	(((x) & 0xf) << 8)
+#define FTMAC100_ITC_TXINT_THR(x)	(((x) & 0x7) << 12)
+#define FTMAC100_ITC_TXINT_TIME_SEL	(1 << 15)
+
+/*
+ * Automatic polling timer control register
+ */
+#define	FTMAC100_APTC_RXPOLL_CNT(x)	(((x) & 0xf) << 0)
+#define	FTMAC100_APTC_RXPOLL_TIME_SEL	(1 << 4)
+#define	FTMAC100_APTC_TXPOLL_CNT(x)	(((x) & 0xf) << 8)
+#define	FTMAC100_APTC_TXPOLL_TIME_SEL	(1 << 12)
+
+/*
+ * DMA burst length and arbitration control register
+ */
+#define FTMAC100_DBLAC_INCR4_EN		(1 << 0)
+#define FTMAC100_DBLAC_INCR8_EN		(1 << 1)
+#define FTMAC100_DBLAC_INCR16_EN	(1 << 2)
+#define FTMAC100_DBLAC_RXFIFO_LTHR(x)	(((x) & 0x7) << 3)
+#define FTMAC100_DBLAC_RXFIFO_HTHR(x)	(((x) & 0x7) << 6)
+#define FTMAC100_DBLAC_RX_THR_EN	(1 << 9)
+
+/*
+ * MAC control register
+ */
+#define	FTMAC100_MACCR_XDMA_EN		(1 << 0)
+#define	FTMAC100_MACCR_RDMA_EN		(1 << 1)
+#define	FTMAC100_MACCR_SW_RST		(1 << 2)
+#define	FTMAC100_MACCR_LOOP_EN		(1 << 3)
+#define	FTMAC100_MACCR_CRC_DIS		(1 << 4)
+#define	FTMAC100_MACCR_XMT_EN		(1 << 5)
+#define	FTMAC100_MACCR_ENRX_IN_HALFTX	(1 << 6)
+#define	FTMAC100_MACCR_RCV_EN		(1 << 8)
+#define	FTMAC100_MACCR_HT_MULTI_EN	(1 << 9)
+#define	FTMAC100_MACCR_RX_RUNT		(1 << 10)
+#define	FTMAC100_MACCR_RX_FTL		(1 << 11)
+#define	FTMAC100_MACCR_RCV_ALL		(1 << 12)
+#define	FTMAC100_MACCR_CRC_APD		(1 << 14)
+#define	FTMAC100_MACCR_FULLDUP		(1 << 15)
+#define	FTMAC100_MACCR_RX_MULTIPKT	(1 << 16)
+#define	FTMAC100_MACCR_RX_BROADPKT	(1 << 17)
+
+/*
+ * PHY control register
+ */
+#define FTMAC100_PHYCR_MIIRDATA		0xffff
+#define FTMAC100_PHYCR_PHYAD(x)		(((x) & 0x1f) << 16)
+#define FTMAC100_PHYCR_REGAD(x)		(((x) & 0x1f) << 21)
+#define FTMAC100_PHYCR_MIIRD		(1 << 26)
+#define FTMAC100_PHYCR_MIIWR		(1 << 27)
+
+/*
+ * PHY write data register
+ */
+#define FTMAC100_PHYWDATA_MIIWDATA(x)	((x) & 0xffff)
+
+/*
+ * Transmit descriptor, aligned to 16 bytes
+ */
+struct ftmac100_txdes {
+	unsigned int	txdes0;
+	unsigned int	txdes1;
+	unsigned int	txdes2;	/* TXBUF_BADR */
+	unsigned int	txdes3;	/* not used by HW */
+} __attribute__ ((aligned(16)));
+
+#define	FTMAC100_TXDES0_TXPKT_LATECOL	(1 << 0)
+#define	FTMAC100_TXDES0_TXPKT_EXSCOL	(1 << 1)
+#define	FTMAC100_TXDES0_TXDMA_OWN	(1 << 31)
+
+#define	FTMAC100_TXDES1_TXBUF_SIZE(x)	((x) & 0x7ff)
+#define	FTMAC100_TXDES1_LTS		(1 << 27)
+#define	FTMAC100_TXDES1_FTS		(1 << 28)
+#define	FTMAC100_TXDES1_TX2FIC		(1 << 29)
+#define	FTMAC100_TXDES1_TXIC		(1 << 30)
+#define	FTMAC100_TXDES1_EDOTR		(1 << 31)
+
+/*
+ * Receive descriptor, aligned to 16 bytes
+ */
+struct ftmac100_rxdes {
+	unsigned int	rxdes0;
+	unsigned int	rxdes1;
+	unsigned int	rxdes2;	/* RXBUF_BADR */
+	unsigned int	rxdes3;	/* not used by HW */
+} __attribute__ ((aligned(16)));
+
+#define	FTMAC100_RXDES0_RFL		0x7ff
+#define	FTMAC100_RXDES0_MULTICAST	(1 << 16)
+#define	FTMAC100_RXDES0_BROADCAST	(1 << 17)
+#define	FTMAC100_RXDES0_RX_ERR		(1 << 18)
+#define	FTMAC100_RXDES0_CRC_ERR		(1 << 19)
+#define	FTMAC100_RXDES0_FTL		(1 << 20)
+#define	FTMAC100_RXDES0_RUNT		(1 << 21)
+#define	FTMAC100_RXDES0_RX_ODD_NB	(1 << 22)
+#define	FTMAC100_RXDES0_LRS		(1 << 28)
+#define	FTMAC100_RXDES0_FRS		(1 << 29)
+#define	FTMAC100_RXDES0_RXDMA_OWN	(1 << 31)
+
+#define	FTMAC100_RXDES1_RXBUF_SIZE(x)	((x) & 0x7ff)
+#define	FTMAC100_RXDES1_EDORR		(1 << 31)
+
+#endif /* __FTMAC100_H */
-- 
1.6.3.3


^ permalink raw reply related

* Re: txqueuelen has wrong units; should be time
From: Albert Cahalan @ 2011-03-01  6:54 UTC (permalink / raw)
  To: David Miller
  Cc: johnwheffner, linville, eric.dumazet, jussi.kivilinna, swmike,
	linux-kernel, netdev
In-Reply-To: <20110228.201852.193726064.davem@davemloft.net>

On Mon, Feb 28, 2011 at 11:18 PM, David Miller <davem@davemloft.net> wrote:
> From: Albert Cahalan <acahalan@gmail.com>
> Date: Mon, 28 Feb 2011 23:11:13 -0500
>
>> It sounds like you need a callback or similar, so that TCP can be
>> informed later that the drop has occurred.
>
> By that point we could have already sent an entire RTT's worth
> of data, or more.
>
> It needs to be synchronous, otherwise performance suffers.

Ouch. OTOH, the current situation: performance suffers.

In case it makes you feel any better, consider two cases
where synchronous feedback is already impossible.
One is when you're routing packets that merely pass through.
The other is when some other box is doing that to you.
Either way, packets go bye-bye and nobody tells TCP.

^ permalink raw reply

* Re: Bug inkvm_set_irq
From: Michael S. Tsirkin @ 2011-03-01  7:03 UTC (permalink / raw)
  To: Jean-Philippe Menil; +Cc: netdev, kvm, virtualization
In-Reply-To: <4D6C22E8.2020007@univ-nantes.fr>

On Mon, Feb 28, 2011 at 11:34:16PM +0100, Jean-Philippe Menil wrote:
> Hi,
> 
> here is another trace with kvm.ko compiled with debug flags.
> 
> the bug:
> [12099.503414] BUG: unable to handle kernel paging request at
> 000000000b6635e9
> [12099.503462] IP: [<ffffffffa03ee877>] kvm_set_irq+0x37/0x140 [kvm]
> [12099.503521] PGD 45d8d2067 PUD 45d58e067 PMD 0
> [12099.503560] Oops: 0000 [#1] SMP
> [12099.503591] last sysfs file:
> /sys/devices/system/cpu/cpu11/cache/index2/shared_cpu_map
> [12099.503641] CPU 0
> [12099.503648] Modules linked in: netconsole configfs vhost_net
> macvtap macvlan tun veth powernow_k8 mperf cpufreq_userspace
> cpufreq_stats cpufreq_powersave cpufreq_ondemand freq_table
> cpufreq_conservative fuse xt_physdev ip6t_LOG ip6table_filter
> ip6_tables ipt_LOG xt_multiport xt_limit xt_tcpudp xt_state
> iptable_filter ip_tables x_tables nf_conntrack_tftp nf_conntrack_ftp
> nf_conntrack_ipv4 nf_defrag_ipv4 8021q bridge stp ext2 mbcache
> dm_round_robin dm_multipath nf_conntrack_ipv6 nf_conntrack
> nf_defrag_ipv6 kvm_amd kvm ipv6 snd_pcm snd_timer snd soundcore
> snd_page_alloc shpchp pci_hotplug tpm_tis i2c_nforce2 tpm i2c_core
> pcspkr evdev psmouse joydev tpm_bios processor ghes dcdbas hed
> button serio_raw thermal_sys xfs exportfs dm_mod sg sr_mod cdrom
> usbhid hid usb_storage ses sd_mod enclosure megaraid_sas ohci_hcd
> lpfc scsi_transport_fc bnx2 scsi_tgt scsi_mod ehci_hcd [last
> unloaded: scsi_wait_scan]
> [12099.504277]
> [12099.504302] Pid: 1742, comm: kworker/0:2 Not tainted
> 2.6.37.2-dsiun-110105+ #2 Dell Inc. PowerEdge M605/0K543T
> [12099.504373] RIP: 0010:[<ffffffffa03ee877>]  [<ffffffffa03ee877>]
> kvm_set_irq+0x37/0x140 [kvm]
> [12099.504444] RSP: 0018:ffff88045e013d00  EFLAGS: 00010246
> [12099.504474] RAX: 000000000b6634c1 RBX: 0000000000000018 RCX:
> 0000000000000001
> [12099.504508] RDX: 0000000000000000 RSI: 0000000000000000 RDI:
> ffff880419b600c0
> [12099.504541] RBP: ffff88045e013dd0 R08: ffff88045e012000 R09:
> 0000000000000000
> [12099.504575] R10: 0000000000000000 R11: 00000000ffffffff R12:
> ffff880419b600c0
> [12099.504609] R13: ffff880419b600c0 R14: ffffffffa03efaa0 R15:
> 0000000000000001
> [12099.504643] FS:  00007f3abaa05710(0000) GS:ffff88007f800000(0000)
> knlGS:0000000000000000
> [12099.504693] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
> [12099.504724] CR2: 000000000b6635e9 CR3: 000000045e2bc000 CR4:
> 00000000000006f0
> [12099.504757] DR0: 0000000000000000 DR1: 0000000000000000 DR2:
> 0000000000000000
> [12099.504791] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7:
> 0000000000000400
> [12099.504825] Process kworker/0:2 (pid: 1742, threadinfo
> ffkvm_set_irqff88045e012000, task ffff88045ffb0d60)
> [12099.504874] Stack:
> [12099.504897]  00000000000119c0 00000000000119c0 00000000000119c0
> ffff88045ffb0d60
> [12099.504953]  ffff88045ffb1010 ffff88045e013fd8 ffff88045ffb1018
> ffff88045e012010
> [12099.505009]  00000000000119c0 ffff88045e013fd8 00000000000119c0
> 00000000000119c0
> [12099.505065] Call Trace:
> [12099.505099]  [<ffffffff813818ce>] ? common_interrupt+0xe/0x13
> [12099.505145]  [<ffffffffa03efaa0>] ? irqfd_inject+0x0/0x50 [kvm]
> [12099.505145]  [<ffffffffa03efaca>] irqfd_inject+0x2a/0x50 [kvm]
> [12099.505145]  [<ffffffff8106b7bb>] process_one_work+0x11b/0x450
> [12099.505145]  [<ffffffff8106bf37>] worker_thread+0x157/0x410
> [12099.505145]  [<ffffffff8103a569>] ? __wake_up_common+0x59/0x90
> [12099.505145]  [<ffffffff8106bde0>] ? worker_thread+0x0/0x410
> [12099.505145]  [<ffffffff8106f996>] kthread+0x96/0xa0
> [12099.505145]  [<ffffffff81003c64>] kernel_thread_helper+0x4/0x10
> [12099.505145]  [<ffffffff8106f900>] ? kthread+0x0/0xa0
> [12099.505145]  [<ffffffff81003c60>] ? kernel_thread_helper+0x0/0x10
> [12099.505145] Code: 55 49 89 fd 41 54 53 89 d3 48 81 ec a8 00 00 00
> 8b 15 a6 75 03 00 89 b5 3c ff ff ff 85 d2 0f 85 d5 00 00 00 49 8b 85
> 58 24 00 00 <3b> 98 28 01 00 00 73 61 89 db 48 8b 84 d8 30 01 00 00
> 48 85 c0
> [12099.505145] RIP  [<ffffffffa03ee877>] kvm_set_irq+0x37/0x140 [kvm]
> [12099.505145]  RSP <ffff88045e013d00>
> [12099.505145] CR2: 000000000b6635e9
> 
> 
> markup_oops result:
> 
> root@ayrshire:~# cat bug.txt | perl markup_oops.pl -m
> /lib/modules/2.6.37.2-dsiun-110105+/kernel/arch/x86/kvm/kvm.ko
> /boot/vmlinuz-2.6.37.2-dsiun-110105+
> vmaoffset = 18446744072103034880 ffffffffa03ee841:	48 89 e5   	mov
> %rsp,%rbp
>  ffffffffa03ee844:	41 57                	push   %r15
>  ffffffffa03ee846:	41 89 cf             	mov    %ecx,%r15d  |  %r15
> => 1  %ecx = 1
>  ffffffffa03ee849:	41 56                	push   %r14        |  %r14
> => ffffffffa03efaa0
>  ffffffffa03ee84b:	41 55                	push   %r13
>  ffffffffa03ee84d:	49 89 fd             	mov    %rdi,%r13   |  %edi
> = ffff880419b600c0  %r13 => ffff880419b600c0
>  ffffffffa03ee850:	41 54                	push   %r12        |  %r12
> => ffff880419b600c0
>  ffffffffa03ee852:	53                   	push   %rbx
>  ffffffffa03ee853:	89 d3                	mov    %edx,%ebx   |  %ebx => 18
>  ffffffffa03ee855:	48 81 ec a8 00 00 00 	sub    $0xa8,%rsp
>  ffffffffa03ee85c:	8b 15 00 00 00 00    	mov    0x0(%rip),%edx
> # ffffffffa03ee862 <kvm_set_irq+0x22>
>  ffffffffa03ee862:	89 b5 3c ff ff ff    	mov    %esi,-0xc4(%rbp) |
> %esi = 0
>  ffffffffa03ee868:	85 d2                	test   %edx,%edx   |  %edx => 0
>  ffffffffa03ee86a:	0f 85 d5 00 00 00    	jne    ffffffffa03ee945
> <kvm_set_irq+0x105>
>  ffffffffa03ee870:	49 8b 85 58 24 00 00 	mov    0x2458(%r13),%rax |
> %eax => b6634c1  %r13 = ffff880419b600c0
> *ffffffffa03ee877:	3b 98 28 01 00 00    	cmp    0x128(%rax),%ebx |
> %eax = b6634c1  %ebx = 18 <--- faulting instruction
>  ffffffffa03ee87d:	73 61                	jae    ffffffffa03ee8e0
> <kvm_set_irq+0xa0>
>  ffffffffa03ee87f:	89 db                	mov    %ebx,%ebx
>  ffffffffa03ee881:	48 8b 84 d8 30 01 00 	mov    0x130(%rax,%rbx,8),%rax
>  ffffffffa03ee888:	00
>  ffffffffa03ee889:	48 85 c0             	test   %rax,%rax
>  ffffffffa03ee88c:	74 52                	je     ffffffffa03ee8e0
> <kvm_set_irq+0xa0>
>  ffffffffa03ee88e:	48 8d 95 40 ff ff ff 	lea    -0xc0(%rbp),%rdx
>  ffffffffa03ee895:	31 db                	xor    %ebx,%ebx
>  ffffffffa03ee897:	48 8b 08             	mov    (%rax),%rcx
>  ffffffffa03ee89a:	83 c3 01             	add    $0x1,%ebx
>  ffffffffa03ee89d:	0f 18 09             	prefetcht0 (%rcx)
>  ffffffffa03ee8a0:	48 8b 48 e0          	mov    -0x20(%rax),%rcx
>  ffffffffa03ee8a4:	48 89 0a             	mov    %rcx,(%rdx)
>  ffffffffa03ee8a7:	48 8b 48 e8          	mov    -0x18(%rax),%rcx
>  ffffffffa03ee8ab:	48 89 4a 08          	mov    %rcx,0x8(%rdx)
>  ffffffffa03ee8af:	48 8b 48 f0          	mov    -0x10(%rax),%rcx
>  ffffffffa03ee8b3:	48 89 4a 10          	mov    %rcx,0x10(%rdx)
>  ffffffffa03ee8b7:	48 8b 48 f8          	mov    -0x8(%rax),%rcx
>  ffffffffa03ee8bb:	48 89 4a 18          	mov    %rcx,0x18(%rdx)
>  ffffffffa03ee8bf:	48 8b 08             	mov    (%rax),%rcx
> 
> The relvant part of objdump for kvm_set_irq:
> root@ayrshire:~# objdump -ldS
> /lib/modules/2.6.37.2-dsiun-110105+/kernel/arch/x86/kvm/kvm.ko >
> dump.txt
> 
> 0000000000006840 <kvm_set_irq>:
> kvm_set_irq():
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:148
>     6840:       55                      push   %rbp
>     6841:       48 89 e5                mov    %rsp,%rbp
>     6844:       41 57                   push   %r15
>     6846:       41 89 cf                mov    %ecx,%r15d
>     6849:       41 56                   push   %r14
>     684b:       41 55                   push   %r13
>     684d:       49 89 fd                mov    %rdi,%r13
>     6850:       41 54                   push   %r12
>     6852:       53                      push   %rbx
>     6853:       89 d3                   mov    %edx,%ebx
>     6855:       48 81 ec a8 00 00 00    sub    $0xa8,%rsp
> trace_kvm_set_irq():
> /usr/src/GIT/linux-2.6-stable/include/trace/events/kvm.h:10
>     685c:       8b 15 00 00 00 00       mov    0x0(%rip),%edx
> # 6862 <kvm_set_irq+0x22>
> kvm_set_irq():
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:148
>     6862:       89 b5 3c ff ff ff       mov    %esi,-0xc4(%rbp)
> trace_kvm_set_irq():
> /usr/src/GIT/linux-2.6-stable/include/trace/events/kvm.h:10
>     6868:       85 d2                   test   %edx,%edx
>     686a:       0f 85 d5 00 00 00       jne    6945 <kvm_set_irq+0x105>
> kvm_set_irq():
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:161
>     6870:       49 8b 85 58 24 00 00    mov    0x2458(%r13),%rax
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:162
>     6877:       3b 98 28 01 00 00       cmp    0x128(%rax),%ebx
>     687d:       73 61                   jae    68e0 <kvm_set_irq+0xa0>
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:163
>     687f:       89 db                   mov    %ebx,%ebx
>     6881:       48 8b 84 d8 30 01 00    mov    0x130(%rax,%rbx,8),%rax
>     6888:       00
>     6889:       48 85 c0                test   %rax,%rax
>     688c:       74 52                   je     68e0 <kvm_set_irq+0xa0>
>     688e:       48 8d 95 40 ff ff ff    lea    -0xc0(%rbp),%rdx
>     6895:       31 db                   xor    %ebx,%ebx
>     6897:       48 8b 08                mov    (%rax),%rcx
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:164
>     689a:       83 c3 01                add    $0x1,%ebx
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:163
>     689d:       0f 18 09                prefetcht0 (%rcx)
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:164
>     68a0:       48 8b 48 e0             mov    -0x20(%rax),%rcx
>     68a4:       48 89 0a                mov    %rcx,(%rdx)
>     68a7:       48 8b 48 e8             mov    -0x18(%rax),%rcx
>     68ab:       48 89 4a 08             mov    %rcx,0x8(%rdx)
>     68af:       48 8b 48 f0             mov    -0x10(%rax),%rcx
>     68b3:       48 89 4a 10             mov    %rcx,0x10(%rdx)
>     68b7:       48 8b 48 f8             mov    -0x8(%rax),%rcx
>     68bb:       48 89 4a 18             mov    %rcx,0x18(%rdx)
>     68bf:       48 8b 08                mov    (%rax),%rcx
>     68c2:       48 89 4a 20             mov    %rcx,0x20(%rdx)
>     68c6:       48 8b 48 08             mov    0x8(%rax),%rcx
>     68ca:       48 89 4a 28             mov    %rcx,0x28(%rdx)
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:163
>     68ce:       48 8b 00                mov    (%rax),%rax
>     68d1:       48 83 c2 30             add    $0x30,%rdx
>     68d5:       48 85 c0                test   %rax,%rax
>     68d8:       75 bd                   jne    6897 <kvm_set_irq+0x57>
>     68da:       eb 06                   jmp    68e2 <kvm_set_irq+0xa2>
>     68dc:       0f 1f 40 00             nopl   0x0(%rax)
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:162
>     68e0:       31 db                   xor    %ebx,%ebx
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:169
>     68e2:       4c 8d b5 40 ff ff ff    lea    -0xc0(%rbp),%r14
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:162
>     68e9:       41 bc ff ff ff ff       mov    $0xffffffff,%r12d
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:167
>     68ef:       85 db                   test   %ebx,%ebx
>     68f1:       74 3d                   je     6930 <kvm_set_irq+0xf0>
>     68f3:       83 eb 01                sub    $0x1,%ebx
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:169
>     68f6:       44 89 f9                mov    %r15d,%ecx
>     68f9:       8b 95 3c ff ff ff       mov    -0xc4(%rbp),%edx
>     68ff:       48 63 c3                movslq %ebx,%rax
>     6902:       4c 89 ee                mov    %r13,%rsi
>     6905:       48 8d 04 40             lea    (%rax,%rax,2),%rax
>     6909:       48 c1 e0 04             shl    $0x4,%rax
>     690d:       49 8d 3c 06             lea    (%r14,%rax,1),%rdi
>     6911:       ff 94 05 48 ff ff ff    callq  *-0xb8(%rbp,%rax,1)
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:170
>     6918:       85 c0                   test   %eax,%eax
>     691a:       78 d3                   js     68ef <kvm_set_irq+0xaf>
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:173
>     691c:       45 85 e4                test   %r12d,%r12d
>     691f:       ba 00 00 00 00          mov    $0x0,%edx
>     6924:       44 0f 48 e2             cmovs  %edx,%r12d
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:167
>     6928:       85 db                   test   %ebx,%ebx
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:173
>     692a:       46 8d 24 20             lea    (%rax,%r12,1),%r12d
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:167
>     692e:       75 c3                   jne    68f3 <kvm_set_irq+0xb3>
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:177
>     6930:       48 81 c4 a8 00 00 00    add    $0xa8,%rsp
>     6937:       44 89 e0                mov    %r12d,%eax
>     693a:       5b                      pop    %rbx
>     693b:       41 5c                   pop    %r12
>     693d:       41 5d                   pop    %r13
>     693f:       41 5e                   pop    %r14
>     6941:       41 5f                   pop    %r15
>     6943:       c9                      leaveq
>     6944:       c3                      retq
> trace_kvm_set_irq():
> /usr/src/GIT/linux-2.6-stable/include/trace/events/kvm.h:10
>     6945:       4c 8b 25 00 00 00 00    mov    0x0(%rip),%r12
> # 694c <kvm_set_irq+0x10c>
>     694c:       4d 85 e4                test   %r12,%r12
>     694f:       0f 84 1b ff ff ff       je     6870 <kvm_set_irq+0x30>
>     6955:       49 8b 04 24             mov    (%r12),%rax
>     6959:       49 8b 7c 24 08          mov    0x8(%r12),%rdi
>     695e:       49 83 c4 10             add    $0x10,%r12
>     6962:       8b 8d 3c ff ff ff       mov    -0xc4(%rbp),%ecx
>     6968:       44 89 fa                mov    %r15d,%edx
>     696b:       89 de                   mov    %ebx,%esi
>     696d:       ff d0                   callq  *%rax
>     696f:       49 8b 04 24             mov    (%r12),%rax
>     6973:       48 85 c0                test   %rax,%rax
>     6976:       75 e1                   jne    6959 <kvm_set_irq+0x119>
>     6978:       e9 f3 fe ff ff          jmpq   6870 <kvm_set_irq+0x30>
> kvm_set_irq():
>     697d:       0f 1f 00                nopl   (%rax)
> 
> So, if i've read correctly, the offset is 0x6877 ?
> 
> root@ayrshire:~# addr2line -e
> /lib/modules/2.6.37.2-dsiun-110105+/kernel/arch/x86/kvm/kvm.ko
> 0x6877
> /usr/src/GIT/linux-2.6-stable/arch/x86/kvm/../../../virt/kvm/irq_comm.c:162
> 
> 
> Is it the correct way to analyse this?
> 
> Regards.

Yes.  So we have:

        irq_rt = rcu_dereference(kvm->irq_routing);

>  ffffffffa03ee870:	49 8b 85 58 24 00 00 	mov    0x2458(%r13),%rax |
> %eax => b6634c1  %r13 = ffff880419b600c0

        if (irq < irq_rt->nr_rt_entries)

> *ffffffffa03ee877:	3b 98 28 01 00 00    	cmp    0x128(%rax),%ebx |
> %eax = b6634c1  %ebx = 18 <--- faulting instruction

The problem then is that while the kvm pointer is
ffff880419b600c0 which looks sane,
the value we read from kvm->irq_routing is b6634c1 which
does not make sense. When we dereference that, kaboom.

Is the kvm pointer wrong or the memory corrupted?
Try printing the kvm pointer during
initialization, e.g. in kvm_vm_ioctl_create_vcpu,
then and compare to markup_oops.


-- 
MST

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox