Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v2] ipv4: netfilter: ipt_CLUSTERIP: fix buffer overflow
From: Changli Gao @ 2011-03-17 12:15 UTC (permalink / raw)
  To: Vasiliy Kulikov
  Cc: Patrick McHardy, linux-kernel, security, David S. Miller,
	Alexey Kuznetsov, James Morris, Hideaki YOSHIFUJI,
	netfilter-devel, netfilter, coreteam, netdev
In-Reply-To: <20110317113229.GA7710@albatros>

On Thu, Mar 17, 2011 at 7:32 PM, Vasiliy Kulikov <segoon@openwall.com> wrote:
> 'buffer' string is copied from userspace.  It is not checked whether it is
> zero terminated.  This may lead to overflow inside of simple_strtoul().
> Changli Gao suggested to copy not more than user supplied 'size' bytes.
>
> It was introduced before the git epoch.  Files "ipt_CLUSTERIP/*" are
> root writable only by default, however, on some setups permissions might be
> relaxed to e.g. network admin user.
>
> Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
Acked-by: Changli Gao <xiaosuo@gmail.com>


-- 
Regards,
Changli Gao(xiaosuo@gmail.com)
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 2/2] virtio_net: remove send completion interrupts and avoid TX queue overrun through packet drop
From: Rusty Russell @ 2011-03-17  5:10 UTC (permalink / raw)
  To: Shirley Ma, Michael S. Tsirkin; +Cc: David Miller, kvm, netdev
In-Reply-To: <1300320775.3255.34.camel@localhost.localdomain>

On Wed, 16 Mar 2011 17:12:55 -0700, Shirley Ma <mashirle@us.ibm.com> wrote:
> Signed-off-by: Shirley Ma <xma@us.ibm.com>

This is fascinating... and deeply weird.

OK, what's the difference between calling xmit_skb and ignoring failure,
and this patch which figures out it's going to fail before calling
xmit_skb?

ie. what if you *just* delete this:

> @@ -605,20 +620,6 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
>  	skb_orphan(skb);
>  	nf_reset(skb);
>  
> -	/* Apparently nice girls don't return TX_BUSY; stop the queue
> -	 * before it gets out of hand.  Naturally, this wastes entries. */
> -	if (capacity < 2+MAX_SKB_FRAGS) {
> -		netif_stop_queue(dev);
> -		if (unlikely(!virtqueue_enable_cb(vi->svq))) {
> -			/* More just got used, free them then recheck. */
> -			capacity += free_old_xmit_skbs(vi);
> -			if (capacity >= 2+MAX_SKB_FRAGS) {
> -				netif_start_queue(dev);
> -				virtqueue_disable_cb(vi->svq);
> -			}
> -		}
> -	}
> -
>  	return NETDEV_TX_OK;
>  }

Thanks!
Rusty.

^ permalink raw reply

* Re: [PATCH] econet: 4 byte infoleak to the network
From: Phil Blundell @ 2011-03-17 11:50 UTC (permalink / raw)
  To: Vasiliy Kulikov
  Cc: linux-kernel, security, David S. Miller, Eric Dumazet, Tejun Heo,
	Nelson Elhage, netdev
In-Reply-To: <1300362011-8653-1-git-send-email-segoon@openwall.com>

On Thu, 2011-03-17 at 14:40 +0300, Vasiliy Kulikov wrote:
> struct aunhdr has 4 padding bytes between 'pad' and 'handle' fields on
> x86_64.  These bytes are not initialized in the variable 'ah' before
> sending 'ah' to the network.  This leads to 4 bytes kernel stack
> infoleak.
> 
> This bug was introduced before the git epoch.
> 
> Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>

Well spotted.  Thanks for the patch.

Acked-by: Phil Blundell <philb@gnu.org>

p.

^ permalink raw reply

* Re: [PATCH] netfilter: xtables: fix reentrancy
From: Eric Dumazet @ 2011-03-17 11:45 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: David Miller, Patrick McHardy, Netfilter Developers, netdev
In-Reply-To: <Pine.LNX.4.64.1103171231220.21558@ask.diku.dk>

Le jeudi 17 mars 2011 à 12:36 +0100, Jesper Dangaard Brouer a écrit :
> Hi Eric,
> 
> How critial is this bug fix?
> 
> Should I apply this on my stable production kernels?
> (I'm preparing a 2.6.38 kernel for prod usage, eventhougt its just been 
> released, because I want your SFQ fixes...)

I would say the race is there, but probability must be very small, and
might need malicious iptables rules (with RETURN targets)

Especially in routers, where OUTPUT path is taken from softirq handler
anyway ;)

So dont worry at all, consider this as a cleanup :)


--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] econet: 4 byte infoleak to the network
From: Vasiliy Kulikov @ 2011-03-17 11:40 UTC (permalink / raw)
  To: linux-kernel
  Cc: security, David S. Miller, Phil Blundell, Eric Dumazet, Tejun Heo,
	Nelson Elhage, netdev

struct aunhdr has 4 padding bytes between 'pad' and 'handle' fields on
x86_64.  These bytes are not initialized in the variable 'ah' before
sending 'ah' to the network.  This leads to 4 bytes kernel stack
infoleak.

This bug was introduced before the git epoch.

Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
---
 net/econet/af_econet.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index 0c28263..116d3fd 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -435,10 +435,10 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
 		udpdest.sin_addr.s_addr = htonl(network | addr.station);
 	}
 
+	memset(&ah, 0, sizeof(ah));
 	ah.port = port;
 	ah.cb = cb & 0x7f;
 	ah.code = 2;		/* magic */
-	ah.pad = 0;
 
 	/* tack our header on the front of the iovec */
 	size = sizeof(struct aunhdr);
-- 
1.7.0.4

^ permalink raw reply related

* Re: [PATCH] netfilter: xtables: fix reentrancy
From: Jesper Dangaard Brouer @ 2011-03-17 11:36 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, Patrick McHardy, Netfilter Developers, netdev
In-Reply-To: <1300358140.3133.117.camel@edumazet-laptop>


Hi Eric,

How critial is this bug fix?

Should I apply this on my stable production kernels?
(I'm preparing a 2.6.38 kernel for prod usage, eventhougt its just been 
released, because I want your SFQ fixes...)


On Thu, 17 Mar 2011, Eric Dumazet wrote:

> While looking at it (and trying to only require preemption disabled
> instead of BH disabled), I believe stackptr management is not safe.
>
> I suggest following patch to make sure we restore *stackptr to origptr
> before enabling BH (or preemption later)
>
> Thanks
>
> [PATCH] netfilter: xtables: fix reentrancy
>
> commit f3c5c1bfd4308 (make ip_tables reentrant) introduced a race in
> handling the stackptr restore, at the end of ipt_do_table()
>
> We should do it before the call to xt_info_rdunlock_bh(), or we allow
> cpu preemption and another cpu overwrites stackptr of original one.
>
> A second fix is to change the underflow test to check the origptr value
> instead of 0 to detect underflow, or else we allow a jump from different
> hooks.
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
[...]

Hilsen
   Jesper Brouer

--
-------------------------------------------------------------------
MSc. Master of Computer Science
Dept. of Computer Science, University of Copenhagen
Author of http://www.adsl-optimizer.dk
-------------------------------------------------------------------

^ permalink raw reply

* [PATCH v2] ipv4: netfilter: ipt_CLUSTERIP: fix buffer overflow
From: Vasiliy Kulikov @ 2011-03-17 11:32 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Changli Gao, linux-kernel, security, David S. Miller,
	Alexey Kuznetsov, James Morris, Hideaki YOSHIFUJI,
	netfilter-devel, netfilter, coreteam, netdev
In-Reply-To: <4D7F5CAC.6070006@trash.net>

'buffer' string is copied from userspace.  It is not checked whether it is
zero terminated.  This may lead to overflow inside of simple_strtoul().
Changli Gao suggested to copy not more than user supplied 'size' bytes.

It was introduced before the git epoch.  Files "ipt_CLUSTERIP/*" are
root writable only by default, however, on some setups permissions might be
relaxed to e.g. network admin user.

Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
---
 net/ipv4/netfilter/ipt_CLUSTERIP.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
index 1e26a48..af7dec6 100644
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -669,8 +669,11 @@ static ssize_t clusterip_proc_write(struct file *file, const char __user *input,
 	char buffer[PROC_WRITELEN+1];
 	unsigned long nodenum;
 
-	if (copy_from_user(buffer, input, PROC_WRITELEN))
+	if (size > PROC_WRITELEN)
+		return -EIO;
+	if (copy_from_user(buffer, input, size))
 		return -EFAULT;
+	buffer[size] = 0;
 
 	if (*buffer == '+') {
 		nodenum = simple_strtoul(buffer+1, NULL, 10);
-- 
1.7.0.4

^ permalink raw reply related

* Re: [PATCH v2] net: add Faraday FTGMAC100 Gigabit Ethernet driver
From: Eric Dumazet @ 2011-03-17 11:30 UTC (permalink / raw)
  To: Po-Yu Chuang; +Cc: netdev, linux-kernel, ratbert
In-Reply-To: <AANLkTino6LzGTZF4fNSkV9pbAfnmAc8wJjk0L8HfHDiJ@mail.gmail.com>

Le jeudi 17 mars 2011 à 18:56 +0800, Po-Yu Chuang a écrit :
> Hi Eric,
> 
> On Thu, Mar 17, 2011 at 6:24 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> > Le jeudi 17 mars 2011 à 17:47 +0800, Po-Yu Chuang a écrit :
> >> From: Po-Yu Chuang <ratbert@faraday-tech.com>
> >>
> >> FTGMAC100 Ethernet Media Access Controller supports 10/100/1000 Mbps
> >> and MII/GMII.  This driver has been working on some ARM/NDS32 SoC's
> >> including Faraday A369 and Andes AG102.
> >>
> >> Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
> >> ---
> >
> > Did you tried GRO support ?
> 
> No, I don't understand how to do that yet. Let me do some study first.
> I'd really appreciate it if you could provide me some pointers about GRO. :-)
> Is there any document about it?

Should be easy and might help performance too.

Take a look at commit :

2edae08e5b75269855fef3c74fe4292c066e7c33 (r8169: add gro support)



^ permalink raw reply

* Re: sctp panic
From: richard -rw- weinberger @ 2011-03-17 11:20 UTC (permalink / raw)
  To: walter schloegl; +Cc: linux-kernel, netdev
In-Reply-To: <20110317104903.153050@gmx.net>

On Thu, Mar 17, 2011 at 11:49 AM, walter schloegl <sctp@muskelshirt.de> wrote:
>
> -------- Original-Nachricht --------
>> Datum: Thu, 17 Mar 2011 08:56:47 +0100
>> Von: richard -rw- weinberger <richard.weinberger@gmail.com>
>> An: sctp@muskelshirt.de
>> CC: linux-kernel@vger.kernel.org
>> Betreff: Re: sctp panic
>
>> On Thu, Mar 17, 2011 at 8:43 AM,  <sctp@muskelshirt.de> wrote:
>> > Hi
>> >
>> > when doing the actions below I get a panic.
>> >
>> > - echo 1 > /proc/sys/net/sctp/addip_enable
>> > - In a C-Programm
>> >    - socket(AF_INET,SOCK_STREAM,IPPROTO_SCTP);
>> >    - connect(....)     (with correct filled parameters)
>> >
>> > Its no process (waiting in accept) neccessary
>>
>> Can you show us the panic message and your C prorgam?
>> This would help reproducing the panic...
>
> The C-Code is:
>
> #include <sys/socket.h>
> #include <netinet/in.h>
> #include <netdb.h>
> #include <stdio.h>
> #include <errno.h>
> #include <unistd.h>
> #include "sctp.h"
>
>
> int
> main(int ac,char **av)
> {
>
>  int s;
>  char *remhost;
>  int port;
>  struct hostent *hp;
>  struct sockaddr_in server;
>
>  if (ac != 3) {
>      printf("usage: %s ip-addr port\n",av[0]);
>      exit(1);
>  }
>
>  remhost = av[1];
>  port = atoi(av[2]);
>
>
>  server.sin_port = htons(port);
>  server.sin_family = AF_INET;
>  hp = gethostbyname(remhost);
>  if (hp == 0)  {
>     fprintf(stderr,"unknown host %s\n",remhost);
>     exit(1);
>  }
>  memcpy((char *)&server.sin_addr,(char *)hp->h_addr,hp->h_length);
>
>  if ((s = socket(AF_INET,SOCK_STREAM,IPPROTO_SCTP)) < 0)  {
>      fprintf(stderr,"socket IPPROTO_SCTP proc %d  failed errno %d\n",getpid(),errno);
>      close(s);
>      exit(1);
>  }
>
>  if (connect(s,(struct sockaddr *)&server,sizeof server) < 0)        {
>         fprintf(stderr,"connect failed errno %d\n",errno);
>         close(s);
>         exit(1);
>  }
> }
>
> The crash says:
>
> crash> bt
> PID: 14646  TASK: ffff8801f40c7560  CPU: 9   COMMAND: "x"
>  #0 [ffff8801f6f5f6f0] machine_kexec at ffffffff8103697b
>  #1 [ffff8801f6f5f750] crash_kexec at ffffffff810b9078
>  #2 [ffff8801f6f5f820] oops_end at ffffffff814cc900
>  #3 [ffff8801f6f5f850] die at ffffffff8101733b
>  #4 [ffff8801f6f5f880] do_trap at ffffffff814cc1d4
>  #5 [ffff8801f6f5f8e0] do_invalid_op at ffffffff81014ee5
>  #6 [ffff8801f6f5f980] invalid_op at ffffffff81013f5b
>    [exception RIP: skb_over_panic+93]
>    RIP: ffffffff81404bdd  RSP: ffff8801f6f5fa38  RFLAGS: 00010296
>    RAX: 0000000000000083  RBX: 0000000000000040  RCX: 00000000000013f6
>    RDX: 0000000000000000  RSI: 0000000000000046  RDI: 0000000000000246
>    RBP: ffff8801f6f5fa58   R8: ffffffff818a3da0   R9: 0000000000000000
>    R10: 0000000000000001  R11: 0000000000000000  R12: ffff8801f4e11000
>    R13: 0000000000000004  R14: 000000000000003e  R15: 0000000000000001
>    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
>  #7 [ffff8801f6f5fa60] skb_put at ffffffff81405c1c
>  #8 [ffff8801f6f5fa80] sctp_addto_chunk at ffffffffa0503c23
>  #9 [ffff8801f6f5fad0] sctp_make_init at ffffffffa0506705
> #10 [ffff8801f6f5fba0] sctp_sf_do_prm_asoc at ffffffffa04f81d4
> #11 [ffff8801f6f5fbd0] sctp_do_sm at ffffffffa04fd381
> #12 [ffff8801f6f5fd60] sctp_primitive_ASSOCIATE at ffffffffa0513daf
> #13 [ffff8801f6f5fd80] __sctp_connect at ffffffffa0510f09
> #14 [ffff8801f6f5fe50] sctp_connect at ffffffffa0511248
> #15 [ffff8801f6f5fe80] inet_dgram_connect at ffffffff81471b6c
> #16 [ffff8801f6f5feb0] sys_connect at ffffffff813ff747
> #17 [ffff8801f6f5ff80] system_call_fastpath at ffffffff81013172
>    RIP: 0000003015ee2150  RSP: 00007fffea7bdce8  RFLAGS: 00010206
>    RAX: 000000000000002a  RBX: ffffffff81013172  RCX: 0000000000000000
>    RDX: 0000000000000010  RSI: 00007fffea7bdd00  RDI: 0000000000000003
>    RBP: 00007fffea7bdd40   R8: 0000000000759020   R9: ff00000000000000
>    R10: 00007fffea7bda70  R11: 0000000000000246  R12: 0000000000000000
>    R13: 00007fffea7bde20  R14: 00000000004006e0  R15: 0000000000000000
>    ORIG_RAX: 000000000000002a  CS: 0033  SS: 002b
>
>
> One more Info:
> When calling my programm with 127.0.0.1 as remote ip-addr I get a:
> connect failed errno 111
>
> When calling it with a "non local" address (and also no "accept process" running) I get the Panic.
>
>
>>
>> > br
>> > walter
>> > --
>> > GMX DSL Doppel-Flat ab 19,99 Euro/mtl.! Jetzt mit
>> > gratis Handy-Flat! http://portal.gmx.net/de/go/dsl
>> > --
>> > To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>> in
>> > the body of a message to majordomo@vger.kernel.org
>> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> > Please read the FAQ at  http://www.tux.org/lkml/
>> >
>>
>> --
>> Thanks,
>> //richard
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/
>
> --
> NEU: FreePhone - kostenlos mobil telefonieren und surfen!
> Jetzt informieren: http://www.gmx.net/de/go/freephone
>

CC'ing netdev

-- 
Thanks,
//richard

^ permalink raw reply

* Re: [PATCH v2] net: add Faraday FTGMAC100 Gigabit Ethernet driver
From: Po-Yu Chuang @ 2011-03-17 10:56 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev, linux-kernel, ratbert
In-Reply-To: <1300357457.3133.92.camel@edumazet-laptop>

Hi Eric,

On Thu, Mar 17, 2011 at 6:24 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> Le jeudi 17 mars 2011 à 17:47 +0800, Po-Yu Chuang a écrit :
>> From: Po-Yu Chuang <ratbert@faraday-tech.com>
>>
>> FTGMAC100 Ethernet Media Access Controller supports 10/100/1000 Mbps
>> and MII/GMII.  This driver has been working on some ARM/NDS32 SoC's
>> including Faraday A369 and Andes AG102.
>>
>> Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
>> ---
>
> Did you tried GRO support ?

No, I don't understand how to do that yet. Let me do some study first.
I'd really appreciate it if you could provide me some pointers about GRO. :-)
Is there any document about it?

best regards,
Po-Yu Chuang

^ permalink raw reply

* Re: [RFC] netfilter: get rid of atomic ops in fast path
From: Patrick McHardy @ 2011-03-17 10:39 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Netfilter Development Mailinglist, netdev, Jesper Dangaard Brouer
In-Reply-To: <1300302005.3202.23.camel@edumazet-laptop>

Am 16.03.2011 20:00, schrieb Eric Dumazet:
> We currently use a percpu spinlock to 'protect' rule bytes/packets
> counters, after various attempts to use RCU instead.
> 
> Lately we added a seqlock so that get_counters() can run without
> blocking BH or 'writers'. But we really use the seqcount in it.
> 
> Spinlock itself is only locked by the current cpu, so we can remove it
> completely.
> 
> This cleanups api, using correct 'writer' vs 'reader' semantic.
> 
> At replace time, the get_counters() call makes sure all cpus are done
> using the old table.
> 
> We could probably avoid blocking BH (we currently block them in xmit
> path), but thats a different topic ;)
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
> This is a POC patch (based on net-next-2.6), only handling ip_tables.
> ip6/arp/... need similar changes.

Thanks Eric, this looks good to me.

^ permalink raw reply

* [PATCH] netfilter: xtables: fix reentrancy
From: Eric Dumazet @ 2011-03-17 10:35 UTC (permalink / raw)
  To: David Miller, Jan Engelhardt; +Cc: kaber, netfilter-devel, netdev, hawk
In-Reply-To: <20110316.131648.15245216.davem@davemloft.net>

Le mercredi 16 mars 2011 à 13:16 -0700, David Miller a écrit :
> From: Eric Dumazet <eric.dumazet@gmail.com>
> Date: Wed, 16 Mar 2011 20:00:05 +0100
> 
> > We currently use a percpu spinlock to 'protect' rule bytes/packets
> > counters, after various attempts to use RCU instead.
> > 
> > Lately we added a seqlock so that get_counters() can run without
> > blocking BH or 'writers'. But we really use the seqcount in it.
> > 
> > Spinlock itself is only locked by the current cpu, so we can remove it
> > completely.
> > 
> > This cleanups api, using correct 'writer' vs 'reader' semantic.
> > 
> > At replace time, the get_counters() call makes sure all cpus are done
> > using the old table.
> > 
> > We could probably avoid blocking BH (we currently block them in xmit
> > path), but thats a different topic ;)
> > 
> > Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> 
> FWIW, I think this is a great idea.

I knew you would be interested :)

While looking at it (and trying to only require preemption disabled
instead of BH disabled), I believe stackptr management is not safe.

I suggest following patch to make sure we restore *stackptr to origptr
before enabling BH (or preemption later)

Thanks

[PATCH] netfilter: xtables: fix reentrancy

commit f3c5c1bfd4308 (make ip_tables reentrant) introduced a race in
handling the stackptr restore, at the end of ipt_do_table()

We should do it before the call to xt_info_rdunlock_bh(), or we allow
cpu preemption and another cpu overwrites stackptr of original one.

A second fix is to change the underflow test to check the origptr value
instead of 0 to detect underflow, or else we allow a jump from different
hooks.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Jan Engelhardt <jengelh@medozas.de>
Cc: Patrick McHardy <kaber@trash.net>
---
 net/ipv4/netfilter/ip_tables.c  |    4 ++--
 net/ipv6/netfilter/ip6_tables.c |    4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index b09ed0d..ffcea0d 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -387,7 +387,7 @@ ipt_do_table(struct sk_buff *skb,
 					verdict = (unsigned)(-v) - 1;
 					break;
 				}
-				if (*stackptr == 0) {
+				if (*stackptr <= origptr) {
 					e = get_entry(table_base,
 					    private->underflow[hook]);
 					pr_debug("Underflow (this is normal) "
@@ -427,10 +427,10 @@ ipt_do_table(struct sk_buff *skb,
 			/* Verdict */
 			break;
 	} while (!acpar.hotdrop);
-	xt_info_rdunlock_bh();
 	pr_debug("Exiting %s; resetting sp from %u to %u\n",
 		 __func__, *stackptr, origptr);
 	*stackptr = origptr;
+	xt_info_rdunlock_bh();
 #ifdef DEBUG_ALLOW_ALL
 	return NF_ACCEPT;
 #else
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index c9598a9..0b2af9b 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -410,7 +410,7 @@ ip6t_do_table(struct sk_buff *skb,
 					verdict = (unsigned)(-v) - 1;
 					break;
 				}
-				if (*stackptr == 0)
+				if (*stackptr <= origptr)
 					e = get_entry(table_base,
 					    private->underflow[hook]);
 				else
@@ -441,8 +441,8 @@ ip6t_do_table(struct sk_buff *skb,
 			break;
 	} while (!acpar.hotdrop);
 
-	xt_info_rdunlock_bh();
 	*stackptr = origptr;
+	xt_info_rdunlock_bh();
 
 #ifdef DEBUG_ALLOW_ALL
 	return NF_ACCEPT;


--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* SO_BINDTODEVICE inconsistency between IPv4 and IPv6
From: David Woodhouse @ 2011-03-17 10:29 UTC (permalink / raw)
  To: netdev; +Cc: Yuniverg, Michael, Yedvab, Nadav

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

We've discovered strange behaviour when we listen on in6addr_any and use
SO_BINDTODEVICE to bind to the lo device.

We can connect to any IPv4 address that is local to the machine, on any
interface. (This is true whether we listen on AF_INET6/in6addr_any and
accept IPv4 connections on the IPv6 socket, or whether we just listen on
AF_INET/INADDR_ANY).

The IPv6 behaviour is different — the only IPv6 address that we can
connect to is ::1.

See attached test case, which listens with SO_BINDTODEVICE as described.
Note that it needs to be run as root because SO_BINDTODEVICE is a
privileged operation.

Why this difference? Ideally, we want the Legacy IP behaviour to happen
for IPv6 too; we want local clients to be able to connect to *any* local
IP address to talk to our service, but we don't want to accept
connections from the outside.

[root@macbook dwmw2]# uname -a
Linux macbook.infradead.org 2.6.35.11-83.fc14.x86_64 #1 SMP Mon Feb 7 07:06:44 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux
[root@macbook dwmw2]# ./port -port  9999 &
Attempt to listening at port: 9999 ...successfully binded local
Block until interrupted by a signal (Ctrl+C or kill) ...
[1] 26839
[root@macbook dwmw2]# host macbook
macbook.infradead.org has address 90.155.92.212
macbook.infradead.org has IPv6 address 2001:8b0:10b:1:216:eaff:fe05:bbb8
[root@macbook dwmw2]# telnet 90.155.92.212 9999
Trying 90.155.92.212...
Connected to macbook.infradead.org (90.155.92.212).
Escape character is '^]'.
^]close

telnet> close
Connection closed.
[root@macbook dwmw2]# telnet 127.0.0.1 9999
Trying 127.0.0.1...
Connected to macbook.infradead.org (127.0.0.1).
Escape character is '^]'.
^]close

telnet> close
Connection closed.
[root@macbook dwmw2]# telnet ::1 9999
Trying ::1...
Connected to macbook.infradead.org (::1).
Escape character is '^]'.
^]close

telnet> close
Connection closed.
[root@macbook dwmw2]# telnet 2001:8b0:10b:1:216:eaff:fe05:bbb8 9999
Trying 2001:8b0:10b:1:216:eaff:fe05:bbb8...
telnet: connect to address 2001:8b0:10b:1:216:eaff:fe05:bbb8: Connection refused
telnet: Unable to connect to remote host: Connection refused
[root@macbook dwmw2]# ip -6 route list table local
local ::1 via :: dev lo  proto none  metric 0  mtu 16436 rtt 10ms rttvar 10ms cwnd 3 advmss 16376 hoplimit 0
local 2001:8b0:10b:1:216:eaff:fe05:bbb8 via :: dev lo  proto none  metric 0  mtu 16436 advmss 16376 hoplimit 0
local fe80::216:eaff:fe05:bbb8 via :: dev lo  proto none  metric 0  mtu 16436 advmss 16376 hoplimit 0
ff02::1 via ff02::1 dev wlan0  metric 0 
    cache  mtu 1500 advmss 1440 hoplimit 0
ff00::/8 dev wlan0  metric 256  mtu 1500 advmss 1440 hoplimit 0
[root@macbook dwmw2]# ip addr list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN qlen 1000
    link/ether 00:22:41:2d:31:0a brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 00:16:ea:05:bb:b8 brd ff:ff:ff:ff:ff:ff
    inet 90.155.92.212/26 brd 90.155.92.255 scope global wlan0
    inet6 2001:8b0:10b:1:216:eaff:fe05:bbb8/64 scope global dynamic 
       valid_lft 294sec preferred_lft 114sec
    inet6 fe80::216:eaff:fe05:bbb8/64 scope link 
       valid_lft forever preferred_lft forever
4: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    link/ether b2:5e:9d:17:67:ce brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
5: virbr1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 
    link/ether 16:7e:11:12:43:09 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.1/24 brd 192.168.100.255 scope global virbr1
7: vpn0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1266 qdisc pfifo_fast state UNKNOWN qlen 500
    link/none 
    inet 10.255.16.41/22 brd 10.255.19.255 scope global vpn0

-- 
dwmw2

[-- Attachment #2: port.c --]
[-- Type: text/x-csrc, Size: 4462 bytes --]

/*! @file  port.c
 *  @brief Test socket with/without setsockopt(..,SO_BINDTODEVICE,..)
 *
 * 
 * 
 * @b Usage: ./port -port portNum
 *
 *
 * @b Author/Date: Michael Yuniverg, 17 March 2011
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <ifaddrs.h>
#include <string.h>
#include <signal.h>
#include <sys/file.h>
#include <net/if.h>


static char *pid_file = NULL;
static int s = -1;
struct addrinfo *paddrp = NULL;

void exitcleanup()
{
	if (pid_file) {
		unlink(pid_file);
	}
	if (paddrp) {
		freeaddrinfo(paddrp);
	}
}

void terminationHandler(int signum, siginfo_t *sinfo, void *dummy)
{
    //printf("Received termination signal:%d   Err:(%d) Code:(%d)\n", signum, sinfo->si_errno, sinfo->si_code);
	if (-1 != s) {
		close(s);
	}
	exit(EXIT_SUCCESS);
}

void setTerminationHandler()
{
	int sigSet = 0;
	// Termination signal handler.
	struct sigaction terminateAction;
	// Set up the structure to specify the termination action.
	terminateAction.sa_sigaction = terminationHandler;
	sigemptyset(&terminateAction.sa_mask);
	terminateAction.sa_flags = SA_SIGINFO;
	sigSet &= sigaction(SIGTERM, &terminateAction, NULL);
	sigSet &= sigaction(SIGQUIT, &terminateAction, NULL);
	sigSet &= sigaction(SIGINT,  &terminateAction, NULL);
	sigSet &= sigaction(SIGHUP,  &terminateAction, NULL);
	sigSet &= sigaction(SIGPIPE, &terminateAction, NULL);
	sigSet &= sigaction(SIGALRM, &terminateAction, NULL);
	sigSet &= sigaction(SIGUSR1, &terminateAction, NULL);
	sigSet &= sigaction(SIGUSR2, &terminateAction, NULL);

	if (sigSet != 0) {
		printf("Failed to register terminate signal handler\n");
	}
}

void Usage() {
        printf ("usage: port [options]\n");
        printf ("   -port <port_num> : indicate which port to listen at\n");
}

int main(int argc, char* argv[])
{
        char *port = NULL;
		struct addrinfo hints, *paddr;
        int backlog = 5;
        int i, err;
	int res = -1;

        // ---------------- Parse Options -----------------------------

        if (argc < 2) {
                Usage();
                return -1;
        }

        for (i = 1; i < argc; i++) {
                if (!strcmp(argv[i], "-port")) {
                        port = argv[++i];
                } else if (!strcmp(argv[i], "-file")) {
                        pid_file = argv[++i];
                } else {
                        Usage();
                        return -1;
                }
        }

        if (port == NULL) {
                Usage();
                return -1;
        }

        
		setTerminationHandler();

       
        atexit(exitcleanup);

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = PF_INET6;
        hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = 0;
#ifdef AI_NUMERICSERV
	hints.ai_flags   |= AI_NUMERICSERV;
#endif
        hints.ai_flags |= AI_PASSIVE;

	if (getaddrinfo(NULL, port, &hints, &paddrp) != 0) {
		goto error;
	}
	for (paddr = paddrp; paddr != NULL; paddr = paddr->ai_next) {

                struct sockaddr_in *sinp;
                sinp = (struct sockaddr_in *)paddr->ai_addr;
                printf ("Attempt to listening at port: %d ...", ntohs(sinp->sin_port));
                 
                s= socket(paddr->ai_family, paddr->ai_socktype, paddr->ai_protocol);
                if (s < 0) {
                        goto error;
                }
				
				/* the problematic system call */
				const char *device = "lo";
				struct ifreq ifr;
				strcpy(ifr.ifr_name, device);
				if(setsockopt(s,SOL_SOCKET,SO_BINDTODEVICE, &ifr,sizeof(ifr)) == -1)
				{
				  //error = errno;
					perror("SO_BINDTODEVICE");
					close(s);
					return -1;
				}
				printf("successfully binded local\n");
				/* end of problematic system call */

				if (bind(s, paddr->ai_addr, paddr->ai_addrlen) == -1) {
                        close(s);
                        printf ("bind failed. Exiting\n");
                        return -1;
                }

                if (listen(s, backlog) == -1) {
                        close(s);
                        goto error;
                }

                printf ("Block until interrupted by a signal (Ctrl+C or kill) ...\n");
                select(1, NULL, NULL, NULL, NULL);
        }

        close(s);
        return 0;
error:
        //err = GetLastError();
        //printf ("FAIL (0x%x)\n", err);
        //DisplayError(err) ;
        return -1;
}

^ permalink raw reply

* Re: [PATCH v2] net: add Faraday FTGMAC100 Gigabit Ethernet driver
From: Eric Dumazet @ 2011-03-17 10:24 UTC (permalink / raw)
  To: Po-Yu Chuang; +Cc: netdev, linux-kernel, ratbert
In-Reply-To: <1300355238-1619-1-git-send-email-ratbert.chuang@gmail.com>

Le jeudi 17 mars 2011 à 17:47 +0800, Po-Yu Chuang a écrit :
> From: Po-Yu Chuang <ratbert@faraday-tech.com>
> 
> FTGMAC100 Ethernet Media Access Controller supports 10/100/1000 Mbps
> and MII/GMII.  This driver has been working on some ARM/NDS32 SoC's
> including Faraday A369 and Andes AG102.
> 
> Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
> ---

Did you tried GRO support ?

^ permalink raw reply

* Re: [PATCHv2 1/9] at91: provide macb clks with "pclk" and "hclk" name
From: Jamie Iles @ 2011-03-17 10:09 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Andrew Victor, nicolas.ferre, Jamie Iles, plagnioj,
	linux-arm-kernel, netdev
In-Reply-To: <20110317100010.GF29758@n2100.arm.linux.org.uk>

On Thu, Mar 17, 2011 at 10:00:10AM +0000, Russell King - ARM Linux wrote:
> On Thu, Mar 17, 2011 at 11:22:37AM +0200, Andrew Victor wrote:
> > hi Russell,
> > 
> > >> There is no reference to a "pclk" or "hclk" in the AT91 architecture.
> > >> So to avoid possible confusion, maybe create two "fake" clocks both
> > >> parented to "macb_clk", and add a comment they're only for
> > >> compatibility with the AVR32.
> > >
> > > It doesn't matter what's in the documentation.
> > >
> > > What matters more than conforming to documentation is keeping the drivers
> > > in a clean and maintainable state without throwing lots of ifdefs into
> > > them.
> > 
> > I'm not saying the drivers need ifdefs, they should request both
> > "pclk" and "hclk" as suggested.
> > 
> > What I was suggesting is the platform clock setup on AT91 as:
> >     macb_clk
> >         |
> >         +-- hclk
> >         +-- pclk
> > 
> > rather than:
> >     pclk
> >         |
> >         +-- hclk
> 
> And what I've been saying all along is to make pclk a _dummy_ clock on
> the platform it doesn't exist for, rather than making it related in some
> way to another clock given to the peripheral.

Ok, so just to summarize, before my patches, at91 provides "macb_clk", 
whereas avr32 provides "pclk" and "hclk".

I've renamed at91's "macb_clk" to "pclk" and added a fake, unrelated 
"hclk".  It was suggested that the fake "hclk" should be a child of 
"pclk" but you're saying to leave it as I have it right?

Jamie

^ permalink raw reply

* Re: [PATCHv2 1/9] at91: provide macb clks with "pclk" and "hclk" name
From: Russell King - ARM Linux @ 2011-03-17 10:00 UTC (permalink / raw)
  To: Andrew Victor
  Cc: nicolas.ferre, Jamie Iles, plagnioj, linux-arm-kernel, netdev
In-Reply-To: <AANLkTin9FH2orOaV-6feGDikCGvb3C-Q2CnRzp-Y2S4X@mail.gmail.com>

On Thu, Mar 17, 2011 at 11:22:37AM +0200, Andrew Victor wrote:
> hi Russell,
> 
> >> There is no reference to a "pclk" or "hclk" in the AT91 architecture.
> >> So to avoid possible confusion, maybe create two "fake" clocks both
> >> parented to "macb_clk", and add a comment they're only for
> >> compatibility with the AVR32.
> >
> > It doesn't matter what's in the documentation.
> >
> > What matters more than conforming to documentation is keeping the drivers
> > in a clean and maintainable state without throwing lots of ifdefs into
> > them.
> 
> I'm not saying the drivers need ifdefs, they should request both
> "pclk" and "hclk" as suggested.
> 
> What I was suggesting is the platform clock setup on AT91 as:
>     macb_clk
>         |
>         +-- hclk
>         +-- pclk
> 
> rather than:
>     pclk
>         |
>         +-- hclk

And what I've been saying all along is to make pclk a _dummy_ clock on
the platform it doesn't exist for, rather than making it related in some
way to another clock given to the peripheral.

^ permalink raw reply

* [PATCH v2] net: add Faraday FTGMAC100 Gigabit Ethernet driver
From: Po-Yu Chuang @ 2011-03-17  9:47 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel, ratbert, eric.dumazet
In-Reply-To: <1299744517-1896-1-git-send-email-ratbert.chuang@gmail.com>

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

FTGMAC100 Ethernet Media Access Controller supports 10/100/1000 Mbps
and MII/GMII.  This driver has been working on some ARM/NDS32 SoC's
including Faraday A369 and Andes AG102.

Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
---
v2:
fix checkpatch.pl found error
use GFP_ATOMIC allocations where needed

 drivers/net/Kconfig     |    9 +
 drivers/net/Makefile    |    1 +
 drivers/net/ftgmac100.c | 1365 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/ftgmac100.h |  246 +++++++++
 4 files changed, 1621 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/ftgmac100.c
 create mode 100644 drivers/net/ftgmac100.h

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 6b12274..b9da914 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2560,6 +2560,15 @@ config PCH_GBE
 	  to Gigabit Ethernet.
 	  This driver enables Gigabit Ethernet function.
 
+config FTGMAC100
+	tristate "Faraday FTGMAC100 Gigabit Ethernet support"
+	depends on ARM
+	select PHYLIB
+	help
+	  This driver supports the FTGMAC100 Gigabit Ethernet controller
+	  from Faraday. It is used on Faraday A369, Andes AG102 and some
+	  other ARM/NDS32 SoC's.
+
 endif # NETDEV_1000
 
 #
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 7c21711..a349b26 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_FTGMAC100) += ftgmac100.o
 obj-$(CONFIG_FTMAC100) += ftmac100.o
 
 obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
diff --git a/drivers/net/ftgmac100.c b/drivers/net/ftgmac100.c
new file mode 100644
index 0000000..5b01c5f
--- /dev/null
+++ b/drivers/net/ftgmac100.c
@@ -0,0 +1,1365 @@
+/*
+ * Faraday FTGMAC100 Gigabit 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/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <net/ip.h>
+
+#include "ftgmac100.h"
+
+#define DRV_NAME	"ftgmac100"
+#define DRV_VERSION	"0.6"
+
+#define RX_QUEUE_ENTRIES	256	/* must be power of 2 */
+#define TX_QUEUE_ENTRIES	512	/* must be power of 2 */
+
+#define MAX_PKT_SIZE		1518
+#define RX_BUF_SIZE		PAGE_SIZE	/* must be smaller than 0x3fff */
+
+/******************************************************************************
+ * private data
+ *****************************************************************************/
+struct ftgmac100_descs {
+	struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
+	struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES];
+};
+
+struct ftgmac100 {
+	struct resource *res;
+	void __iomem *base;
+	int irq;
+
+	struct ftgmac100_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_bus *mii_bus;
+	int phy_irq[PHY_MAX_ADDR];
+	struct phy_device *phydev;
+	int old_speed;
+};
+
+static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
+				   struct ftgmac100_rxdes *rxdes, gfp_t gfp);
+
+/******************************************************************************
+ * internal functions (hardware register access)
+ *****************************************************************************/
+#define INT_MASK_ALL_ENABLED	(FTGMAC100_INT_RPKT_LOST	| \
+				 FTGMAC100_INT_XPKT_ETH		| \
+				 FTGMAC100_INT_XPKT_LOST	| \
+				 FTGMAC100_INT_AHB_ERR		| \
+				 FTGMAC100_INT_PHYSTS_CHG	| \
+				 FTGMAC100_INT_RPKT_BUF		| \
+				 FTGMAC100_INT_NO_RXBUF)
+
+static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
+{
+	iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
+}
+
+static void ftgmac100_set_rx_buffer_size(struct ftgmac100 *priv,
+		unsigned int size)
+{
+	size = FTGMAC100_RBSR_SIZE(size);
+	iowrite32(size, priv->base + FTGMAC100_OFFSET_RBSR);
+}
+
+static void ftgmac100_set_normal_prio_tx_ring_base(struct ftgmac100 *priv,
+						   dma_addr_t addr)
+{
+	iowrite32(addr, priv->base + FTGMAC100_OFFSET_NPTXR_BADR);
+}
+
+static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv)
+{
+	iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD);
+}
+
+static int ftgmac100_reset_hw(struct ftgmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	int i;
+
+	/* NOTE: reset clears all registers */
+	iowrite32(FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR);
+	for (i = 0; i < 5; i++) {
+		unsigned int maccr;
+
+		maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
+		if (!(maccr & FTGMAC100_MACCR_SW_RST))
+			return 0;
+
+		usleep_range(1000, 10000);
+	}
+
+	netdev_err(netdev, "software reset failed\n");
+	return -EIO;
+}
+
+static void ftgmac100_set_mac(struct ftgmac100 *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 + FTGMAC100_OFFSET_MAC_MADR);
+	iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
+}
+
+static void ftgmac100_init_hw(struct ftgmac100 *priv)
+{
+	/* setup ring buffer base registers */
+	ftgmac100_set_rx_ring_base(priv,
+				   priv->descs_dma_addr +
+				   offsetof(struct ftgmac100_descs, rxdes));
+	ftgmac100_set_normal_prio_tx_ring_base(priv,
+					       priv->descs_dma_addr +
+					       offsetof(struct ftgmac100_descs, txdes));
+
+	ftgmac100_set_rx_buffer_size(priv, RX_BUF_SIZE);
+
+	iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), priv->base + FTGMAC100_OFFSET_APTC);
+
+	ftgmac100_set_mac(priv, priv->netdev->dev_addr);
+}
+
+#define MACCR_ENABLE_ALL	(FTGMAC100_MACCR_TXDMA_EN	| \
+				 FTGMAC100_MACCR_RXDMA_EN	| \
+				 FTGMAC100_MACCR_TXMAC_EN	| \
+				 FTGMAC100_MACCR_RXMAC_EN	| \
+				 FTGMAC100_MACCR_FULLDUP	| \
+				 FTGMAC100_MACCR_CRC_APD	| \
+				 FTGMAC100_MACCR_RX_RUNT	| \
+				 FTGMAC100_MACCR_RX_BROADPKT)
+
+static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed)
+{
+	int maccr = MACCR_ENABLE_ALL;
+
+	switch (speed) {
+	default:
+	case 10:
+		break;
+
+	case 100:
+		maccr |= FTGMAC100_MACCR_FAST_MODE;
+		break;
+
+	case 1000:
+		maccr |= FTGMAC100_MACCR_GIGA_MODE;
+		break;
+	}
+
+	iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
+}
+
+static void ftgmac100_stop_hw(struct ftgmac100 *priv)
+{
+	iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
+}
+
+/******************************************************************************
+ * internal functions (receive descriptor)
+ *****************************************************************************/
+static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS);
+}
+
+static bool ftgmac100_rxdes_last_segment(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_LRS);
+}
+
+static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY);
+}
+
+static void ftgmac100_rxdes_set_dma_own(struct ftgmac100_rxdes *rxdes)
+{
+	/* clear status bits */
+	rxdes->rxdes0 &= cpu_to_le32(FTGMAC100_RXDES0_EDORR);
+}
+
+static bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ERR);
+}
+
+static bool ftgmac100_rxdes_crc_error(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_CRC_ERR);
+}
+
+static bool ftgmac100_rxdes_frame_too_long(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FTL);
+}
+
+static bool ftgmac100_rxdes_runt(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RUNT);
+}
+
+static bool ftgmac100_rxdes_odd_nibble(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ODD_NB);
+}
+
+static unsigned int ftgmac100_rxdes_data_length(struct ftgmac100_rxdes *rxdes)
+{
+	return le32_to_cpu(rxdes->rxdes0) & FTGMAC100_RXDES0_VDBC;
+}
+
+static bool ftgmac100_rxdes_multicast(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_MULTICAST);
+}
+
+static void ftgmac100_rxdes_set_end_of_ring(struct ftgmac100_rxdes *rxdes)
+{
+	rxdes->rxdes0 |= cpu_to_le32(FTGMAC100_RXDES0_EDORR);
+}
+
+static void ftgmac100_rxdes_set_dma_addr(struct ftgmac100_rxdes *rxdes,
+					 dma_addr_t addr)
+{
+	rxdes->rxdes3 = cpu_to_le32(addr);
+}
+
+static dma_addr_t ftgmac100_rxdes_get_dma_addr(struct ftgmac100_rxdes *rxdes)
+{
+	return le32_to_cpu(rxdes->rxdes3);
+}
+
+static bool ftgmac100_rxdes_is_tcp(struct ftgmac100_rxdes *rxdes)
+{
+	return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) ==
+	       cpu_to_le32(FTGMAC100_RXDES1_PROT_TCPIP);
+}
+
+static bool ftgmac100_rxdes_is_udp(struct ftgmac100_rxdes *rxdes)
+{
+	return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) ==
+	       cpu_to_le32(FTGMAC100_RXDES1_PROT_UDPIP);
+}
+
+static bool ftgmac100_rxdes_tcpcs_err(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR);
+}
+
+static bool ftgmac100_rxdes_udpcs_err(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_UDP_CHKSUM_ERR);
+}
+
+static bool ftgmac100_rxdes_ipcs_err(struct ftgmac100_rxdes *rxdes)
+{
+	return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_IP_CHKSUM_ERR);
+}
+
+/*
+ * rxdes2 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 ftgmac100_rxdes_set_page(struct ftgmac100_rxdes *rxdes, struct page *page)
+{
+	rxdes->rxdes2 = (unsigned int)page;
+}
+
+static struct page *ftgmac100_rxdes_get_page(struct ftgmac100_rxdes *rxdes)
+{
+	return (struct page *)rxdes->rxdes2;
+}
+
+/******************************************************************************
+ * internal functions (receive)
+ *****************************************************************************/
+static int ftgmac100_next_rx_pointer(int pointer)
+{
+	return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
+}
+
+static void ftgmac100_rx_pointer_advance(struct ftgmac100 *priv)
+{
+	priv->rx_pointer = ftgmac100_next_rx_pointer(priv->rx_pointer);
+}
+
+static struct ftgmac100_rxdes *ftgmac100_current_rxdes(struct ftgmac100 *priv)
+{
+	return &priv->descs->rxdes[priv->rx_pointer];
+}
+
+static struct ftgmac100_rxdes *
+ftgmac100_rx_locate_first_segment(struct ftgmac100 *priv)
+{
+	struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv);
+
+	while (ftgmac100_rxdes_packet_ready(rxdes)) {
+		if (ftgmac100_rxdes_first_segment(rxdes))
+			return rxdes;
+
+		ftgmac100_rxdes_set_dma_own(rxdes);
+		ftgmac100_rx_pointer_advance(priv);
+		rxdes = ftgmac100_current_rxdes(priv);
+	}
+
+	return NULL;
+}
+
+static bool ftgmac100_rx_packet_error(struct ftgmac100 *priv,
+				      struct ftgmac100_rxdes *rxdes)
+{
+	struct net_device *netdev = priv->netdev;
+	bool error = false;
+
+	if (unlikely(ftgmac100_rxdes_rx_error(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx err\n");
+
+		netdev->stats.rx_errors++;
+		error = true;
+	}
+
+	if (unlikely(ftgmac100_rxdes_crc_error(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx crc err\n");
+
+		netdev->stats.rx_crc_errors++;
+		error = true;
+	} else if (unlikely(ftgmac100_rxdes_ipcs_err(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx IP checksum err\n");
+
+		error = true;
+	}
+
+	if (unlikely(ftgmac100_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(ftgmac100_rxdes_runt(rxdes))) {
+		if (net_ratelimit())
+			netdev_info(netdev, "rx runt\n");
+
+		netdev->stats.rx_length_errors++;
+		error = true;
+	} else if (unlikely(ftgmac100_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 ftgmac100_rx_drop_packet(struct ftgmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv);
+	bool done = false;
+
+	if (net_ratelimit())
+		netdev_dbg(netdev, "drop packet %p\n", rxdes);
+
+	do {
+		if (ftgmac100_rxdes_last_segment(rxdes))
+			done = true;
+
+		ftgmac100_rxdes_set_dma_own(rxdes);
+		ftgmac100_rx_pointer_advance(priv);
+		rxdes = ftgmac100_current_rxdes(priv);
+	} while (!done && ftgmac100_rxdes_packet_ready(rxdes));
+
+	netdev->stats.rx_dropped++;
+}
+
+static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftgmac100_rxdes *rxdes;
+	struct sk_buff *skb;
+	bool done = false;
+
+	rxdes = ftgmac100_rx_locate_first_segment(priv);
+	if (!rxdes)
+		return false;
+
+	if (unlikely(ftgmac100_rx_packet_error(priv, rxdes))) {
+		ftgmac100_rx_drop_packet(priv);
+		return true;
+	}
+
+	/* start processing */
+	skb = netdev_alloc_skb_ip_align(netdev, 128);
+	if (unlikely(!skb)) {
+		if (net_ratelimit())
+			netdev_err(netdev, "rx skb alloc failed\n");
+
+		ftgmac100_rx_drop_packet(priv);
+		return true;
+	}
+
+	if (unlikely(ftgmac100_rxdes_multicast(rxdes)))
+		netdev->stats.multicast++;
+
+	/*
+	 * It seems that HW does checksum incorrectly with fragmented packets,
+	 * so we are conservative here - if HW checksum error, let software do
+	 * the checksum again.
+	 */
+	if ((ftgmac100_rxdes_is_tcp(rxdes) && !ftgmac100_rxdes_tcpcs_err(rxdes)) ||
+	    (ftgmac100_rxdes_is_udp(rxdes) && !ftgmac100_rxdes_udpcs_err(rxdes)))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+	do {
+		dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes);
+		struct page *page = ftgmac100_rxdes_get_page(rxdes);
+		unsigned int size;
+
+		dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+
+		size = ftgmac100_rxdes_data_length(rxdes);
+		skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size);
+
+		skb->len += size;
+		skb->data_len += size;
+		skb->truesize += size;
+
+		if (ftgmac100_rxdes_last_segment(rxdes))
+			done = true;
+
+		ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
+
+		ftgmac100_rx_pointer_advance(priv);
+		rxdes = ftgmac100_current_rxdes(priv);
+	} while (!done);
+
+	__pskb_pull_tail(skb, min(skb->len, 64U));
+	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 ftgmac100_txdes_reset(struct ftgmac100_txdes *txdes)
+{
+	/* clear all except end of ring bit */
+	txdes->txdes0 &= cpu_to_le32(FTGMAC100_TXDES0_EDOTR);
+	txdes->txdes1 = 0;
+	txdes->txdes2 = 0;
+	txdes->txdes3 = 0;
+}
+
+static bool ftgmac100_txdes_owned_by_dma(struct ftgmac100_txdes *txdes)
+{
+	return txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN);
+}
+
+static void ftgmac100_txdes_set_dma_own(struct ftgmac100_txdes *txdes)
+{
+	/*
+	 * Make sure dma own bit will not be set before any other
+	 * descriptor fields.
+	 */
+	wmb();
+	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN);
+}
+
+static void ftgmac100_txdes_set_end_of_ring(struct ftgmac100_txdes *txdes)
+{
+	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_EDOTR);
+}
+
+static void ftgmac100_txdes_set_first_segment(struct ftgmac100_txdes *txdes)
+{
+	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS);
+}
+
+static void ftgmac100_txdes_set_last_segment(struct ftgmac100_txdes *txdes)
+{
+	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS);
+}
+
+static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes *txdes,
+					    unsigned int len)
+{
+	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len));
+}
+
+static void ftgmac100_txdes_set_txint(struct ftgmac100_txdes *txdes)
+{
+	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TXIC);
+}
+
+static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes *txdes)
+{
+	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TCP_CHKSUM);
+}
+
+static void ftgmac100_txdes_set_udpcs(struct ftgmac100_txdes *txdes)
+{
+	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_UDP_CHKSUM);
+}
+
+static void ftgmac100_txdes_set_ipcs(struct ftgmac100_txdes *txdes)
+{
+	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_IP_CHKSUM);
+}
+
+static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes *txdes,
+					 dma_addr_t addr)
+{
+	txdes->txdes3 = cpu_to_le32(addr);
+}
+
+static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes)
+{
+	return le32_to_cpu(txdes->txdes3);
+}
+
+/*
+ * txdes2 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 ftgmac100_txdes_set_skb(struct ftgmac100_txdes *txdes,
+				    struct sk_buff *skb)
+{
+	txdes->txdes2 = (unsigned int)skb;
+}
+
+static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes)
+{
+	return (struct sk_buff *)txdes->txdes2;
+}
+
+/******************************************************************************
+ * internal functions (transmit)
+ *****************************************************************************/
+static int ftgmac100_next_tx_pointer(int pointer)
+{
+	return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
+}
+
+static void ftgmac100_tx_pointer_advance(struct ftgmac100 *priv)
+{
+	priv->tx_pointer = ftgmac100_next_tx_pointer(priv->tx_pointer);
+}
+
+static void ftgmac100_tx_clean_pointer_advance(struct ftgmac100 *priv)
+{
+	priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv->tx_clean_pointer);
+}
+
+static struct ftgmac100_txdes *ftgmac100_current_txdes(struct ftgmac100 *priv)
+{
+	return &priv->descs->txdes[priv->tx_pointer];
+}
+
+static struct ftgmac100_txdes *
+ftgmac100_current_clean_txdes(struct ftgmac100 *priv)
+{
+	return &priv->descs->txdes[priv->tx_clean_pointer];
+}
+
+static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftgmac100_txdes *txdes;
+	struct sk_buff *skb;
+	dma_addr_t map;
+
+	if (priv->tx_pending == 0)
+		return false;
+
+	txdes = ftgmac100_current_clean_txdes(priv);
+
+	if (ftgmac100_txdes_owned_by_dma(txdes))
+		return false;
+
+	skb = ftgmac100_txdes_get_skb(txdes);
+	map = ftgmac100_txdes_get_dma_addr(txdes);
+
+	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);
+
+	ftgmac100_txdes_reset(txdes);
+
+	ftgmac100_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 ftgmac100_tx_complete(struct ftgmac100 *priv)
+{
+	while (ftgmac100_tx_complete_packet(priv))
+		;
+}
+
+static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb,
+			  dma_addr_t map)
+{
+	struct net_device *netdev = priv->netdev;
+	struct ftgmac100_txdes *txdes;
+	unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+
+	txdes = ftgmac100_current_txdes(priv);
+	ftgmac100_tx_pointer_advance(priv);
+
+	/* setup TX descriptor */
+	ftgmac100_txdes_set_skb(txdes, skb);
+	ftgmac100_txdes_set_dma_addr(txdes, map);
+	ftgmac100_txdes_set_buffer_size(txdes, len);
+
+	ftgmac100_txdes_set_first_segment(txdes);
+	ftgmac100_txdes_set_last_segment(txdes);
+	ftgmac100_txdes_set_txint(txdes);
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		__be16 protocol = skb->protocol;
+
+		if (protocol == cpu_to_be16(ETH_P_IP)) {
+			u8 ip_proto = ip_hdr(skb)->protocol;
+
+			ftgmac100_txdes_set_ipcs(txdes);
+			if (ip_proto == IPPROTO_TCP)
+				ftgmac100_txdes_set_tcpcs(txdes);
+			else if (ip_proto == IPPROTO_UDP)
+				ftgmac100_txdes_set_udpcs(txdes);
+		}
+	}
+
+	spin_lock(&priv->tx_lock);
+	priv->tx_pending++;
+	if (priv->tx_pending == TX_QUEUE_ENTRIES)
+		netif_stop_queue(netdev);
+
+	/* start transmit */
+	ftgmac100_txdes_set_dma_own(txdes);
+	spin_unlock(&priv->tx_lock);
+
+	ftgmac100_txdma_normal_prio_start_polling(priv);
+
+	return NETDEV_TX_OK;
+}
+
+/******************************************************************************
+ * internal functions (buffer)
+ *****************************************************************************/
+static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
+				   struct ftgmac100_rxdes *rxdes, gfp_t gfp)
+{
+	struct net_device *netdev = priv->netdev;
+	struct page *page;
+	dma_addr_t map;
+
+	page = alloc_page(gfp);
+	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;
+	}
+
+	ftgmac100_rxdes_set_page(rxdes, page);
+	ftgmac100_rxdes_set_dma_addr(rxdes, map);
+	ftgmac100_rxdes_set_dma_own(rxdes);
+	return 0;
+}
+
+static void ftgmac100_free_buffers(struct ftgmac100 *priv)
+{
+	int i;
+
+	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+		struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+		struct page *page = ftgmac100_rxdes_get_page(rxdes);
+		dma_addr_t map = ftgmac100_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 ftgmac100_txdes *txdes = &priv->descs->txdes[i];
+		struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes);
+		dma_addr_t map = ftgmac100_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 ftgmac100_descs),
+			  priv->descs, priv->descs_dma_addr);
+}
+
+static int ftgmac100_alloc_buffers(struct ftgmac100 *priv)
+{
+	int i;
+
+	priv->descs = dma_alloc_coherent(priv->dev,
+					 sizeof(struct ftgmac100_descs),
+					 &priv->descs_dma_addr, GFP_KERNEL);
+	if (!priv->descs)
+		return -ENOMEM;
+
+	memset(priv->descs, 0, sizeof(struct ftgmac100_descs));
+
+	/* initialize RX ring */
+	ftgmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
+
+	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+		struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+
+		if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
+			goto err;
+	}
+
+	/* initialize TX ring */
+	ftgmac100_txdes_set_end_of_ring(&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
+	return 0;
+
+err:
+	ftgmac100_free_buffers(priv);
+	return -ENOMEM;
+}
+
+/******************************************************************************
+ * internal functions (mdio)
+ *****************************************************************************/
+static void ftgmac100_adjust_link(struct net_device *netdev)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+	struct phy_device *phydev = priv->phydev;
+	int ier;
+
+	if (phydev->speed == priv->old_speed)
+		return;
+
+	priv->old_speed = phydev->speed;
+
+	ier = ioread32(priv->base + FTGMAC100_OFFSET_IER);
+
+	/* disable all interrupts */
+	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
+
+	netif_stop_queue(netdev);
+	ftgmac100_stop_hw(priv);
+
+	netif_start_queue(netdev);
+	ftgmac100_init_hw(priv);
+	ftgmac100_start_hw(priv, phydev->speed);
+
+	/* re-enable interrupts */
+	iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER);
+}
+
+static int ftgmac100_mii_probe(struct ftgmac100 *priv)
+{
+	struct net_device *netdev = priv->netdev;
+	struct phy_device *phydev = NULL;
+	int i;
+
+	/* search for connect PHY device */
+	for (i = 0; i < PHY_MAX_ADDR; i++) {
+		struct phy_device *tmp = priv->mii_bus->phy_map[i];
+
+		if (tmp) {
+			phydev = tmp;
+			break;
+		}
+	}
+
+	/* now we are supposed to have a proper phydev, to attach to... */
+	if (!phydev) {
+		netdev_info(netdev, "%s: no PHY found\n", netdev->name);
+		return -ENODEV;
+	}
+
+	phydev = phy_connect(netdev, dev_name(&phydev->dev),
+			     &ftgmac100_adjust_link, 0,
+			     PHY_INTERFACE_MODE_GMII);
+
+	if (IS_ERR(phydev)) {
+		netdev_err(netdev, "%s: Could not attach to PHY\n", netdev->name);
+		return PTR_ERR(phydev);
+	}
+
+	priv->phydev = phydev;
+	return 0;
+}
+
+/******************************************************************************
+ * struct mii_bus functions
+ *****************************************************************************/
+static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+	struct net_device *netdev = bus->priv;
+	struct ftgmac100 *priv = netdev_priv(netdev);
+	unsigned int phycr;
+	int i;
+
+	phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
+
+	/* preserve MDC cycle threshold */
+	phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
+
+	phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) |
+		 FTGMAC100_PHYCR_REGAD(regnum) |
+		 FTGMAC100_PHYCR_MIIRD;
+
+	iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR);
+
+	for (i = 0; i < 10; i++) {
+		phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
+
+		if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) {
+			int data;
+
+			data = ioread32(priv->base + FTGMAC100_OFFSET_PHYDATA);
+			return FTGMAC100_PHYDATA_MIIRDATA(data);
+		}
+
+		usleep_range(100, 1000);
+	}
+
+	netdev_err(netdev, "mdio read timed out\n");
+	return -EIO;
+}
+
+static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr,
+				   int regnum, u16 value)
+{
+	struct net_device *netdev = bus->priv;
+	struct ftgmac100 *priv = netdev_priv(netdev);
+	unsigned int phycr;
+	int data;
+	int i;
+
+	phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
+
+	/* preserve MDC cycle threshold */
+	phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
+
+	phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) |
+		 FTGMAC100_PHYCR_REGAD(regnum) |
+		 FTGMAC100_PHYCR_MIIWR;
+
+	data = FTGMAC100_PHYDATA_MIIWDATA(value);
+
+	iowrite32(data, priv->base + FTGMAC100_OFFSET_PHYDATA);
+	iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR);
+
+	for (i = 0; i < 10; i++) {
+		phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
+
+		if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0)
+			return 0;
+
+		usleep_range(100, 1000);
+	}
+
+	netdev_err(netdev, "mdio write timed out\n");
+	return -EIO;
+}
+
+static int ftgmac100_mdiobus_reset(struct mii_bus *bus)
+{
+	return 0;
+}
+
+/******************************************************************************
+ * struct ethtool_ops functions
+ *****************************************************************************/
+static void ftgmac100_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 ftgmac100_get_settings(struct net_device *netdev,
+				  struct ethtool_cmd *cmd)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+
+	return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int ftgmac100_set_settings(struct net_device *netdev,
+				  struct ethtool_cmd *cmd)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+
+	return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static const struct ethtool_ops ftgmac100_ethtool_ops = {
+	.set_settings		= ftgmac100_set_settings,
+	.get_settings		= ftgmac100_get_settings,
+	.get_drvinfo		= ftgmac100_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+};
+
+/******************************************************************************
+ * interrupt handler
+ *****************************************************************************/
+static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
+{
+	struct net_device *netdev = dev_id;
+	struct ftgmac100 *priv = netdev_priv(netdev);
+
+	if (likely(netif_running(netdev))) {
+		/* Disable interrupts for polling */
+		iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
+		napi_schedule(&priv->napi);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/******************************************************************************
+ * struct napi_struct functions
+ *****************************************************************************/
+static int ftgmac100_poll(struct napi_struct *napi, int budget)
+{
+	struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi);
+	struct net_device *netdev = priv->netdev;
+	unsigned int status;
+	bool completed = true;
+	int rx = 0;
+
+	status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
+	iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
+
+	if (status & (FTGMAC100_INT_RPKT_BUF | FTGMAC100_INT_NO_RXBUF)) {
+		/*
+		 * FTGMAC100_INT_RPKT_BUF:
+		 *	RX DMA has received packets into RX buffer successfully
+		 *
+		 * FTGMAC100_INT_NO_RXBUF:
+		 *	RX buffer unavailable
+		 */
+		bool retry;
+
+		do {
+			retry = ftgmac100_rx_packet(priv, &rx);
+		} while (retry && rx < budget);
+
+		if (retry && rx == budget)
+			completed = false;
+	}
+
+	if (status & (FTGMAC100_INT_XPKT_ETH | FTGMAC100_INT_XPKT_LOST)) {
+		/*
+		 * FTGMAC100_INT_XPKT_ETH:
+		 *	packet transmitted to ethernet successfully
+		 *
+		 * FTGMAC100_INT_XPKT_LOST:
+		 *	packet transmitted to ethernet lost due to late
+		 *	collision or excessive collision
+		 */
+		ftgmac100_tx_complete(priv);
+	}
+
+	if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST |
+		      FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) {
+		if (net_ratelimit())
+			netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status,
+				    status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
+				    status & FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "",
+				    status & FTGMAC100_INT_AHB_ERR ? "AHB_ERR " : "",
+				    status & FTGMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : "");
+
+		if (status & FTGMAC100_INT_NO_RXBUF) {
+			/* RX buffer unavailable */
+			netdev->stats.rx_over_errors++;
+		}
+
+		if (status & FTGMAC100_INT_RPKT_LOST) {
+			/* received packet lost due to RX FIFO full */
+			netdev->stats.rx_fifo_errors++;
+		}
+	}
+
+	if (completed) {
+		napi_complete(napi);
+
+		/* enable all interrupts */
+		iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+	}
+
+	return rx;
+}
+
+/******************************************************************************
+ * struct net_device_ops functions
+ *****************************************************************************/
+static int ftgmac100_open(struct net_device *netdev)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+	int err;
+
+	err = ftgmac100_alloc_buffers(priv);
+	if (err) {
+		netdev_err(netdev, "failed to allocate buffers\n");
+		goto err_alloc;
+	}
+
+	err = request_irq(priv->irq, ftgmac100_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 = ftgmac100_reset_hw(priv);
+	if (err)
+		goto err_hw;
+
+	ftgmac100_init_hw(priv);
+	ftgmac100_start_hw(priv, 10);
+
+	phy_start(priv->phydev);
+
+	napi_enable(&priv->napi);
+	netif_start_queue(netdev);
+
+	/* enable all interrupts */
+	iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+	return 0;
+
+err_hw:
+	free_irq(priv->irq, netdev);
+err_irq:
+	ftgmac100_free_buffers(priv);
+err_alloc:
+	return err;
+}
+
+static int ftgmac100_stop(struct net_device *netdev)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+
+	/* disable all interrupts */
+	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
+
+	netif_stop_queue(netdev);
+	napi_disable(&priv->napi);
+	phy_stop(priv->phydev);
+
+	ftgmac100_stop_hw(priv);
+	free_irq(priv->irq, netdev);
+	ftgmac100_free_buffers(priv);
+
+	return 0;
+}
+
+static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
+				     struct net_device *netdev)
+{
+	struct ftgmac100 *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 ftgmac100_xmit(priv, skb, map);
+}
+
+/* optional */
+static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+
+	return phy_mii_ioctl(priv->phydev, ifr, cmd);
+}
+
+static const struct net_device_ops ftgmac100_netdev_ops = {
+	.ndo_open		= ftgmac100_open,
+	.ndo_stop		= ftgmac100_stop,
+	.ndo_start_xmit		= ftgmac100_hard_start_xmit,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= ftgmac100_do_ioctl,
+};
+
+/******************************************************************************
+ * struct platform_driver functions
+ *****************************************************************************/
+static int ftgmac100_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int irq;
+	struct net_device *netdev;
+	struct ftgmac100 *priv;
+	int err;
+	int i;
+
+	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, &ftgmac100_ethtool_ops);
+	netdev->netdev_ops = &ftgmac100_netdev_ops;
+	netdev->features = NETIF_F_IP_CSUM;
+
+	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, ftgmac100_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 mdio bus */
+	priv->mii_bus = mdiobus_alloc();
+	if (!priv->mii_bus) {
+		err = -EIO;
+		goto err_alloc_mdiobus;
+	}
+
+	priv->mii_bus->name = "ftgmac100_mdio";
+	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii");
+
+	priv->mii_bus->priv = netdev;
+	priv->mii_bus->read = ftgmac100_mdiobus_read;
+	priv->mii_bus->write = ftgmac100_mdiobus_write;
+	priv->mii_bus->reset = ftgmac100_mdiobus_reset;
+	priv->mii_bus->irq = priv->phy_irq;
+
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		priv->mii_bus->irq[i] = PHY_POLL;
+
+	err = mdiobus_register(priv->mii_bus);
+	if (err) {
+		dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
+		goto err_register_mdiobus;
+	}
+
+	err = ftgmac100_mii_probe(priv);
+	if (err) {
+		dev_err(&pdev->dev, "MII Probe failed!\n");
+		goto err_mii_probe;
+	}
+
+	/* 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:
+	phy_disconnect(priv->phydev);
+err_mii_probe:
+	mdiobus_unregister(priv->mii_bus);
+err_register_mdiobus:
+	mdiobus_free(priv->mii_bus);
+err_alloc_mdiobus:
+	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 ftgmac100_remove(struct platform_device *pdev)
+{
+	struct net_device *netdev;
+	struct ftgmac100 *priv;
+
+	netdev = platform_get_drvdata(pdev);
+	priv = netdev_priv(netdev);
+
+	unregister_netdev(netdev);
+
+	phy_disconnect(priv->phydev);
+	mdiobus_unregister(priv->mii_bus);
+	mdiobus_free(priv->mii_bus);
+
+	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 ftgmac100_driver = {
+	.probe		= ftgmac100_probe,
+	.remove		= __exit_p(ftgmac100_remove),
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+/******************************************************************************
+ * initialization / finalization
+ *****************************************************************************/
+static int __init ftgmac100_init(void)
+{
+	pr_info("Loading version " DRV_VERSION " ...\n");
+	return platform_driver_register(&ftgmac100_driver);
+}
+
+static void __exit ftgmac100_exit(void)
+{
+	platform_driver_unregister(&ftgmac100_driver);
+}
+
+module_init(ftgmac100_init);
+module_exit(ftgmac100_exit);
+
+MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
+MODULE_DESCRIPTION("FTGMAC100 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ftgmac100.h b/drivers/net/ftgmac100.h
new file mode 100644
index 0000000..13408d4
--- /dev/null
+++ b/drivers/net/ftgmac100.h
@@ -0,0 +1,246 @@
+/*
+ * Faraday FTGMAC100 Gigabit 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 __FTGMAC100_H
+#define __FTGMAC100_H
+
+#define FTGMAC100_OFFSET_ISR		0x00
+#define FTGMAC100_OFFSET_IER		0x04
+#define FTGMAC100_OFFSET_MAC_MADR	0x08
+#define FTGMAC100_OFFSET_MAC_LADR	0x0c
+#define FTGMAC100_OFFSET_MAHT0		0x10
+#define FTGMAC100_OFFSET_MAHT1		0x14
+#define FTGMAC100_OFFSET_NPTXPD		0x18
+#define FTGMAC100_OFFSET_RXPD		0x1c
+#define FTGMAC100_OFFSET_NPTXR_BADR	0x20
+#define FTGMAC100_OFFSET_RXR_BADR	0x24
+#define FTGMAC100_OFFSET_HPTXPD		0x28
+#define FTGMAC100_OFFSET_HPTXR_BADR	0x2c
+#define FTGMAC100_OFFSET_ITC		0x30
+#define FTGMAC100_OFFSET_APTC		0x34
+#define FTGMAC100_OFFSET_DBLAC		0x38
+#define FTGMAC100_OFFSET_DMAFIFOS	0x3c
+#define FTGMAC100_OFFSET_REVR		0x40
+#define FTGMAC100_OFFSET_FEAR		0x44
+#define FTGMAC100_OFFSET_TPAFCR		0x48
+#define FTGMAC100_OFFSET_RBSR		0x4c
+#define FTGMAC100_OFFSET_MACCR		0x50
+#define FTGMAC100_OFFSET_MACSR		0x54
+#define FTGMAC100_OFFSET_TM		0x58
+#define FTGMAC100_OFFSET_PHYCR		0x60
+#define FTGMAC100_OFFSET_PHYDATA	0x64
+#define FTGMAC100_OFFSET_FCR		0x68
+#define FTGMAC100_OFFSET_BPR		0x6c
+#define FTGMAC100_OFFSET_WOLCR		0x70
+#define FTGMAC100_OFFSET_WOLSR		0x74
+#define FTGMAC100_OFFSET_WFCRC		0x78
+#define FTGMAC100_OFFSET_WFBM1		0x80
+#define FTGMAC100_OFFSET_WFBM2		0x84
+#define FTGMAC100_OFFSET_WFBM3		0x88
+#define FTGMAC100_OFFSET_WFBM4		0x8c
+#define FTGMAC100_OFFSET_NPTXR_PTR	0x90
+#define FTGMAC100_OFFSET_HPTXR_PTR	0x94
+#define FTGMAC100_OFFSET_RXR_PTR	0x98
+#define FTGMAC100_OFFSET_TX		0xa0
+#define FTGMAC100_OFFSET_TX_MCOL_SCOL	0xa4
+#define FTGMAC100_OFFSET_TX_ECOL_FAIL	0xa8
+#define FTGMAC100_OFFSET_TX_LCOL_UND	0xac
+#define FTGMAC100_OFFSET_RX		0xb0
+#define FTGMAC100_OFFSET_RX_BC		0xb4
+#define FTGMAC100_OFFSET_RX_MC		0xb8
+#define FTGMAC100_OFFSET_RX_PF_AEP	0xbc
+#define FTGMAC100_OFFSET_RX_RUNT	0xc0
+#define FTGMAC100_OFFSET_RX_CRCER_FTL	0xc4
+#define FTGMAC100_OFFSET_RX_COL_LOST	0xc8
+
+/*
+ * Interrupt status register & interrupt enable register
+ */
+#define FTGMAC100_INT_RPKT_BUF		(1 << 0)
+#define FTGMAC100_INT_RPKT_FIFO		(1 << 1)
+#define FTGMAC100_INT_NO_RXBUF		(1 << 2)
+#define FTGMAC100_INT_RPKT_LOST		(1 << 3)
+#define FTGMAC100_INT_XPKT_ETH		(1 << 4)
+#define FTGMAC100_INT_XPKT_FIFO		(1 << 5)
+#define FTGMAC100_INT_NO_NPTXBUF	(1 << 6)
+#define FTGMAC100_INT_XPKT_LOST		(1 << 7)
+#define FTGMAC100_INT_AHB_ERR		(1 << 8)
+#define FTGMAC100_INT_PHYSTS_CHG	(1 << 9)
+#define FTGMAC100_INT_NO_HPTXBUF	(1 << 10)
+
+/*
+ * Interrupt timer control register
+ */
+#define FTGMAC100_ITC_RXINT_CNT(x)	(((x) & 0xf) << 0)
+#define FTGMAC100_ITC_RXINT_THR(x)	(((x) & 0x7) << 4)
+#define FTGMAC100_ITC_RXINT_TIME_SEL	(1 << 7)
+#define FTGMAC100_ITC_TXINT_CNT(x)	(((x) & 0xf) << 8)
+#define FTGMAC100_ITC_TXINT_THR(x)	(((x) & 0x7) << 12)
+#define FTGMAC100_ITC_TXINT_TIME_SEL	(1 << 15)
+
+/*
+ * Automatic polling timer control register
+ */
+#define FTGMAC100_APTC_RXPOLL_CNT(x)	(((x) & 0xf) << 0)
+#define FTGMAC100_APTC_RXPOLL_TIME_SEL	(1 << 4)
+#define FTGMAC100_APTC_TXPOLL_CNT(x)	(((x) & 0xf) << 8)
+#define FTGMAC100_APTC_TXPOLL_TIME_SEL	(1 << 12)
+
+/*
+ * DMA burst length and arbitration control register
+ */
+#define FTGMAC100_DBLAC_RXFIFO_LTHR(x)	(((x) & 0x7) << 0)
+#define FTGMAC100_DBLAC_RXFIFO_HTHR(x)	(((x) & 0x7) << 3)
+#define FTGMAC100_DBLAC_RX_THR_EN	(1 << 6)
+#define FTGMAC100_DBLAC_RXBURST_SIZE(x)	(((x) & 0x3) << 8)
+#define FTGMAC100_DBLAC_TXBURST_SIZE(x)	(((x) & 0x3) << 10)
+#define FTGMAC100_DBLAC_RXDES_SIZE(x)	(((x) & 0xf) << 12)
+#define FTGMAC100_DBLAC_TXDES_SIZE(x)	(((x) & 0xf) << 16)
+#define FTGMAC100_DBLAC_IFG_CNT(x)	(((x) & 0x7) << 20)
+#define FTGMAC100_DBLAC_IFG_INC		(1 << 23)
+
+/*
+ * DMA FIFO status register
+ */
+#define FTGMAC100_DMAFIFOS_RXDMA1_SM(dmafifos)	((dmafifos) & 0xf)
+#define FTGMAC100_DMAFIFOS_RXDMA2_SM(dmafifos)	(((dmafifos) >> 4) & 0xf)
+#define FTGMAC100_DMAFIFOS_RXDMA3_SM(dmafifos)	(((dmafifos) >> 8) & 0x7)
+#define FTGMAC100_DMAFIFOS_TXDMA1_SM(dmafifos)	(((dmafifos) >> 12) & 0xf)
+#define FTGMAC100_DMAFIFOS_TXDMA2_SM(dmafifos)	(((dmafifos) >> 16) & 0x3)
+#define FTGMAC100_DMAFIFOS_TXDMA3_SM(dmafifos)	(((dmafifos) >> 18) & 0xf)
+#define FTGMAC100_DMAFIFOS_RXFIFO_EMPTY		(1 << 26)
+#define FTGMAC100_DMAFIFOS_TXFIFO_EMPTY		(1 << 27)
+#define FTGMAC100_DMAFIFOS_RXDMA_GRANT		(1 << 28)
+#define FTGMAC100_DMAFIFOS_TXDMA_GRANT		(1 << 29)
+#define FTGMAC100_DMAFIFOS_RXDMA_REQ		(1 << 30)
+#define FTGMAC100_DMAFIFOS_TXDMA_REQ		(1 << 31)
+
+/*
+ * Receive buffer size register
+ */
+#define FTGMAC100_RBSR_SIZE(x)		((x) & 0x3fff)
+
+/*
+ * MAC control register
+ */
+#define FTGMAC100_MACCR_TXDMA_EN	(1 << 0)
+#define FTGMAC100_MACCR_RXDMA_EN	(1 << 1)
+#define FTGMAC100_MACCR_TXMAC_EN	(1 << 2)
+#define FTGMAC100_MACCR_RXMAC_EN	(1 << 3)
+#define FTGMAC100_MACCR_RM_VLAN		(1 << 4)
+#define FTGMAC100_MACCR_HPTXR_EN	(1 << 5)
+#define FTGMAC100_MACCR_LOOP_EN		(1 << 6)
+#define FTGMAC100_MACCR_ENRX_IN_HALFTX	(1 << 7)
+#define FTGMAC100_MACCR_FULLDUP		(1 << 8)
+#define FTGMAC100_MACCR_GIGA_MODE	(1 << 9)
+#define FTGMAC100_MACCR_CRC_APD		(1 << 10)
+#define FTGMAC100_MACCR_RX_RUNT		(1 << 12)
+#define FTGMAC100_MACCR_JUMBO_LF	(1 << 13)
+#define FTGMAC100_MACCR_RX_ALL		(1 << 14)
+#define FTGMAC100_MACCR_HT_MULTI_EN	(1 << 15)
+#define FTGMAC100_MACCR_RX_MULTIPKT	(1 << 16)
+#define FTGMAC100_MACCR_RX_BROADPKT	(1 << 17)
+#define FTGMAC100_MACCR_DISCARD_CRCERR	(1 << 18)
+#define FTGMAC100_MACCR_FAST_MODE	(1 << 19)
+#define FTGMAC100_MACCR_SW_RST		(1 << 31)
+
+/*
+ * PHY control register
+ */
+#define FTGMAC100_PHYCR_MDC_CYCTHR_MASK	0x3f
+#define FTGMAC100_PHYCR_MDC_CYCTHR(x)	((x) & 0x3f)
+#define FTGMAC100_PHYCR_PHYAD(x)	(((x) & 0x1f) << 16)
+#define FTGMAC100_PHYCR_REGAD(x)	(((x) & 0x1f) << 21)
+#define FTGMAC100_PHYCR_MIIRD		(1 << 26)
+#define FTGMAC100_PHYCR_MIIWR		(1 << 27)
+
+/*
+ * PHY data register
+ */
+#define FTGMAC100_PHYDATA_MIIWDATA(x)		((x) & 0xffff)
+#define FTGMAC100_PHYDATA_MIIRDATA(phydata)	(((phydata) >> 16) & 0xffff)
+
+/*
+ * Transmit descriptor, aligned to 16 bytes
+ */
+struct ftgmac100_txdes {
+	unsigned int	txdes0;
+	unsigned int	txdes1;
+	unsigned int	txdes2;	/* not used by HW */
+	unsigned int	txdes3;	/* TXBUF_BADR */
+} __attribute__ ((aligned(16)));
+
+#define FTGMAC100_TXDES0_TXBUF_SIZE(x)	((x) & 0x3fff)
+#define FTGMAC100_TXDES0_EDOTR		(1 << 15)
+#define FTGMAC100_TXDES0_CRC_ERR	(1 << 19)
+#define FTGMAC100_TXDES0_LTS		(1 << 28)
+#define FTGMAC100_TXDES0_FTS		(1 << 29)
+#define FTGMAC100_TXDES0_TXDMA_OWN	(1 << 31)
+
+#define FTGMAC100_TXDES1_VLANTAG_CI(x)	((x) & 0xffff)
+#define FTGMAC100_TXDES1_INS_VLANTAG	(1 << 16)
+#define FTGMAC100_TXDES1_TCP_CHKSUM	(1 << 17)
+#define FTGMAC100_TXDES1_UDP_CHKSUM	(1 << 18)
+#define FTGMAC100_TXDES1_IP_CHKSUM	(1 << 19)
+#define FTGMAC100_TXDES1_LLC		(1 << 22)
+#define FTGMAC100_TXDES1_TX2FIC		(1 << 30)
+#define FTGMAC100_TXDES1_TXIC		(1 << 31)
+
+/*
+ * Receive descriptor, aligned to 16 bytes
+ */
+struct ftgmac100_rxdes {
+	unsigned int	rxdes0;
+	unsigned int	rxdes1;
+	unsigned int	rxdes2;	/* not used by HW */
+	unsigned int	rxdes3;	/* RXBUF_BADR */
+} __attribute__ ((aligned(16)));
+
+#define FTGMAC100_RXDES0_VDBC		0x3fff
+#define FTGMAC100_RXDES0_EDORR		(1 << 15)
+#define FTGMAC100_RXDES0_MULTICAST	(1 << 16)
+#define FTGMAC100_RXDES0_BROADCAST	(1 << 17)
+#define FTGMAC100_RXDES0_RX_ERR		(1 << 18)
+#define FTGMAC100_RXDES0_CRC_ERR	(1 << 19)
+#define FTGMAC100_RXDES0_FTL		(1 << 20)
+#define FTGMAC100_RXDES0_RUNT		(1 << 21)
+#define FTGMAC100_RXDES0_RX_ODD_NB	(1 << 22)
+#define FTGMAC100_RXDES0_FIFO_FULL	(1 << 23)
+#define FTGMAC100_RXDES0_PAUSE_OPCODE	(1 << 24)
+#define FTGMAC100_RXDES0_PAUSE_FRAME	(1 << 25)
+#define FTGMAC100_RXDES0_LRS		(1 << 28)
+#define FTGMAC100_RXDES0_FRS		(1 << 29)
+#define FTGMAC100_RXDES0_RXPKT_RDY	(1 << 31)
+
+#define FTGMAC100_RXDES1_VLANTAG_CI	0xffff
+#define FTGMAC100_RXDES1_PROT_MASK	(0x3 << 20)
+#define FTGMAC100_RXDES1_PROT_NONIP	(0x0 << 20)
+#define FTGMAC100_RXDES1_PROT_IP	(0x1 << 20)
+#define FTGMAC100_RXDES1_PROT_TCPIP	(0x2 << 20)
+#define FTGMAC100_RXDES1_PROT_UDPIP	(0x3 << 20)
+#define FTGMAC100_RXDES1_LLC		(1 << 22)
+#define FTGMAC100_RXDES1_DF		(1 << 23)
+#define FTGMAC100_RXDES1_VLANTAG_AVAIL	(1 << 24)
+#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR	(1 << 25)
+#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR	(1 << 26)
+#define FTGMAC100_RXDES1_IP_CHKSUM_ERR	(1 << 27)
+
+#endif /* __FTGMAC100_H */
-- 
1.6.3.3

^ permalink raw reply related

* Re: [PATCHv2 3/9] macb: unify at91 and avr32 platform data
From: Jamie Iles @ 2011-03-17  9:34 UTC (permalink / raw)
  To: Peter Korsgaard
  Cc: Russell King - ARM Linux, avictor.za@gmail.com, Jamie Iles,
	plagnioj, linux-arm-kernel, nicolas.ferre, netdev
In-Reply-To: <87wrjyksnm.fsf@macbook.be.48ers.dk>

On Thu, Mar 17, 2011 at 10:22:53AM +0100, Peter Korsgaard wrote:
> >>>>> "Russell" == Russell King <- ARM Linux <linux@arm.linux.org.uk>> writes:
> 
> Hi,
> 
>  >> That should probably be cleaned up as well then. Sharing platform_data
>  >> structures between unrelated drivers seems like quite a mess to me.
> 
>  Russell> Why should every driver have a separate platform data structure?
>  Russell> Is it right to end up with thousands of unique data structures each
>  Russell> specific to a particular driver?  To me, that sounds like a headache
>  Russell> waiting to happen.
> 
> Well, the point of the platform data is to provide driver specific
> (E.G. not generic) data to the driver, so in general it will be
> different for different hardware.
> 
> The current situation with 2 different structure defination depending on
> arch, macro magic and 1 of these structures also used for a 2nd driver
> isn't optimal.
> 
> But ok, I don't feel strongly about struct macb_platform_data also being
> used for the old at91_ether driver, but it shouldn't be called
> eth_platform_data as it isn't really a generic structure.

Ok, I'll rename to macb_platform_data and update at91_ether to use that 
with a comment describing that we're sharing the platform data with 
macb.  At least that gets rid of the preprocessor stuff in board.h for 
at91 too.

Jamie

^ permalink raw reply

* Re: [PATCHv2 3/9] macb: unify at91 and avr32 platform data
From: Peter Korsgaard @ 2011-03-17  9:22 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: avictor.za@gmail.com, Jamie Iles, plagnioj, linux-arm-kernel,
	nicolas.ferre, netdev
In-Reply-To: <20110317085835.GC29758@n2100.arm.linux.org.uk>

>>>>> "Russell" == Russell King <- ARM Linux <linux@arm.linux.org.uk>> writes:

Hi,

 >> That should probably be cleaned up as well then. Sharing platform_data
 >> structures between unrelated drivers seems like quite a mess to me.

 Russell> Why should every driver have a separate platform data structure?
 Russell> Is it right to end up with thousands of unique data structures each
 Russell> specific to a particular driver?  To me, that sounds like a headache
 Russell> waiting to happen.

Well, the point of the platform data is to provide driver specific
(E.G. not generic) data to the driver, so in general it will be
different for different hardware.

The current situation with 2 different structure defination depending on
arch, macro magic and 1 of these structures also used for a 2nd driver
isn't optimal.

But ok, I don't feel strongly about struct macb_platform_data also being
used for the old at91_ether driver, but it shouldn't be called
eth_platform_data as it isn't really a generic structure.

-- 
Bye, Peter Korsgaard

^ permalink raw reply

* Re: [PATCHv2 1/9] at91: provide macb clks with "pclk" and "hclk" name
From: Andrew Victor @ 2011-03-17  9:22 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: nicolas.ferre, Jamie Iles, plagnioj, linux-arm-kernel, netdev
In-Reply-To: <20110316083844.GA13262@n2100.arm.linux.org.uk>

hi Russell,

>> There is no reference to a "pclk" or "hclk" in the AT91 architecture.
>> So to avoid possible confusion, maybe create two "fake" clocks both
>> parented to "macb_clk", and add a comment they're only for
>> compatibility with the AVR32.
>
> It doesn't matter what's in the documentation.
>
> What matters more than conforming to documentation is keeping the drivers
> in a clean and maintainable state without throwing lots of ifdefs into
> them.

I'm not saying the drivers need ifdefs, they should request both
"pclk" and "hclk" as suggested.

What I was suggesting is the platform clock setup on AT91 as:
    macb_clk
        |
        +-- hclk
        +-- pclk

rather than:
    pclk
        |
        +-- hclk

Regards,
  Andrew Victor

^ permalink raw reply

* Re: [PATCHv2 3/9] macb: unify at91 and avr32 platform data
From: Russell King - ARM Linux @ 2011-03-17  8:58 UTC (permalink / raw)
  To: Peter Korsgaard
  Cc: avictor.za@gmail.com, Jamie Iles, plagnioj, linux-arm-kernel,
	nicolas.ferre, netdev
In-Reply-To: <871v26m8tf.fsf@macbook.be.48ers.dk>

On Thu, Mar 17, 2011 at 09:48:28AM +0100, Peter Korsgaard wrote:
> >>>>> "avictor" == avictor za@gmail com <avictor.za@gmail.com> writes:
> 
> Hi,
> 
>  >> How about at the same time renaming it to something a bit less
>  >> generic like macb_platform_data?
> 
>  avictor> The AT91RM9200 uses the same platform_data structure, but has a
>  avictor> different Ethernet peripheral (ie, not the MACB)
> 
>  avictor> The phy_irq_pin is used in the AT91RM9200 ethernet driver.
>  avictor> (drivers/net/arm/at91_ether.c)
> 
> That should probably be cleaned up as well then. Sharing platform_data
> structures between unrelated drivers seems like quite a mess to me.

Why should every driver have a separate platform data structure?
Is it right to end up with thousands of unique data structures each
specific to a particular driver?  To me, that sounds like a headache
waiting to happen.

^ permalink raw reply

* Re: [PATCHv2 3/9] macb: unify at91 and avr32 platform data
From: Peter Korsgaard @ 2011-03-17  8:48 UTC (permalink / raw)
  To: avictor.za@gmail.com
  Cc: Jamie Iles, netdev, plagnioj, nicolas.ferre, linux-arm-kernel
In-Reply-To: <AANLkTi=TiRnc0ceosH-P7fN7wKrLZE0MCJHQ30gM_Q9b@mail.gmail.com>

>>>>> "avictor" == avictor za@gmail com <avictor.za@gmail.com> writes:

Hi,

 >> How about at the same time renaming it to something a bit less
 >> generic like macb_platform_data?

 avictor> The AT91RM9200 uses the same platform_data structure, but has a
 avictor> different Ethernet peripheral (ie, not the MACB)

 avictor> The phy_irq_pin is used in the AT91RM9200 ethernet driver.
 avictor> (drivers/net/arm/at91_ether.c)

That should probably be cleaned up as well then. Sharing platform_data
structures between unrelated drivers seems like quite a mess to me.

-- 
Bye, Peter Korsgaard

^ permalink raw reply

* Re: [PATCHv2 3/9] macb: unify at91 and avr32 platform data
From: avictor.za @ 2011-03-17  8:43 UTC (permalink / raw)
  To: Peter Korsgaard
  Cc: Jamie Iles, netdev, plagnioj, nicolas.ferre, linux-arm-kernel
In-Reply-To: <87vczkmy94.fsf@macbook.be.48ers.dk>

hi,

>  Jamie> Both at91 and avr32 defines its own platform data structure for
>  Jamie> the macb driver and both share common structures though at91
>  Jamie> includes a currently unused phy_irq_pin.  Create a common
>  Jamie> eth_platform_data for macb that both at91 and avr32 can use.  In
>  Jamie> future we can use this to support other architectures that use the
>  Jamie> same IP block with the macb driver.
>
> How about at the same time renaming it to something a bit less generic
> like macb_platform_data?

The AT91RM9200 uses the same platform_data structure, but has a
different Ethernet peripheral (ie, not the MACB)

The phy_irq_pin is used in the AT91RM9200 ethernet driver.
(drivers/net/arm/at91_ether.c)


Regards,
  Andrew Victor

^ permalink raw reply

* Re: [PATCH] Add useful per-connection TCP stats for diagnosis purpose.
From: Eric Dumazet @ 2011-03-17  8:42 UTC (permalink / raw)
  To: H.K. Jerry Chu; +Cc: netdev
In-Reply-To: <1300349189-2731-1-git-send-email-hkchu@google.com>

Le jeudi 17 mars 2011 à 01:06 -0700, H.K. Jerry Chu a écrit :
> From: Jerry Chu <hkchu@google.com>
> 
> This patch add a number of very useful counters/stats (defined in
> tcp_stats.h) to help diagnosing TCP related problems.
> 
> create_time     - when the connection was created (in jiffies)
> total_inbytes   - total inbytes as consumed by the receiving apps.
> total_outbytes  - total outbytes sent down from the transmitting apps.
> 
> total_outdatasegs - total data carrying segments sent so far, including
> 		retransmitted ones.
> 
> total_xmit      - total accumulated time (usecs) when the connection
> 		has something to send.
> 
> total_retrans_time - total time (usecs, accumulated) the connection
> 		spends trying to recover lost packets. For each
> 		loss event the time is measured from the lost packet
> 		was first sent till the retransmitted packet was
> 		eventually ack'ed.
> 
> total_cwnd_limit - total time (usecs, excluding time spent on loss
>     		recovery) the xmit is stopped due to cwnd limited
> 
> total_swnd_limit - total time (usecs) theconnection is swnd limited
> 
> The following two counters are for listeners only:
> 
> accepted_reqs   - total # of accepted connection requests.
> listen_drops    - total # of dropped SYN reqs (SYN cookies excluded) due
>     		to listener's queue overflow.
> 
> total_retrans_time/total_retrans ratio gives a rough picture of how
> quickly in average the connection can recover from a pkt loss. E.g.,
> when the network is more congested, or the traffic contains mainly
> smaller RPC where tail drop often requires RTO to recover,
> the total_retrans_time/total_retrans ratio tends to be higher.
> 
> Currently the new counters/stats are exported through /proc/net/tcp.

Please dont. Use iproute2 instead.

> Some simple, abbreviated field names have been added to the output of
> /proc/net/tcp in order to allow backward/forward compatibility in the
> future. Obviously the new counters/stats can also be easily exported
> through other APIs.
> 

/proc/net/tcp is legacy. You should touch it eventually, but after
"other APIS" are done. It was the old way (quick but a bit ugly)



> Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
> ---
>  include/linux/ktime.h    |    3 ++
>  include/linux/tcp.h      |    1 +
>  include/net/tcp_stats.h  |   65 ++++++++++++++++++++++++++++++++++++++++++++++
>  net/ipv4/tcp.c           |   30 ++++++++++++++++++---
>  net/ipv4/tcp_input.c     |   13 +++++++++
>  net/ipv4/tcp_ipv4.c      |   41 ++++++++++++++++++++++++++---
>  net/ipv4/tcp_minisocks.c |    9 ++++++
>  net/ipv4/tcp_output.c    |   47 +++++++++++++++++++++++++++++++--
>  net/ipv6/tcp_ipv6.c      |    8 +++++
>  9 files changed, 206 insertions(+), 11 deletions(-)
>  create mode 100644 include/net/tcp_stats.h
> 
> diff --git a/include/linux/ktime.h b/include/linux/ktime.h
> index e1ceaa9..e60e758 100644
> --- a/include/linux/ktime.h
> +++ b/include/linux/ktime.h
> @@ -333,6 +333,9 @@ extern void ktime_get_ts(struct timespec *ts);
>  /* Get the real (wall-) time in timespec format: */
>  #define ktime_get_real_ts(ts)	getnstimeofday(ts)

Hmm, this kind of changes are out of netdev scope and should be avoided

>  
> +#define ktime_since(a)		ktime_to_us(ktime_sub(ktime_get(), (a)))

us are implied in ktime_since() ? thats strange.

> +#define ktime_zero(a)		ktime_equal((a), ktime_set(0, 0))

ktime_zero() sounds like : "give me zero time" or "clear the ktime
field". 

> +
>  static inline ktime_t ns_to_ktime(u64 ns)
>  {
>  	static const ktime_t ktime_zero = { .tv64 = 0 };
> diff --git a/include/linux/tcp.h b/include/linux/tcp.h
> index e64f4c6..ea5cb5d 100644
> --- a/include/linux/tcp.h
> +++ b/include/linux/tcp.h
> @@ -460,6 +460,7 @@ struct tcp_sock {
>  	 * contains related tcp_cookie_transactions fields.
>  	 */
>  	struct tcp_cookie_values  *cookie_values;
> +	struct tcp_stats	*conn_stats;
>  };

Really, using separate cache lines to store some stats is expensive.
You should add counters in existing structure, to avoid additional cache
line dirties. Carefully placing stats in already dirtied cache lines.

You also should use native ktime_t infrastructure, to make the maths
really fast in fast path.

Only when stats are to be returned to user, you'll have to convert the
native timestamps to user exportable ones.

Quite frankly, using u64 fields allow nanosec resolution.

BTW, we probably could 'export' sk->sk_drops for TCP, like we do for
UDP.




^ permalink raw reply

* Re: build breakage due to br_multicast.c referencing ipv6_dev_get_saddr()
From: Eric Dumazet @ 2011-03-17  8:23 UTC (permalink / raw)
  To: Jan Beulich; +Cc: Stephen Hemminger, davem, bridge, netdev, linus.luessing
In-Reply-To: <4D81CDF6020000780003700B@vpn.id2.novell.com>

Le jeudi 17 mars 2011 à 08:01 +0000, Jan Beulich a écrit :
> >>> On 16.03.11 at 16:24, Stephen Hemminger <shemminger@linux-foundation.org>
> wrote:
> > On Wed, 16 Mar 2011 12:34:19 +0000
> > "Jan Beulich" <JBeulich@novell.com> wrote:
> > 
> >> With BRIDGE=y and IPV6=m commit
> >> fe29ec41aaa51902aebd63658dfb04fe6fea8be5 ("bridge: Use IPv6
> >> link-local address for multicast listener queries") causes the build to
> >> break.
> > 
> > Rather than continue with the config games, lets just make the necessary
> > ipv6 pieces accessible.
> 
> The below (however ugly it may look) seems to do the trick for me,
> for this particular symbol. Possibly other symbols need doing the
> same (didn't check which ones e.g. infiniband depends on), so
> some sort of abstraction might be desirable to make the whole
> thing look less ugly.

You should check how things are properly done with RCU, because you must
make sure the module unload wont delete text another cpu is using.

net code usually use synchronize_{rcu|net}() calls.


example :

net/ipv4/netfilter/nf_nat_sip.c

static void __exit nf_nat_sip_fini(void)
{
        rcu_assign_pointer(nf_nat_sip_hook, NULL);
        rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, NULL);
        rcu_assign_pointer(nf_nat_sip_expect_hook, NULL);
        rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL);
        rcu_assign_pointer(nf_nat_sdp_port_hook, NULL);
        rcu_assign_pointer(nf_nat_sdp_session_hook, NULL);
        rcu_assign_pointer(nf_nat_sdp_media_hook, NULL);
        synchronize_rcu();
}




^ 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