* Re: Merging SSB and HND/AI support
From: Johannes Berg @ 2011-01-17 11:13 UTC (permalink / raw)
To: Michael Büsch
Cc: Jonas Gorski, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1295261783.24530.3.camel@maggie>
On Mon, 2011-01-17 at 11:56 +0100, Michael Büsch wrote:
> > As far as I can see, there are two possibilities:
> >
> > a) Merge the HND/AI code into the current SSB code, or
> >
> > b) add the missing code for SoCs to brcm80211 and replace the SSB code with it.
>
> Why can't we keep those two platforms separated?
> Is there really a lot of shared code between SSB and HND/AI?
I don't think there's a lot of shared code, but I believe that you need
b43 to be able to target cores on both? And b43 currently uses the SSB
APIs only.
johannes
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: Merging SSB and HND/AI support
From: Michael Büsch @ 2011-01-17 10:56 UTC (permalink / raw)
To: Jonas Gorski
Cc: linux-mips-6z/3iImG2C8G8FEW9MqTrA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <AANLkTi=GDcy50zsC6=Dgv1-Ty3cYK2qpx9o=q3JdXuCh-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Mon, 2011-01-17 at 11:46 +0100, Jonas Gorski wrote:
> Hello,
>
> I am currently looking into adding support for the newer Broadcom
> BCM47xx/53xx SoCs. They require having HND/AI support, which probably
> means merging the current SSB code and the HND/AI code from the
> brcm80211 driver. Is anyone already working on this?
>
> As far as I can see, there are two possibilities:
>
> a) Merge the HND/AI code into the current SSB code, or
>
> b) add the missing code for SoCs to brcm80211 and replace the SSB code with it.
Why can't we keep those two platforms separated?
Is there really a lot of shared code between SSB and HND/AI?
It's true that there's currently a lot of device functionality built
into ssb. Like pci bridge, mips core, extif, etc...
If you take all that code out, you're probably not left with anything.
So why do we need to replace or merge SSB in the first place? Can't
it co-exist with HND/AI?
--
Greetings Michael.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Merging SSB and HND/AI support
From: Jonas Gorski @ 2011-01-17 10:46 UTC (permalink / raw)
To: Michael Buesch, linux-mips, linux-wireless, netdev
Hello,
I am currently looking into adding support for the newer Broadcom
BCM47xx/53xx SoCs. They require having HND/AI support, which probably
means merging the current SSB code and the HND/AI code from the
brcm80211 driver. Is anyone already working on this?
As far as I can see, there are two possibilities:
a) Merge the HND/AI code into the current SSB code, or
b) add the missing code for SoCs to brcm80211 and replace the SSB code with it.
The former is probably the less intrusive one, but requires a bit of
ssb-named-but-actually-not-ssb code unless one renames several
functions and structs.
The latter has the advantage of having a certain bus abstraction
already built-in, but would require adapting the b43 code to it. It
also looks like it doesn't support (very) old SoCs.
Regards,
Jonas
P.S: The Maintainers file says SSB's list is netdev, but I would have
expected it to be linux-wireless. Is this still correct?
^ permalink raw reply
* Re: Flow Control and Port Mirroring Revisited
From: Michael S. Tsirkin @ 2011-01-17 10:38 UTC (permalink / raw)
To: Rusty Russell
Cc: Simon Horman, Jesse Gross, Eric Dumazet, virtualization, dev,
virtualization, netdev, kvm
In-Reply-To: <201101171026.26213.rusty@rustcorp.com.au>
On Mon, Jan 17, 2011 at 10:26:25AM +1030, Rusty Russell wrote:
> On Mon, 17 Jan 2011 09:07:30 am Simon Horman wrote:
>
> [snip]
>
> I've been away, but what concerns me is that socket buffer limits are
> bypassed in various configurations, due to skb cloning. We should probably
> drop such limits altogether, or fix them to be consistent.
Further, it looks like when the limits are not bypassed, they
easily result in deadlocks. For example, with
multiple tun devices attached to a single bridge in host,
if a number of these have their queues blocked,
others will reach the socket buffer limit and
traffic on the bridge will get blocked altogether.
It might be better to drop the limits altogether
unless we can fix them. Happily, as the limits are off by
default, doing so does not require kernel changes.
> Simple fix is as someone suggested here, to attach the clone. That might
> seriously reduce your sk limit, though. I haven't thought about it hard,
> but might it make sense to move ownership into skb_shared_info; ie. the
> data, rather than the skb head?
>
> Cheers,
> Rusty.
tracking data ownership might benefit others such as various zero-copy
strategies. It might need to be done per-page, though, not per-skb.
--
MST
^ permalink raw reply
* Re: [PATCH] net: bluetooth: fix locking problem
From: Andrei Emeltchenko @ 2011-01-17 10:28 UTC (permalink / raw)
To: Vasiliy Kulikov
Cc: kernel-janitors, Marcel Holtmann, Gustavo F. Padovan,
David S. Miller, linux-bluetooth, netdev, linux-kernel
In-Reply-To: <1295258917-31092-1-git-send-email-segoon@openwall.com>
On Mon, Jan 17, 2011 at 12:08 PM, Vasiliy Kulikov <segoon@openwall.com> wrote:
> If alloc_skb() failed we still hold hci_dev_list_lock. The code should
> unlock it before exit.
>
> Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
> ---
> Compile tested only.
>
> net/bluetooth/mgmt.c | 4 +++-
> 1 files changed, 3 insertions(+), 1 deletions(-)
>
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index f827fd9..ace8726 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -111,8 +111,10 @@ static int read_index_list(struct sock *sk)
>
> body_len = sizeof(*ev) + sizeof(*rp) + (2 * count);
> skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC);
> - if (!skb)
> + if (!skb) {
> + read_unlock(&hci_dev_list_lock);
> return -ENOMEM;
> + }
patch was send already on weekend
>
> hdr = (void *) skb_put(skb, sizeof(*hdr));
> hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
> --
> 1.7.0.4
>
> --
> 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/
>
^ permalink raw reply
* Re: Flow Control and Port Mirroring Revisited
From: Michael S. Tsirkin @ 2011-01-17 10:26 UTC (permalink / raw)
To: Simon Horman
Cc: Jesse Gross, Eric Dumazet, Rusty Russell, virtualization, dev,
virtualization, netdev, kvm
In-Reply-To: <20110116223728.GA6279@verge.net.au>
On Mon, Jan 17, 2011 at 07:37:30AM +0900, Simon Horman wrote:
> On Fri, Jan 14, 2011 at 08:54:15AM +0200, Michael S. Tsirkin wrote:
> > On Fri, Jan 14, 2011 at 03:35:28PM +0900, Simon Horman wrote:
> > > On Fri, Jan 14, 2011 at 06:58:18AM +0200, Michael S. Tsirkin wrote:
> > > > On Fri, Jan 14, 2011 at 08:41:36AM +0900, Simon Horman wrote:
> > > > > On Thu, Jan 13, 2011 at 10:45:38AM -0500, Jesse Gross wrote:
> > > > > > On Thu, Jan 13, 2011 at 1:47 AM, Simon Horman <horms@verge.net.au> wrote:
> > > > > > > On Mon, Jan 10, 2011 at 06:31:55PM +0900, Simon Horman wrote:
> > > > > > >> On Fri, Jan 07, 2011 at 10:23:58AM +0900, Simon Horman wrote:
> > > > > > >> > On Thu, Jan 06, 2011 at 05:38:01PM -0500, Jesse Gross wrote:
> > > > > > >> >
> > > > > > >> > [ snip ]
> > > > > > >> > >
> > > > > > >> > > I know that everyone likes a nice netperf result but I agree with
> > > > > > >> > > Michael that this probably isn't the right question to be asking. I
> > > > > > >> > > don't think that socket buffers are a real solution to the flow
> > > > > > >> > > control problem: they happen to provide that functionality but it's
> > > > > > >> > > more of a side effect than anything. It's just that the amount of
> > > > > > >> > > memory consumed by packets in the queue(s) doesn't really have any
> > > > > > >> > > implicit meaning for flow control (think multiple physical adapters,
> > > > > > >> > > all with the same speed instead of a virtual device and a physical
> > > > > > >> > > device with wildly different speeds). The analog in the physical
> > > > > > >> > > world that you're looking for would be Ethernet flow control.
> > > > > > >> > > Obviously, if the question is limiting CPU or memory consumption then
> > > > > > >> > > that's a different story.
> > > > > > >> >
> > > > > > >> > Point taken. I will see if I can control CPU (and thus memory) consumption
> > > > > > >> > using cgroups and/or tc.
> > > > > > >>
> > > > > > >> I have found that I can successfully control the throughput using
> > > > > > >> the following techniques
> > > > > > >>
> > > > > > >> 1) Place a tc egress filter on dummy0
> > > > > > >>
> > > > > > >> 2) Use ovs-ofctl to add a flow that sends skbs to dummy0 and then eth1,
> > > > > > >> this is effectively the same as one of my hacks to the datapath
> > > > > > >> that I mentioned in an earlier mail. The result is that eth1
> > > > > > >> "paces" the connection.
> > > >
> > > > This is actually a bug. This means that one slow connection will affect
> > > > fast ones. I intend to change the default for qemu to sndbuf=0 : this
> > > > will fix it but break your "pacing". So pls do not count on this
> > > > behaviour.
> > >
> > > Do you have a patch I could test?
> >
> > You can (and users already can) just run qemu with sndbuf=0. But if you
> > like, below.
>
> Thanks
>
> > > > > > > Further to this, I wonder if there is any interest in providing
> > > > > > > a method to switch the action order - using ovs-ofctl is a hack imho -
> > > > > > > and/or switching the default action order for mirroring.
> > > > > >
> > > > > > I'm not sure that there is a way to do this that is correct in the
> > > > > > generic case. It's possible that the destination could be a VM while
> > > > > > packets are being mirrored to a physical device or we could be
> > > > > > multicasting or some other arbitrarily complex scenario. Just think
> > > > > > of what a physical switch would do if it has ports with two different
> > > > > > speeds.
> > > > >
> > > > > Yes, I have considered that case. And I agree that perhaps there
> > > > > is no sensible default. But perhaps we could make it configurable somehow?
> > > >
> > > > The fix is at the application level. Run netperf with -b and -w flags to
> > > > limit the speed to a sensible value.
> > >
> > > Perhaps I should have stated my goals more clearly.
> > > I'm interested in situations where I don't control the application.
> >
> > Well an application that streams UDP without any throttling
> > at the application level will break on a physical network, right?
> > So I am not sure why should one try to make it work on the virtual one.
> >
> > But let's assume that you do want to throttle the guest
> > for reasons such as QOS. The proper approach seems
> > to be to throttle the sender, not have a dummy throttled
> > receiver "pacing" it. Place the qemu process in the
> > correct net_cls cgroup, set the class id and apply a rate limit?
>
> I would like to be able to use a class to rate limit egress packets.
> That much works fine for me.
>
> What I would also like is for there to be back-pressure such that the guest
> doesn't consume lots of CPU, spinning, sending packets as fast as it can,
> almost of all of which are dropped. That does seem like a lot of wasted
> CPU to me.
>
> Unfortunately there are several problems with this and I am fast concluding
> that I will need to use a CPU cgroup. Which does make some sense, as what I
> am really trying to limit here is CPU usage not network packet rates - even
> if the test using the CPU is netperf. So long as the CPU usage can
> (mostly) be attributed to the guest using a cgroup should work fine. And
> indeed seems to in my limited testing.
>
> One scenario in which I don't think it is possible for there to be
> back-pressure in a meaningful sense is if root in the guest sets
> /proc/sys/net/core/wmem_default to a large value, say 2000000.
>
>
> I do think that to some extent there is back-pressure provided by sockbuf
> in the case where process on the host is sending directly to a physical
> interface. And to my mind it would be "nice" if the same kind of
> back-pressure was present in guests. But through our discussions of the
> past week or so I get the feeling that is not your view of things.
It might be nice. Unfortunately this is not what we have implemented:
the sockbuf backpressure blocks the socket, what we have blocks all
transmit from the guest. Another issue is that the strategy we have
seems to be broken if the target is a guest on another machine.
So it won't be all that simple to implement well, and before we try,
I'd like to know whether there are applications that are helped
by it. For example, we could try to measure latency at various
pps and see whether the backpressure helps. netperf has -b, -w
flags which might help these measurements.
> Perhaps I could characterise the guest situation by saying:
> Egress packet rates can be controlled using tc on the host;
> Guest CPU usage can be controlled using CPU cgroups on the host;
> Sockbuf controls memory usage on the host;
Not really, the memory usage on the host is controlled by the
various queue lengths in the host. E.g. if you send packets to
the physical device, they will get queued there.
> Back-pressure is irrelevant.
Or at least, broken :)
--
MST
^ permalink raw reply
* [PATCH] net: bluetooth: fix locking problem
From: Vasiliy Kulikov @ 2011-01-17 10:08 UTC (permalink / raw)
To: kernel-janitors
Cc: Marcel Holtmann, Gustavo F. Padovan, David S. Miller,
linux-bluetooth, netdev, linux-kernel
If alloc_skb() failed we still hold hci_dev_list_lock. The code should
unlock it before exit.
Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
---
Compile tested only.
net/bluetooth/mgmt.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index f827fd9..ace8726 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -111,8 +111,10 @@ static int read_index_list(struct sock *sk)
body_len = sizeof(*ev) + sizeof(*rp) + (2 * count);
skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC);
- if (!skb)
+ if (!skb) {
+ read_unlock(&hci_dev_list_lock);
return -ENOMEM;
+ }
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
--
1.7.0.4
^ permalink raw reply related
* Re: [PATCH 3/3] vhost-net: use lock_sock_fast() in peek_head_len()
From: Michael S. Tsirkin @ 2011-01-17 9:57 UTC (permalink / raw)
To: Jason Wang; +Cc: virtualization, netdev, kvm, linux-kernel, eric.dumazet
In-Reply-To: <20110117081117.18900.48672.stgit@dhcp-91-7.nay.redhat.com.englab.nay.redhat.com>
On Mon, Jan 17, 2011 at 04:11:17PM +0800, Jason Wang wrote:
> We can use lock_sock_fast() instead of lock_sock() in order to get
> speedup in peek_head_len().
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
Queued for 2.6.39, thanks everyone.
> ---
> drivers/vhost/net.c | 4 ++--
> 1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index c32a2e4..50b622a 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -211,12 +211,12 @@ static int peek_head_len(struct sock *sk)
> {
> struct sk_buff *head;
> int len = 0;
> + bool slow = lock_sock_fast(sk);
>
> - lock_sock(sk);
> head = skb_peek(&sk->sk_receive_queue);
> if (head)
> len = head->len;
> - release_sock(sk);
> + unlock_sock_fast(sk, slow);
> return len;
> }
>
^ permalink raw reply
* Re: rps testing questions
From: Eric Dumazet @ 2011-01-17 9:53 UTC (permalink / raw)
To: mi wake; +Cc: netdev
In-Reply-To: <AANLkTin1pC=auiFBt83YomdhVgUO8uSdvq=tPaDu0=3U@mail.gmail.com>
Le lundi 17 janvier 2011 à 17:43 +0800, mi wake a écrit :
> I do a rps(Receive Packet Steering) testing on centos 5.5 with kernel 2.6.37.
> cpu: 8 core Intel.
> ethernet adapter: bnx2x
>
> Problem statement:
> enable rps with:
> echo "ff" > /sys/class/net/eth2/queues/rx-0/rps_cpus.
>
bnx2x with one queue only ?
> running 1 instances of netperf TCP_RR: netperf -t TCP_RR -H 192.168.0.1 -c -C
> without rps: 9963.48(Trans Rate per sec)
> with rps: 9387.59(Trans Rate per sec)
>
> I do ab and tbench testing also find there is less tps with enable
> rps.but,there is more cpu using when with enable rps.when with enable
> rps ,softirqs is blanced on cpus.
Really ? that seems unlikely with your one flow test, unless you _also_
have hardware IRQS hitting all your cpus. (That would be very bad)
>
> is there something wrong with my test?
> --
If you test with one flow, RPS brings nothing at all. Better handle the
packet directly from the cpu handling the hardware IRQ (and NAPI)
You better make sure hardware IRQ are on one cpu, instead of many cpus.
^ permalink raw reply
* rps testing questions
From: mi wake @ 2011-01-17 9:43 UTC (permalink / raw)
To: netdev
I do a rps(Receive Packet Steering) testing on centos 5.5 with kernel 2.6.37.
cpu: 8 core Intel.
ethernet adapter: bnx2x
Problem statement:
enable rps with:
echo "ff" > /sys/class/net/eth2/queues/rx-0/rps_cpus.
running 1 instances of netperf TCP_RR: netperf -t TCP_RR -H 192.168.0.1 -c -C
without rps: 9963.48(Trans Rate per sec)
with rps: 9387.59(Trans Rate per sec)
I do ab and tbench testing also find there is less tps with enable
rps.but,there is more cpu using when with enable rps.when with enable
rps ,softirqs is blanced on cpus.
is there something wrong with my test?
^ permalink raw reply
* Re: [PATCH] virtio-net: fix a typo
From: Michael S. Tsirkin @ 2011-01-17 9:42 UTC (permalink / raw)
To: Aurelien Jarno; +Cc: linux-kernel, netdev, virtualization, rusty
In-Reply-To: <1295252059-27438-1-git-send-email-aurelien@aurel32.net>
On Mon, Jan 17, 2011 at 09:14:19AM +0100, Aurelien Jarno wrote:
> fitler -> filter
>
> Cc: netdev@vger.kernel.org
> Cc: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
obviously.
Cc:
virtualization@lists.linux-foundation.org
rusty@rustcorp.com.au
as well.
BTW Rusty, ./scripts/get_maintainer.pl doesn't work
for most of virtio since there is no entry in MAINTAINERS for virtio
net/block/core. Adding it will help people know whom to contact
with issues/patches. It will also help if
I am Cc'd on patches as this way I won't forget to
review them.
--->
Add MAINTAINERS entry for virtio core, net, block
Patches should keep coming through Rusty but it
helps if I'm Cc'd as well.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
----
Comments?
diff --git a/MAINTAINERS b/MAINTAINERS
index 23d0436..7de75ad 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6439,6 +6439,16 @@ S: Maintained
F: drivers/char/virtio_console.c
F: include/linux/virtio_console.h
+VIRTIO CORE, NET AND BLOCK DRIVERS
+M: Rusty Russell <rusty@rustcorp.com.au>
+M: "Michael S. Tsirkin" <mst@redhat.com>
+L: virtualization@lists.linux-foundation.org
+S: Maintained
+F: drivers/virtio/
+F: drivers/net/virtio_net.c
+F: drivers/block/virtio_blk.c
+F: include/linux/virtio_*.h
+
VIRTIO HOST (VHOST)
M: "Michael S. Tsirkin" <mst@redhat.com>
L: kvm@vger.kernel.org
^ permalink raw reply related
* Re: [PATCH 3/3] vhost-net: use lock_sock_fast() in peek_head_len()
From: Eric Dumazet @ 2011-01-17 9:33 UTC (permalink / raw)
To: Jason Wang; +Cc: virtualization, mst, linux-kernel, kvm, netdev
In-Reply-To: <20110117081117.18900.48672.stgit@dhcp-91-7.nay.redhat.com.englab.nay.redhat.com>
Le lundi 17 janvier 2011 à 16:11 +0800, Jason Wang a écrit :
> We can use lock_sock_fast() instead of lock_sock() in order to get
> speedup in peek_head_len().
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
> ---
> drivers/vhost/net.c | 4 ++--
> 1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index c32a2e4..50b622a 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -211,12 +211,12 @@ static int peek_head_len(struct sock *sk)
> {
> struct sk_buff *head;
> int len = 0;
> + bool slow = lock_sock_fast(sk);
>
> - lock_sock(sk);
> head = skb_peek(&sk->sk_receive_queue);
> if (head)
> len = head->len;
> - release_sock(sk);
> + unlock_sock_fast(sk, slow);
> return len;
> }
>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH] dummy: do not create a link (dummy0) at module init by default
From: Jarek Poplawski @ 2011-01-17 9:25 UTC (permalink / raw)
To: David Ward; +Cc: netdev, shemminger
In-Reply-To: <1295225393-5779-1-git-send-email-david.ward@ll.mit.edu>
On 2011-01-17 01:49, David Ward wrote:
> When the dummy network driver is initialized with no parameters, a link
> is automatically created (named 'dummy0'). This is inconsistent with
> other virtual network drivers such as veth, macvlan, and macvtap, which
> do not create a link upon initialization.
Actually, this is consistent with _other_ virtual network drivers such
as loopback, ifb, and teql, which create a link upon initialization ;-)
Jarek P.
^ permalink raw reply
* [PATCH v2] net: add Faraday FTMAC100 10/100 Ethernet driver
From: Po-Yu Chuang @ 2011-01-17 9:21 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, ratbert, bhutchings, eric.dumazet, joe, dilinger
In-Reply-To: <1294919372-1904-1-git-send-email-ratbert.chuang@gmail.com>
From: Po-Yu Chuang <ratbert@faraday-tech.com>
FTMAC100 Ethernet Media Access Controller supports 10/100 Mbps and
MII. This driver has been working on some ARM/NDS32 SoC's including
Faraday A320 and Andes AG101.
Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
---
v2:
always use NAPI
do not use our own net_device_stats structure
don't set trans_start and last_rx
stats.rx_packets and stats.rx_bytes include dropped packets
add missed netif_napi_del()
initialize spinlocks in probe function
remove rx_lock and hw_lock
use netdev_[err/info/dbg] instead of dev_* ones
use netdev_alloc_skb_ip_align()
remove ftmac100_get_stats()
use is_valid_ether_addr() instead of is_zero_ether_addr()
add const to ftmac100_ethtool_ops and ftmac100_netdev_ops
use net_ratelimit() instead of printk_ratelimit()
no explicit inline
use %pM to print MAC address
add comment before wmb
use napi poll() to handle all interrupts
As I said in the previous mail, this controller is used not only on ARM,
so I still leave it in drivers/net/. If there is any suggestion, please
let me know. Thanks
drivers/net/Kconfig | 9 +
drivers/net/Makefile | 1 +
drivers/net/ftmac100.c | 1223 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/ftmac100.h | 180 +++++++
4 files changed, 1413 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/ftmac100.c
create mode 100644 drivers/net/ftmac100.h
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 3fda24a..0720acc 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2014,6 +2014,15 @@ config BCM63XX_ENET
This driver supports the ethernet MACs in the Broadcom 63xx
MIPS chipset family (BCM63XX).
+config FTMAC100
+ tristate "Faraday FTMAC100 10/100 Ethernet support"
+ depends on ARM
+ select MII
+ help
+ This driver supports the FTMAC100 Ethernet controller from
+ Faraday. It is used on Faraday A320, Andes AG101, AG101P
+ and some other ARM/NDS32 SoC's.
+
source "drivers/net/fs_enet/Kconfig"
source "drivers/net/octeon/Kconfig"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90738d..7c21711 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -147,6 +147,7 @@ obj-$(CONFIG_FORCEDETH) += forcedeth.o
obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
obj-$(CONFIG_AX88796) += ax88796.o
obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
+obj-$(CONFIG_FTMAC100) += ftmac100.o
obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c
new file mode 100644
index 0000000..33b0c77
--- /dev/null
+++ b/drivers/net/ftmac100.c
@@ -0,0 +1,1223 @@
+/*
+ * Faraday FTMAC100 10/100 Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+
+#include "ftmac100.h"
+
+#define DRV_NAME "ftmac100"
+#define DRV_VERSION "0.1"
+
+#define RX_QUEUE_ENTRIES 128 /* must be power of 2 */
+#define TX_QUEUE_ENTRIES 16 /* must be power of 2 */
+
+#define MAX_PKT_SIZE 1518
+#define RX_BUF_SIZE 2044 /* must be smaller than 0x7ff */
+
+/******************************************************************************
+ * priveate data
+ *****************************************************************************/
+struct ftmac100_descs {
+ struct ftmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
+ struct ftmac100_txdes txdes[TX_QUEUE_ENTRIES];
+};
+
+struct ftmac100 {
+ struct resource *res;
+ void *base;
+ int irq;
+
+ struct ftmac100_descs *descs;
+ dma_addr_t descs_dma_addr;
+
+ unsigned int rx_pointer;
+ unsigned int tx_clean_pointer;
+ unsigned int tx_pointer;
+ unsigned int tx_pending;
+
+ spinlock_t tx_lock;
+
+ struct net_device *netdev;
+ struct device *dev;
+ struct napi_struct napi;
+
+ struct mii_if_info mii;
+};
+
+/******************************************************************************
+ * internal functions (hardware register access)
+ *****************************************************************************/
+static void ftmac100_enable_all_int(struct ftmac100 *priv)
+{
+ unsigned int imr;
+
+ imr = FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF
+ | FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST
+ | FTMAC100_INT_RPKT_LOST | FTMAC100_INT_AHB_ERR
+ | FTMAC100_INT_PHYSTS_CHG;
+ iowrite32(imr, priv->base + FTMAC100_OFFSET_IMR);
+}
+
+static void ftmac100_disable_all_int(struct ftmac100 *priv)
+{
+ iowrite32(0, priv->base + FTMAC100_OFFSET_IMR);
+}
+
+static void ftmac100_set_receive_ring_base(struct ftmac100 *priv,
+ dma_addr_t addr)
+{
+ iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR);
+}
+
+static void ftmac100_set_transmit_ring_base(struct ftmac100 *priv,
+ dma_addr_t addr)
+{
+ iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR);
+}
+
+static void ftmac100_txdma_start_polling(struct ftmac100 *priv)
+{
+ iowrite32(1, priv->base + FTMAC100_OFFSET_TXPD);
+}
+
+static int ftmac100_reset(struct ftmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ int i;
+
+ /* NOTE: reset clears all registers */
+ iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR);
+
+ for (i = 0; i < 5; i++) {
+ int maccr;
+
+ maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
+ if (!(maccr & FTMAC100_MACCR_SW_RST)) {
+ /*
+ * FTMAC100_MACCR_SW_RST cleared does not indicate
+ * that hardware reset completed (what the f*ck).
+ * We still need to wait for a while.
+ */
+ usleep_range(500, 1000);
+ return 0;
+ }
+
+ usleep_range(1000, 10000);
+ }
+
+ netdev_err(netdev, "software reset failed\n");
+ return -EIO;
+}
+
+static void ftmac100_set_mac(struct ftmac100 *priv, const unsigned char *mac)
+{
+ unsigned int maddr = mac[0] << 8 | mac[1];
+ unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+
+ iowrite32(maddr, priv->base + FTMAC100_OFFSET_MAC_MADR);
+ iowrite32(laddr, priv->base + FTMAC100_OFFSET_MAC_LADR);
+}
+
+static int ftmac100_start_hw(struct ftmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ int maccr;
+
+ if (ftmac100_reset(priv))
+ return -EIO;
+
+ /* setup ring buffer base registers */
+
+ ftmac100_set_receive_ring_base(priv,
+ priv->descs_dma_addr + offsetof(struct ftmac100_descs, rxdes));
+ ftmac100_set_transmit_ring_base(priv,
+ priv->descs_dma_addr + offsetof(struct ftmac100_descs, txdes));
+
+ iowrite32(FTMAC100_APTC_RXPOLL_CNT(1),
+ priv->base + FTMAC100_OFFSET_APTC);
+
+ ftmac100_set_mac(priv, netdev->dev_addr);
+
+ maccr = FTMAC100_MACCR_XMT_EN |
+ FTMAC100_MACCR_RCV_EN |
+ FTMAC100_MACCR_XDMA_EN |
+ FTMAC100_MACCR_RDMA_EN |
+ FTMAC100_MACCR_CRC_APD |
+ FTMAC100_MACCR_FULLDUP |
+ FTMAC100_MACCR_RX_RUNT |
+ FTMAC100_MACCR_RX_BROADPKT;
+
+ iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR);
+ return 0;
+}
+
+static void ftmac100_stop_hw(struct ftmac100 *priv)
+{
+ iowrite32(0, priv->base + FTMAC100_OFFSET_MACCR);
+}
+
+/******************************************************************************
+ * internal functions (receive descriptor)
+ *****************************************************************************/
+static int ftmac100_rxdes_first_segment(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_FRS;
+}
+
+static int ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_LRS;
+}
+
+static int ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RXDMA_OWN;
+}
+
+static void ftmac100_rxdes_set_dma_own(struct ftmac100_rxdes *rxdes)
+{
+ /* clear status bits */
+ rxdes->rxdes0 = FTMAC100_RXDES0_RXDMA_OWN;
+}
+
+static int ftmac100_rxdes_rx_error(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RX_ERR;
+}
+
+static int ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_CRC_ERR;
+}
+
+static int ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_FTL;
+}
+
+static int ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RUNT;
+}
+
+static int ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RX_ODD_NB;
+}
+
+static unsigned int ftmac100_rxdes_frame_length(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RFL;
+}
+
+static int ftmac100_rxdes_multicast(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_MULTICAST;
+}
+
+static void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes,
+ unsigned int size)
+{
+ rxdes->rxdes1 = (rxdes->rxdes1 & FTMAC100_RXDES1_EDORR) |
+ FTMAC100_RXDES1_RXBUF_SIZE(size);
+}
+
+static void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes)
+{
+ rxdes->rxdes1 |= FTMAC100_RXDES1_EDORR;
+}
+
+static void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes,
+ dma_addr_t addr)
+{
+ rxdes->rxdes2 = addr;
+}
+
+static dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes2;
+}
+
+/* rxdes3 is not used by hardware, we use it to keep track of buffer */
+static void ftmac100_rxdes_set_va(struct ftmac100_rxdes *rxdes, void *addr)
+{
+ rxdes->rxdes3 = (unsigned int)addr;
+}
+
+static void *ftmac100_rxdes_get_va(struct ftmac100_rxdes *rxdes)
+{
+ return (void *)rxdes->rxdes3;
+}
+
+/******************************************************************************
+ * internal functions (receive)
+ *****************************************************************************/
+static int ftmac100_next_rx_pointer(int pointer)
+{
+ return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
+}
+
+static void ftmac100_rx_pointer_advance(struct ftmac100 *priv)
+{
+ priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer);
+}
+
+static struct ftmac100_rxdes *ftmac100_current_rxdes(struct ftmac100 *priv)
+{
+ return &priv->descs->rxdes[priv->rx_pointer];
+}
+
+static struct ftmac100_rxdes *ftmac100_rx_locate_first_segment(
+ struct ftmac100 *priv)
+{
+ struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
+
+ while (!ftmac100_rxdes_owned_by_dma(rxdes)) {
+ if (ftmac100_rxdes_first_segment(rxdes))
+ return rxdes;
+
+ ftmac100_rxdes_set_dma_own(rxdes);
+ ftmac100_rx_pointer_advance(priv);
+ rxdes = ftmac100_current_rxdes(priv);
+ }
+
+ return NULL;
+}
+
+static int ftmac100_rx_packet_error(struct ftmac100 *priv,
+ struct ftmac100_rxdes *rxdes)
+{
+ struct net_device *netdev = priv->netdev;
+ int error = 0;
+
+ if (unlikely(ftmac100_rxdes_rx_error(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx err\n");
+
+ netdev->stats.rx_errors++;
+ error = 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_crc_error(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx crc err\n");
+
+ netdev->stats.rx_crc_errors++;
+ error = 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_frame_too_long(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx frame too long\n");
+
+ netdev->stats.rx_length_errors++;
+ error = 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_runt(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx runt\n");
+
+ netdev->stats.rx_length_errors++;
+ error = 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_odd_nibble(rxdes))) {
+ if (net_ratelimit())
+ netdev_info(netdev, "rx odd nibble\n");
+
+ netdev->stats.rx_length_errors++;
+ error = 1;
+ }
+
+ return error;
+}
+
+static void ftmac100_rx_drop_packet(struct ftmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
+ int done = 0;
+
+ if (net_ratelimit())
+ netdev_dbg(netdev, "drop packet %p\n", rxdes);
+
+ do {
+ if (ftmac100_rxdes_last_segment(rxdes))
+ done = 1;
+
+ ftmac100_rxdes_set_dma_own(rxdes);
+ ftmac100_rx_pointer_advance(priv);
+ rxdes = ftmac100_current_rxdes(priv);
+ } while (!done && !ftmac100_rxdes_owned_by_dma(rxdes));
+
+ netdev->stats.rx_dropped++;
+}
+
+static int ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftmac100_rxdes *rxdes;
+ struct sk_buff *skb;
+ int length;
+ int copied = 0;
+ int done = 0;
+
+ rxdes = ftmac100_rx_locate_first_segment(priv);
+ if (!rxdes)
+ return 0;
+
+ length = ftmac100_rxdes_frame_length(rxdes);
+
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += length;
+
+ if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) {
+ ftmac100_rx_drop_packet(priv);
+ return 1;
+ }
+
+ /* start processing */
+ skb = netdev_alloc_skb_ip_align(netdev, length);
+ if (unlikely(!skb)) {
+ if (net_ratelimit())
+ netdev_err(netdev, "rx skb alloc failed\n");
+
+ ftmac100_rx_drop_packet(priv);
+ return 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_multicast(rxdes)))
+ netdev->stats.multicast++;
+
+ do {
+ dma_addr_t d = ftmac100_rxdes_get_dma_addr(rxdes);
+ void *buf = ftmac100_rxdes_get_va(rxdes);
+ int size;
+
+ size = min(length - copied, RX_BUF_SIZE);
+
+ dma_sync_single_for_cpu(priv->dev, d, RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ memcpy(skb_put(skb, size), buf, size);
+
+ copied += size;
+
+ if (ftmac100_rxdes_last_segment(rxdes))
+ done = 1;
+
+ dma_sync_single_for_device(priv->dev, d, RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ ftmac100_rxdes_set_dma_own(rxdes);
+
+ ftmac100_rx_pointer_advance(priv);
+ rxdes = ftmac100_current_rxdes(priv);
+ } while (!done && copied < length);
+
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ /* push packet to protocol stack */
+ netif_receive_skb(skb);
+
+ (*processed)++;
+ return 1;
+}
+
+/******************************************************************************
+ * internal functions (transmit descriptor)
+ *****************************************************************************/
+static void ftmac100_txdes_reset(struct ftmac100_txdes *txdes)
+{
+ /* clear all except end of ring bit */
+ txdes->txdes0 = 0;
+ txdes->txdes1 &= FTMAC100_TXDES1_EDOTR;
+ txdes->txdes2 = 0;
+ txdes->txdes3 = 0;
+}
+
+static int ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes)
+{
+ return txdes->txdes0 & FTMAC100_TXDES0_TXDMA_OWN;
+}
+
+static void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes)
+{
+ /*
+ * Make sure dma own bit will not be set before any other
+ * descriptor fiels.
+ */
+ wmb();
+
+ txdes->txdes0 |= FTMAC100_TXDES0_TXDMA_OWN;
+}
+
+static int ftmac100_txdes_excessive_collision(struct ftmac100_txdes *txdes)
+{
+ return txdes->txdes0 & FTMAC100_TXDES0_TXPKT_EXSCOL;
+}
+
+static int ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes)
+{
+ return txdes->txdes0 & FTMAC100_TXDES0_TXPKT_LATECOL;
+}
+
+static void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_EDOTR;
+}
+
+static void ftmac100_txdes_set_first_segment(struct ftmac100_txdes *txdes)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_FTS;
+}
+
+static void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_LTS;
+}
+
+static void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_TXIC;
+}
+
+static void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes,
+ unsigned int len)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_TXBUF_SIZE(len);
+}
+
+static void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes,
+ dma_addr_t addr)
+{
+ txdes->txdes2 = addr;
+}
+
+static dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes)
+{
+ return txdes->txdes2;
+}
+
+/* txdes3 is not used by hardware, we use it to keep track of socket buffer */
+static void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes,
+ struct sk_buff *skb)
+{
+ txdes->txdes3 = (unsigned int)skb;
+}
+
+static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes)
+{
+ return (struct sk_buff *)txdes->txdes3;
+}
+
+/******************************************************************************
+ * internal functions (transmit)
+ *****************************************************************************/
+static int ftmac100_next_tx_pointer(int pointer)
+{
+ return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
+}
+
+static void ftmac100_tx_pointer_advance(struct ftmac100 *priv)
+{
+ priv->tx_pointer = ftmac100_next_tx_pointer(priv->tx_pointer);
+}
+
+static void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv)
+{
+ priv->tx_clean_pointer =
+ ftmac100_next_tx_pointer(priv->tx_clean_pointer);
+}
+
+static struct ftmac100_txdes *ftmac100_current_txdes(struct ftmac100 *priv)
+{
+ return &priv->descs->txdes[priv->tx_pointer];
+}
+
+static struct ftmac100_txdes *ftmac100_current_clean_txdes(
+ struct ftmac100 *priv)
+{
+ return &priv->descs->txdes[priv->tx_clean_pointer];
+}
+
+static int ftmac100_tx_complete_packet(struct ftmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftmac100_txdes *txdes;
+ struct sk_buff *skb;
+ dma_addr_t map;
+
+ if (priv->tx_pending == 0)
+ return 0;
+
+ txdes = ftmac100_current_clean_txdes(priv);
+
+ if (ftmac100_txdes_owned_by_dma(txdes))
+ return 0;
+
+ skb = ftmac100_txdes_get_skb(txdes);
+ map = ftmac100_txdes_get_dma_addr(txdes);
+
+ if (unlikely(ftmac100_txdes_excessive_collision(txdes) ||
+ ftmac100_txdes_late_collision(txdes))) {
+ /*
+ * packet transmitted to ethernet lost due to late collision
+ * or excessive collision
+ */
+ netdev->stats.tx_aborted_errors++;
+ } else {
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += skb->len;
+ }
+
+ dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
+
+ dev_kfree_skb_irq(skb);
+
+ ftmac100_txdes_reset(txdes);
+
+ ftmac100_tx_clean_pointer_advance(priv);
+
+ priv->tx_pending--;
+ netif_wake_queue(netdev);
+
+ return 1;
+}
+
+static void ftmac100_tx_complete(struct ftmac100 *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ while (ftmac100_tx_complete_packet(priv))
+ ;
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+}
+
+static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
+ dma_addr_t map)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftmac100_txdes *txdes;
+ unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+ unsigned long flags;
+
+ txdes = ftmac100_current_txdes(priv);
+ ftmac100_tx_pointer_advance(priv);
+
+ /* setup TX descriptor */
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ ftmac100_txdes_set_skb(txdes, skb);
+ ftmac100_txdes_set_dma_addr(txdes, map);
+
+ ftmac100_txdes_set_first_segment(txdes);
+ ftmac100_txdes_set_last_segment(txdes);
+ ftmac100_txdes_set_txint(txdes);
+ ftmac100_txdes_set_buffer_size(txdes, len);
+
+ priv->tx_pending++;
+ if (priv->tx_pending == TX_QUEUE_ENTRIES) {
+ if (net_ratelimit())
+ netdev_info(netdev, "tx queue full\n");
+
+ netif_stop_queue(netdev);
+ }
+
+ /* start transmit */
+ ftmac100_txdes_set_dma_own(txdes);
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ ftmac100_txdma_start_polling(priv);
+
+ return NETDEV_TX_OK;
+}
+
+/******************************************************************************
+ * internal functions (buffer)
+ *****************************************************************************/
+static void ftmac100_free_buffers(struct ftmac100 *priv)
+{
+ int i;
+
+ for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {
+ struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+ dma_addr_t d = ftmac100_rxdes_get_dma_addr(rxdes);
+ void *page = ftmac100_rxdes_get_va(rxdes);
+
+ if (d)
+ dma_unmap_single(priv->dev, d, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+
+ if (page != NULL)
+ free_page((unsigned long)page);
+ }
+
+ for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
+ struct ftmac100_txdes *txdes = &priv->descs->txdes[i];
+ struct sk_buff *skb = ftmac100_txdes_get_skb(txdes);
+
+ if (skb) {
+ dma_addr_t map;
+
+ map = ftmac100_txdes_get_dma_addr(txdes);
+ dma_unmap_single(priv->dev, map, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ dev_kfree_skb(skb);
+ }
+ }
+
+ dma_free_coherent(priv->dev, sizeof(struct ftmac100_descs),
+ priv->descs, priv->descs_dma_addr);
+}
+
+static int ftmac100_alloc_buffers(struct ftmac100 *priv)
+{
+ int i;
+
+ priv->descs = dma_alloc_coherent(priv->dev,
+ sizeof(struct ftmac100_descs), &priv->descs_dma_addr,
+ GFP_KERNEL | GFP_DMA);
+ if (priv->descs == NULL)
+ return -ENOMEM;
+
+ memset(priv->descs, 0, sizeof(struct ftmac100_descs));
+
+ /* initialize RX ring */
+
+ ftmac100_rxdes_set_end_of_ring(
+ &priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
+
+ for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {
+ struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+ void *page;
+ dma_addr_t d;
+
+ page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+ if (page == NULL)
+ goto err;
+
+ d = dma_map_single(priv->dev, page, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, d))) {
+ free_page((unsigned long)page);
+ goto err;
+ }
+
+ /*
+ * The hardware enforces a sub-2K maximum packet size, so we
+ * put two buffers on every hardware page.
+ */
+ ftmac100_rxdes_set_va(rxdes, page);
+ ftmac100_rxdes_set_va(rxdes + 1, page + PAGE_SIZE / 2);
+
+ ftmac100_rxdes_set_dma_addr(rxdes, d);
+ ftmac100_rxdes_set_dma_addr(rxdes + 1, d + PAGE_SIZE / 2);
+
+ ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE);
+ ftmac100_rxdes_set_buffer_size(rxdes + 1, RX_BUF_SIZE);
+
+ ftmac100_rxdes_set_dma_own(rxdes);
+ ftmac100_rxdes_set_dma_own(rxdes + 1);
+ }
+
+ /* initialize TX ring */
+
+ ftmac100_txdes_set_end_of_ring(
+ &priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
+ return 0;
+
+err:
+ ftmac100_free_buffers(priv);
+ return -ENOMEM;
+}
+
+/******************************************************************************
+ * struct mii_if_info functions
+ *****************************************************************************/
+static int ftmac100_mdio_read(struct net_device *netdev, int phy_id, int reg)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ int phycr;
+ int i;
+
+ phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
+ FTMAC100_PHYCR_REGAD(reg) |
+ FTMAC100_PHYCR_MIIRD;
+
+ iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
+ for (i = 0; i < 10; i++) {
+ phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
+
+ if ((phycr & FTMAC100_PHYCR_MIIRD) == 0)
+ return phycr & FTMAC100_PHYCR_MIIRDATA;
+
+ usleep_range(100, 1000);
+ }
+
+ netdev_err(netdev, "mdio read timed out\n");
+ return 0xffff;
+}
+
+static void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg,
+ int data)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ int phycr;
+ int i;
+
+ phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
+ FTMAC100_PHYCR_REGAD(reg) |
+ FTMAC100_PHYCR_MIIWR;
+
+ data = FTMAC100_PHYWDATA_MIIWDATA(data);
+
+ iowrite32(data, priv->base + FTMAC100_OFFSET_PHYWDATA);
+ iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
+
+ for (i = 0; i < 10; i++) {
+ phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
+
+ if ((phycr & FTMAC100_PHYCR_MIIWR) == 0)
+ return;
+
+ usleep_range(100, 1000);
+ }
+
+ netdev_err(netdev, "mdio write timed out\n");
+}
+
+/******************************************************************************
+ * struct ethtool_ops functions
+ *****************************************************************************/
+static void ftmac100_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, dev_name(&netdev->dev));
+}
+
+static int ftmac100_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return mii_ethtool_gset(&priv->mii, cmd);
+}
+
+static int ftmac100_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return mii_ethtool_sset(&priv->mii, cmd);
+}
+
+static int ftmac100_nway_reset(struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return mii_nway_restart(&priv->mii);
+}
+
+static u32 ftmac100_get_link(struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return mii_link_ok(&priv->mii);
+}
+
+static const struct ethtool_ops ftmac100_ethtool_ops = {
+ .set_settings = ftmac100_set_settings,
+ .get_settings = ftmac100_get_settings,
+ .get_drvinfo = ftmac100_get_drvinfo,
+ .nway_reset = ftmac100_nway_reset,
+ .get_link = ftmac100_get_link,
+};
+
+/******************************************************************************
+ * interrupt handler
+ *****************************************************************************/
+static irqreturn_t ftmac100_interrupt(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct ftmac100 *priv = netdev_priv(netdev);
+
+ if (likely(netif_running(netdev))) {
+ /* Disable interrupts for polling */
+ ftmac100_disable_all_int(priv);
+ napi_schedule(&priv->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/******************************************************************************
+ * struct napi_struct functions
+ *****************************************************************************/
+static int ftmac100_poll(struct napi_struct *napi, int budget)
+{
+ struct ftmac100 *priv = container_of(napi, struct ftmac100, napi);
+ struct net_device *netdev = priv->netdev;
+ unsigned int status;
+ int completed = 1;
+ int rx = 0;
+
+ status = ioread32(priv->base + FTMAC100_OFFSET_ISR);
+
+ if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) {
+ /*
+ * FTMAC100_INT_RPKT_FINISH:
+ * RX DMA has received packets into RX buffer successfully
+ *
+ * FTMAC100_INT_NORXBUF:
+ * RX buffer unavailable
+ */
+ int retry;
+
+ do {
+ retry = ftmac100_rx_packet(priv, &rx);
+ } while (retry && rx < budget);
+
+ if (retry && rx == budget)
+ completed = 0;
+ }
+
+ if (status & FTMAC100_INT_NORXBUF) {
+ /* RX buffer unavailable */
+ if (net_ratelimit())
+ netdev_info(netdev, "INT_NORXBUF\n");
+
+ netdev->stats.rx_over_errors++;
+ }
+
+ if (status & (FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST)) {
+ /*
+ * FTMAC100_INT_XPKT_OK:
+ * packet transmitted to ethernet successfully
+ *
+ * FTMAC100_INT_XPKT_LOST:
+ * packet transmitted to ethernet lost due to late
+ * collision or excessive collision
+ */
+ ftmac100_tx_complete(priv);
+ }
+
+ if (status & FTMAC100_INT_RPKT_LOST) {
+ /* received packet lost due to RX FIFO full */
+ if (net_ratelimit())
+ netdev_info(netdev, "INT_RPKT_LOST\n");
+
+ netdev->stats.rx_fifo_errors++;
+ }
+
+ if (status & FTMAC100_INT_AHB_ERR) {
+ /* AHB error */
+ if (net_ratelimit())
+ netdev_info(netdev, "INT_AHB_ERR\n");
+
+ /* do nothing */
+ }
+
+ if (status & FTMAC100_INT_PHYSTS_CHG) {
+ /* PHY link status change */
+ if (net_ratelimit())
+ netdev_info(netdev, "INT_PHYSTS_CHG\n");
+
+ mii_check_link(&priv->mii);
+ }
+
+ if (completed) {
+ /* stop polling */
+ napi_complete(napi);
+ ftmac100_enable_all_int(priv);
+ }
+
+ return rx;
+}
+
+/******************************************************************************
+ * struct net_device_ops functions
+ *****************************************************************************/
+static int ftmac100_open(struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ int err;
+
+ err = ftmac100_alloc_buffers(priv);
+ if (err) {
+ netdev_err(netdev, "failed to allocate buffers\n");
+ goto err_alloc;
+ }
+
+ err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name,
+ netdev);
+ if (err) {
+ netdev_err(netdev, "failed to request irq %d\n", priv->irq);
+ goto err_irq;
+ }
+
+ priv->rx_pointer = 0;
+ priv->tx_clean_pointer = 0;
+ priv->tx_pointer = 0;
+ priv->tx_pending = 0;
+
+ err = ftmac100_start_hw(priv);
+ if (err)
+ goto err_hw;
+
+ napi_enable(&priv->napi);
+ netif_start_queue(netdev);
+
+ ftmac100_enable_all_int(priv);
+ return 0;
+
+err_hw:
+ free_irq(priv->irq, netdev);
+err_irq:
+ ftmac100_free_buffers(priv);
+err_alloc:
+ return err;
+}
+
+static int ftmac100_stop(struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+
+ ftmac100_disable_all_int(priv);
+ netif_stop_queue(netdev);
+ napi_disable(&priv->napi);
+ ftmac100_stop_hw(priv);
+ free_irq(priv->irq, netdev);
+ ftmac100_free_buffers(priv);
+
+ return 0;
+}
+
+static int ftmac100_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ dma_addr_t map;
+
+ if (unlikely(skb->len > MAX_PKT_SIZE)) {
+ if (net_ratelimit())
+ netdev_dbg(netdev, "tx packet too big\n");
+
+ netdev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ map = dma_map_single(priv->dev, skb->data, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, map))) {
+ /* drop packet */
+ if (net_ratelimit())
+ netdev_err(netdev, "map socket buffer failed\n");
+
+ netdev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ return ftmac100_xmit(priv, skb, map);
+}
+
+/* optional */
+static int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr,
+ int cmd)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ struct mii_ioctl_data *data = if_mii(ifr);
+
+ return generic_mii_ioctl(&priv->mii, data, cmd, NULL);
+}
+
+static const struct net_device_ops ftmac100_netdev_ops = {
+ .ndo_open = ftmac100_open,
+ .ndo_stop = ftmac100_stop,
+ .ndo_start_xmit = ftmac100_hard_start_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = ftmac100_do_ioctl,
+};
+
+/******************************************************************************
+ * struct platform_driver functions
+ *****************************************************************************/
+static int ftmac100_remove(struct platform_device *pdev)
+{
+ struct net_device *netdev;
+ struct ftmac100 *priv;
+
+ netdev = platform_get_drvdata(pdev);
+ if (netdev == NULL)
+ return 0;
+
+ platform_set_drvdata(pdev, NULL);
+
+ priv = netdev_priv(netdev);
+
+ netif_napi_del(&priv->napi);
+ unregister_netdev(netdev);
+
+ if (priv->base != NULL)
+ iounmap(priv->base);
+
+ if (priv->res != NULL)
+ release_resource(priv->res);
+
+ free_netdev(netdev);
+ return 0;
+}
+
+static int ftmac100_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int irq;
+ struct net_device *netdev;
+ struct ftmac100 *priv;
+ int err;
+
+ if (!pdev)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /* setup net_device */
+
+ netdev = alloc_etherdev(sizeof(struct ftmac100));
+ if (netdev == NULL) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ SET_ETHTOOL_OPS(netdev, &ftmac100_ethtool_ops);
+ netdev->netdev_ops = &ftmac100_netdev_ops;
+
+ platform_set_drvdata(pdev, netdev);
+
+ /* setup private data */
+
+ priv = netdev_priv(netdev);
+ priv->netdev = netdev;
+ priv->dev = &pdev->dev;
+
+ spin_lock_init(&priv->tx_lock);
+
+ /* initialize NAPI */
+ netif_napi_add(netdev, &priv->napi, ftmac100_poll, 64);
+
+ /* map io memory */
+ priv->res = request_mem_region(res->start, res->end - res->start,
+ dev_name(&pdev->dev));
+ if (priv->res == NULL) {
+ dev_err(&pdev->dev, "Could not reserve memory region\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ priv->base = ioremap(res->start, res->end - res->start);
+ if (priv->base == NULL) {
+ dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
+ err = -EIO;
+ goto err_out;
+ }
+
+ priv->irq = irq;
+
+ /* initialize struct mii_if_info */
+
+ priv->mii.phy_id = 0;
+ priv->mii.phy_id_mask = 0x1f;
+ priv->mii.reg_num_mask = 0x1f;
+ priv->mii.dev = netdev;
+ priv->mii.mdio_read = ftmac100_mdio_read;
+ priv->mii.mdio_write = ftmac100_mdio_write;
+
+ /* register network device */
+
+ err = register_netdev(netdev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register netdev\n");
+ goto err_out;
+ }
+
+ 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_out:
+ ftmac100_remove(pdev);
+ return err;
+}
+
+static struct platform_driver ftmac100_driver = {
+ .probe = ftmac100_probe,
+ .remove = ftmac100_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/******************************************************************************
+ * initialization / finalization
+ *****************************************************************************/
+static int __init ftmac100_init(void)
+{
+ printk(KERN_INFO "Loading " DRV_NAME ": version " DRV_VERSION " ...\n");
+ return platform_driver_register(&ftmac100_driver);
+}
+
+static void __exit ftmac100_exit(void)
+{
+ platform_driver_unregister(&ftmac100_driver);
+}
+
+module_init(ftmac100_init);
+module_exit(ftmac100_exit);
+
+MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
+MODULE_DESCRIPTION("FTMAC100 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ftmac100.h b/drivers/net/ftmac100.h
new file mode 100644
index 0000000..46a0c47
--- /dev/null
+++ b/drivers/net/ftmac100.h
@@ -0,0 +1,180 @@
+/*
+ * Faraday FTMAC100 10/100 Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __FTMAC100_H
+#define __FTMAC100_H
+
+#define FTMAC100_OFFSET_ISR 0x00
+#define FTMAC100_OFFSET_IMR 0x04
+#define FTMAC100_OFFSET_MAC_MADR 0x08
+#define FTMAC100_OFFSET_MAC_LADR 0x0c
+#define FTMAC100_OFFSET_MAHT0 0x10
+#define FTMAC100_OFFSET_MAHT1 0x14
+#define FTMAC100_OFFSET_TXPD 0x18
+#define FTMAC100_OFFSET_RXPD 0x1c
+#define FTMAC100_OFFSET_TXR_BADR 0x20
+#define FTMAC100_OFFSET_RXR_BADR 0x24
+#define FTMAC100_OFFSET_ITC 0x28
+#define FTMAC100_OFFSET_APTC 0x2c
+#define FTMAC100_OFFSET_DBLAC 0x30
+#define FTMAC100_OFFSET_MACCR 0x88
+#define FTMAC100_OFFSET_MACSR 0x8c
+#define FTMAC100_OFFSET_PHYCR 0x90
+#define FTMAC100_OFFSET_PHYWDATA 0x94
+#define FTMAC100_OFFSET_FCR 0x98
+#define FTMAC100_OFFSET_BPR 0x9c
+#define FTMAC100_OFFSET_TS 0xc4
+#define FTMAC100_OFFSET_DMAFIFOS 0xc8
+#define FTMAC100_OFFSET_TM 0xcc
+#define FTMAC100_OFFSET_TX_MCOL_SCOL 0xd4
+#define FTMAC100_OFFSET_RPF_AEP 0xd8
+#define FTMAC100_OFFSET_XM_PG 0xdc
+#define FTMAC100_OFFSET_RUNT_TLCC 0xe0
+#define FTMAC100_OFFSET_CRCER_FTL 0xe4
+#define FTMAC100_OFFSET_RLC_RCC 0xe8
+#define FTMAC100_OFFSET_BROC 0xec
+#define FTMAC100_OFFSET_MULCA 0xf0
+#define FTMAC100_OFFSET_RP 0xf4
+#define FTMAC100_OFFSET_XP 0xf8
+
+/*
+ * Interrupt status register & interrupt mask register
+ */
+#define FTMAC100_INT_RPKT_FINISH (1 << 0)
+#define FTMAC100_INT_NORXBUF (1 << 1)
+#define FTMAC100_INT_XPKT_FINISH (1 << 2)
+#define FTMAC100_INT_NOTXBUF (1 << 3)
+#define FTMAC100_INT_XPKT_OK (1 << 4)
+#define FTMAC100_INT_XPKT_LOST (1 << 5)
+#define FTMAC100_INT_RPKT_SAV (1 << 6)
+#define FTMAC100_INT_RPKT_LOST (1 << 7)
+#define FTMAC100_INT_AHB_ERR (1 << 8)
+#define FTMAC100_INT_PHYSTS_CHG (1 << 9)
+
+/*
+ * Interrupt timer control register
+ */
+#define FTMAC100_ITC_RXINT_CNT(x) (((x) & 0xf) << 0)
+#define FTMAC100_ITC_RXINT_THR(x) (((x) & 0x7) << 4)
+#define FTMAC100_ITC_RXINT_TIME_SEL (1 << 7)
+#define FTMAC100_ITC_TXINT_CNT(x) (((x) & 0xf) << 8)
+#define FTMAC100_ITC_TXINT_THR(x) (((x) & 0x7) << 12)
+#define FTMAC100_ITC_TXINT_TIME_SEL (1 << 15)
+
+/*
+ * Automatic polling timer control register
+ */
+#define FTMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0)
+#define FTMAC100_APTC_RXPOLL_TIME_SEL (1 << 4)
+#define FTMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8)
+#define FTMAC100_APTC_TXPOLL_TIME_SEL (1 << 12)
+
+/*
+ * DMA burst length and arbitration control register
+ */
+#define FTMAC100_DBLAC_INCR4_EN (1 << 0)
+#define FTMAC100_DBLAC_INCR8_EN (1 << 1)
+#define FTMAC100_DBLAC_INCR16_EN (1 << 2)
+#define FTMAC100_DBLAC_RXFIFO_LTHR(x) (((x) & 0x7) << 3)
+#define FTMAC100_DBLAC_RXFIFO_HTHR(x) (((x) & 0x7) << 6)
+#define FTMAC100_DBLAC_RX_THR_EN (1 << 9)
+
+/*
+ * MAC control register
+ */
+#define FTMAC100_MACCR_XDMA_EN (1 << 0)
+#define FTMAC100_MACCR_RDMA_EN (1 << 1)
+#define FTMAC100_MACCR_SW_RST (1 << 2)
+#define FTMAC100_MACCR_LOOP_EN (1 << 3)
+#define FTMAC100_MACCR_CRC_DIS (1 << 4)
+#define FTMAC100_MACCR_XMT_EN (1 << 5)
+#define FTMAC100_MACCR_ENRX_IN_HALFTX (1 << 6)
+#define FTMAC100_MACCR_RCV_EN (1 << 8)
+#define FTMAC100_MACCR_HT_MULTI_EN (1 << 9)
+#define FTMAC100_MACCR_RX_RUNT (1 << 10)
+#define FTMAC100_MACCR_RX_FTL (1 << 11)
+#define FTMAC100_MACCR_RCV_ALL (1 << 12)
+#define FTMAC100_MACCR_CRC_APD (1 << 14)
+#define FTMAC100_MACCR_FULLDUP (1 << 15)
+#define FTMAC100_MACCR_RX_MULTIPKT (1 << 16)
+#define FTMAC100_MACCR_RX_BROADPKT (1 << 17)
+
+/*
+ * PHY control register
+ */
+#define FTMAC100_PHYCR_MIIRDATA 0xffff
+#define FTMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16)
+#define FTMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21)
+#define FTMAC100_PHYCR_MIIRD (1 << 26)
+#define FTMAC100_PHYCR_MIIWR (1 << 27)
+
+/*
+ * PHY write data register
+ */
+#define FTMAC100_PHYWDATA_MIIWDATA(x) ((x) & 0xffff)
+
+/*
+ * Transmit descriptor, aligned to 16 bytes
+ */
+struct ftmac100_txdes {
+ unsigned int txdes0;
+ unsigned int txdes1;
+ unsigned int txdes2; /* TXBUF_BADR */
+ unsigned int txdes3; /* not used by HW */
+} __attribute__ ((aligned(16)));
+
+#define FTMAC100_TXDES0_TXPKT_LATECOL (1 << 0)
+#define FTMAC100_TXDES0_TXPKT_EXSCOL (1 << 1)
+#define FTMAC100_TXDES0_TXDMA_OWN (1 << 31)
+
+#define FTMAC100_TXDES1_TXBUF_SIZE(x) ((x) & 0x7ff)
+#define FTMAC100_TXDES1_LTS (1 << 27)
+#define FTMAC100_TXDES1_FTS (1 << 28)
+#define FTMAC100_TXDES1_TX2FIC (1 << 29)
+#define FTMAC100_TXDES1_TXIC (1 << 30)
+#define FTMAC100_TXDES1_EDOTR (1 << 31)
+
+/*
+ * Receive descriptor, aligned to 16 bytes
+ */
+struct ftmac100_rxdes {
+ unsigned int rxdes0;
+ unsigned int rxdes1;
+ unsigned int rxdes2; /* RXBUF_BADR */
+ unsigned int rxdes3; /* not used by HW */
+} __attribute__ ((aligned(16)));
+
+#define FTMAC100_RXDES0_RFL 0x7ff
+#define FTMAC100_RXDES0_MULTICAST (1 << 16)
+#define FTMAC100_RXDES0_BROADCAST (1 << 17)
+#define FTMAC100_RXDES0_RX_ERR (1 << 18)
+#define FTMAC100_RXDES0_CRC_ERR (1 << 19)
+#define FTMAC100_RXDES0_FTL (1 << 20)
+#define FTMAC100_RXDES0_RUNT (1 << 21)
+#define FTMAC100_RXDES0_RX_ODD_NB (1 << 22)
+#define FTMAC100_RXDES0_LRS (1 << 28)
+#define FTMAC100_RXDES0_FRS (1 << 29)
+#define FTMAC100_RXDES0_RXDMA_OWN (1 << 31)
+
+#define FTMAC100_RXDES1_RXBUF_SIZE(x) ((x) & 0x7ff)
+#define FTMAC100_RXDES1_EDORR (1 << 31)
+
+#endif /* __FTMAC100_H */
--
1.6.3.3
^ permalink raw reply related
* Re: [PATCH] net_sched: accurate bytes/packets stats/rates
From: Hagen Paul Pfeifer @ 2011-01-17 9:16 UTC (permalink / raw)
To: Eric Dumazet
Cc: Changli Gao, Jarek Poplawski, Stephen Hemminger, David Miller,
netdev, Patrick McHardy, jamal
In-Reply-To: <1295248669.12859.23.camel@edumazet-laptop>
* Eric Dumazet | 2011-01-17 08:17:49 [+0100]:
>Hmm, considering qdisc stats are not used in kernel (only updated and
>reported to tc users) it seems to me counting arrival instead of
>departure rates is mostly useless for the user, if drops are ignored.
>
>(I am not speaking of direct drops, when we try to enqueue() this skb,
>but later ones, when another skb is enqueued and we drop a previously
>enqueued skb)
>
>User really wants to see the effective departure rate, to check its
>qdisc parameters in respect with kernel ones (HZ=100/1000, HIGH res
>timers off/on, ...)
>
>Arrival rates are of litle use. However, it might be good to have a
>second "bstats" only for dropped packets/bytes, or extend bstats in a
>compatible way (maybe adding fields to the end of structure)
Sure, qdiscs like CHOKe, SFQ, pfifo_head are only analyzable with this kind of
additional information. E.g. pfifo_head currently provides no statistic that
the queue length is possible underestimated and tunning is required.
Hagen
^ permalink raw reply
* Re: [PATCH v4 05/10] net/fec: add dual fec support for mx28
From: Uwe Kleine-König @ 2011-01-17 9:06 UTC (permalink / raw)
To: Shawn Guo
Cc: gerg, Greg Ungerer, B32542, netdev, s.hauer, jamie, baruch,
w.sang, r64343, eric, bryan.wu, jamie, davem, linux-arm-kernel,
lw
In-Reply-To: <20110113144805.GS24920@pengutronix.de>
Hello,
On Thu, Jan 13, 2011 at 03:48:05PM +0100, Uwe Kleine-König wrote:
> > #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
> > - defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC)
> > + defined(CONFIG_M520x) || defined(CONFIG_M532x) || \
> > + defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28)
> I wonder what is excluded here. FEC depends on
>
> M523x || M527x || M5272 || M528x || M520x || M532x || \
> MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5 || SOC_IMX28
>
> so the only difference is that the latter lists M5272 which seems a bit
> redundant in the presence of M527x.
M527x = {M5271, M5275}, so it seems to me that only M5272 is excluded
here. I don't know if it's possible to have a kernel supporting M5272
and (e.g.) M527x. If yes, does the driver work correct on M5272 then?
Greg, it seems to me that M5272 is the exception here, not all the
others. Would it make sense to make the above read:
#if !defined(CONFIG_M5272)
?
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* Re: [PATCH 1/3] vhost-net: check the support of mergeable buffer outside the receive loop
From: Michael S. Tsirkin @ 2011-01-17 8:46 UTC (permalink / raw)
To: Jason Wang; +Cc: virtualization, netdev, kvm, linux-kernel
In-Reply-To: <20110117081058.18900.67456.stgit@dhcp-91-7.nay.redhat.com.englab.nay.redhat.com>
On Mon, Jan 17, 2011 at 04:10:59PM +0800, Jason Wang wrote:
> No need to check the support of mergeable buffer inside the recevie
> loop as the whole handle_rx()_xx is in the read critical region. So
> this patch move it ahead of the receiving loop.
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
Well feature check is mostly just features & bit
so why would it be slower? Because of the rcu
adding memory barriers? Do you observe a
measureable speedup with this patch?
Apropos, I noticed that the check in vhost_has_feature
is wrong: it uses the same kind of RCU as the
private pointer. So we'll have to fix that properly
by adding more lockdep classes, but for now
we'll need to make
the check 1 || lockdep_is_held(&dev->mutex);
and add a TODO.
> ---
> drivers/vhost/net.c | 5 +++--
> 1 files changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index 14fc189..95e49de 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -411,7 +411,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
> };
>
> size_t total_len = 0;
> - int err, headcount;
> + int err, headcount, mergeable;
> size_t vhost_hlen, sock_hlen;
> size_t vhost_len, sock_len;
> struct socket *sock = rcu_dereference(vq->private_data);
> @@ -425,6 +425,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
>
> vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
> vq->log : NULL;
> + mergeable = vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF);
>
> while ((sock_len = peek_head_len(sock->sk))) {
> sock_len += sock_hlen;
> @@ -474,7 +475,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
> break;
> }
> /* TODO: Should check and handle checksum. */
> - if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF) &&
> + if (likely(mergeable) &&
> memcpy_toiovecend(vq->hdr, (unsigned char *)&headcount,
> offsetof(typeof(hdr), num_buffers),
> sizeof hdr.num_buffers)) {
^ permalink raw reply
* Re: [PATCH v4 05/10] net/fec: add dual fec support for mx28
From: Uwe Kleine-König @ 2011-01-17 8:42 UTC (permalink / raw)
To: Lothar Waßmann
Cc: Shawn Guo, gerg, B32542, netdev, s.hauer, jamie, baruch, w.sang,
r64343, eric, bryan.wu, jamie, davem, linux-arm-kernel
In-Reply-To: <19763.64214.220441.325208@ipc1.ka-ro>
Hi,
On Mon, Jan 17, 2011 at 09:16:22AM +0100, Lothar Waßmann wrote:
> Shawn Guo writes:
> > On Fri, Jan 14, 2011 at 08:52:23AM +0100, Uwe Kleine-König wrote:
> > > On Fri, Jan 14, 2011 at 01:48:40PM +0800, Shawn Guo wrote:
> > > > Hi Uwe,
> > > >
> > > > On Thu, Jan 13, 2011 at 03:48:05PM +0100, Uwe Kleine-König wrote:
> > > >
> > > > [...]
> > > >
> > > > > > +/* Controller is ENET-MAC */
> > > > > > +#define FEC_QUIRK_ENET_MAC (1 << 0)
> > > > > does this really qualify to be a quirk?
> > > > >
> > > > My understanding is that ENET-MAC is a type of "quirky" FEC
> > > > controller.
> > > >
> > > > > > +/* Controller needs driver to swap frame */
> > > > > > +#define FEC_QUIRK_SWAP_FRAME (1 << 1)
> > > > > IMHO this is a bit misnamed. FEC_QUIRK_NEEDS_BE_DATA or similar would
> > > > > be more accurate.
> > > > >
> > > > When your make this change, you may want to pick a better name for
> > > > function swap_buffer too.
> > > >
> > > > [...]
> > > >
> > > > > > +static void *swap_buffer(void *bufaddr, int len)
> > > > > > +{
> > > > > > + int i;
> > > > > > + unsigned int *buf = bufaddr;
> > > > > > +
> > > > > > + for (i = 0; i < (len + 3) / 4; i++, buf++)
> > > > > > + *buf = cpu_to_be32(*buf);
> > > > > if len isn't a multiple of 4 this accesses bytes behind len. Is this
> > > > > generally OK here? (E.g. because skbs always have a length that is a
> > > > > multiple of 4?)
> > > > The len may not be a multiple of 4. But I believe bufaddr is always
> > > > a buffer allocated in a length that is a multiple of 4, and the 1~3
> > > > bytes exceeding the len very likely has no data that matters. But
> > > > yes, it deserves a safer implementation.
> > > Did you test what happens if bufaddr isn't aligned? Does it work at all
> > > then?
> > >
> > I see many calls passing a len that is not a multiple of 4, but it
> > works good.
> >
> That does not prove anything, actually.
>
> Anyway "bufaddr isn't aligned" != "len is not a multiple of 4".
> Is there any guarantee that the function cannot be called with a
> non-aligned buffer address?
Over the weekend I wondered if we could reach alignment via a dma-mask
setting. Didn't check yet how this is configured.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* Re: [PATCH 2/3] vhost-net: Unify the code of mergeable and big buffer handling
From: Michael S. Tsirkin @ 2011-01-17 8:36 UTC (permalink / raw)
To: Jason Wang; +Cc: virtualization, netdev, kvm, linux-kernel
In-Reply-To: <20110117081108.18900.60053.stgit@dhcp-91-7.nay.redhat.com.englab.nay.redhat.com>
On Mon, Jan 17, 2011 at 04:11:08PM +0800, Jason Wang wrote:
> Codes duplication were found between the handling of mergeable and big
> buffers, so this patch tries to unify them. This could be easily done
> by adding a quota to the get_rx_bufs() which is used to limit the
> number of buffers it returns (for mergeable buffer, the quota is
> simply UIO_MAXIOV, for big buffers, the quota is just 1), and then the
> previous handle_rx_mergeable() could be resued also for big buffers.
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
We actually started this way. However the code turned out
to have measureable overhead when handle_rx_mergeable
is called with quota 1.
So before applying this I'd like to see some data
to show this is not the case anymore.
> ---
> drivers/vhost/net.c | 128 +++------------------------------------------------
> 1 files changed, 7 insertions(+), 121 deletions(-)
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index 95e49de..c32a2e4 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -227,6 +227,7 @@ static int peek_head_len(struct sock *sk)
> * @iovcount - returned count of io vectors we fill
> * @log - vhost log
> * @log_num - log offset
> + * @quota - headcount quota, 1 for big buffer
> * returns number of buffer heads allocated, negative on error
> */
> static int get_rx_bufs(struct vhost_virtqueue *vq,
> @@ -234,7 +235,8 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
> int datalen,
> unsigned *iovcount,
> struct vhost_log *log,
> - unsigned *log_num)
> + unsigned *log_num,
> + unsigned int quota)
> {
> unsigned int out, in;
> int seg = 0;
> @@ -242,7 +244,7 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
> unsigned d;
> int r, nlogs = 0;
>
> - while (datalen > 0) {
> + while (datalen > 0 && headcount < quota) {
> if (unlikely(seg >= UIO_MAXIOV)) {
> r = -ENOBUFS;
> goto err;
> @@ -282,116 +284,7 @@ err:
>
> /* Expects to be always run from workqueue - which acts as
> * read-size critical section for our kind of RCU. */
> -static void handle_rx_big(struct vhost_net *net)
> -{
> - struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
> - unsigned out, in, log, s;
> - int head;
> - struct vhost_log *vq_log;
> - struct msghdr msg = {
> - .msg_name = NULL,
> - .msg_namelen = 0,
> - .msg_control = NULL, /* FIXME: get and handle RX aux data. */
> - .msg_controllen = 0,
> - .msg_iov = vq->iov,
> - .msg_flags = MSG_DONTWAIT,
> - };
> -
> - struct virtio_net_hdr hdr = {
> - .flags = 0,
> - .gso_type = VIRTIO_NET_HDR_GSO_NONE
> - };
> -
> - size_t len, total_len = 0;
> - int err;
> - size_t hdr_size;
> - struct socket *sock = rcu_dereference(vq->private_data);
> - if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
> - return;
> -
> - mutex_lock(&vq->mutex);
> - vhost_disable_notify(vq);
> - hdr_size = vq->vhost_hlen;
> -
> - vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
> - vq->log : NULL;
> -
> - for (;;) {
> - head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
> - ARRAY_SIZE(vq->iov),
> - &out, &in,
> - vq_log, &log);
> - /* On error, stop handling until the next kick. */
> - if (unlikely(head < 0))
> - break;
> - /* OK, now we need to know about added descriptors. */
> - if (head == vq->num) {
> - if (unlikely(vhost_enable_notify(vq))) {
> - /* They have slipped one in as we were
> - * doing that: check again. */
> - vhost_disable_notify(vq);
> - continue;
> - }
> - /* Nothing new? Wait for eventfd to tell us
> - * they refilled. */
> - break;
> - }
> - /* We don't need to be notified again. */
> - if (out) {
> - vq_err(vq, "Unexpected descriptor format for RX: "
> - "out %d, int %d\n",
> - out, in);
> - break;
> - }
> - /* Skip header. TODO: support TSO/mergeable rx buffers. */
> - s = move_iovec_hdr(vq->iov, vq->hdr, hdr_size, in);
> - msg.msg_iovlen = in;
> - len = iov_length(vq->iov, in);
> - /* Sanity check */
> - if (!len) {
> - vq_err(vq, "Unexpected header len for RX: "
> - "%zd expected %zd\n",
> - iov_length(vq->hdr, s), hdr_size);
> - break;
> - }
> - err = sock->ops->recvmsg(NULL, sock, &msg,
> - len, MSG_DONTWAIT | MSG_TRUNC);
> - /* TODO: Check specific error and bomb out unless EAGAIN? */
> - if (err < 0) {
> - vhost_discard_vq_desc(vq, 1);
> - break;
> - }
> - /* TODO: Should check and handle checksum. */
> - if (err > len) {
> - pr_debug("Discarded truncated rx packet: "
> - " len %d > %zd\n", err, len);
> - vhost_discard_vq_desc(vq, 1);
> - continue;
> - }
> - len = err;
> - err = memcpy_toiovec(vq->hdr, (unsigned char *)&hdr, hdr_size);
> - if (err) {
> - vq_err(vq, "Unable to write vnet_hdr at addr %p: %d\n",
> - vq->iov->iov_base, err);
> - break;
> - }
> - len += hdr_size;
> - vhost_add_used_and_signal(&net->dev, vq, head, len);
> - if (unlikely(vq_log))
> - vhost_log_write(vq, vq_log, log, len);
> - total_len += len;
> - if (unlikely(total_len >= VHOST_NET_WEIGHT)) {
> - vhost_poll_queue(&vq->poll);
> - break;
> - }
> - }
> -
> - mutex_unlock(&vq->mutex);
> -}
> -
> -/* Expects to be always run from workqueue - which acts as
> - * read-size critical section for our kind of RCU. */
> -static void handle_rx_mergeable(struct vhost_net *net)
> +static void handle_rx(struct vhost_net *net)
> {
> struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
> unsigned uninitialized_var(in), log;
> @@ -431,7 +324,8 @@ static void handle_rx_mergeable(struct vhost_net *net)
> sock_len += sock_hlen;
> vhost_len = sock_len + vhost_hlen;
> headcount = get_rx_bufs(vq, vq->heads, vhost_len,
> - &in, vq_log, &log);
> + &in, vq_log, &log,
> + likely(mergeable) ? UIO_MAXIOV : 1);
> /* On error, stop handling until the next kick. */
> if (unlikely(headcount < 0))
> break;
> @@ -497,14 +391,6 @@ static void handle_rx_mergeable(struct vhost_net *net)
> mutex_unlock(&vq->mutex);
> }
>
> -static void handle_rx(struct vhost_net *net)
> -{
> - if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF))
> - handle_rx_mergeable(net);
> - else
> - handle_rx_big(net);
> -}
> -
> static void handle_tx_kick(struct vhost_work *work)
> {
> struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
^ permalink raw reply
* Re: NETLINK: Failed to browse: Invalid argument from avahi-daemon in F14 since 2.6.37-git8
From: Jarek Poplawski @ 2011-01-17 8:32 UTC (permalink / raw)
To: Alessandro Suardi; +Cc: linux-kernel, netdev
In-Reply-To: <AANLkTinHOFwPCwJNCy9cFMCf+_VNb-7YFk-3VJFXoaoZ@mail.gmail.com>
On 2011-01-15 19:34, Alessandro Suardi wrote:
> /var/log/messages says:
>
> Jan 13 12:42:02 duff avahi-daemon[2771]: Found user 'avahi' (UID 70)
> and group 'avahi' (GID 70).
> Jan 13 12:42:02 duff avahi-daemon[2771]: Successfully dropped root privileges.
> Jan 13 12:42:02 duff avahi-daemon[2771]: avahi-daemon 0.6.27 starting up.
> Jan 13 12:42:02 duff avahi-daemon[2771]: Successfully called chroot().
> Jan 13 12:42:02 duff avahi-daemon[2771]: Successfully dropped
> remaining capabilities.
> Jan 13 12:42:02 duff avahi-daemon[2771]: Loading service file
> /services/ssh.service.
> Jan 13 12:42:02 duff avahi-daemon[2771]: Loading service file
> /services/udisks.service.
> Jan 13 12:42:02 duff avahi-daemon[2771]: NETLINK: Failed to browse:
> Invalid argument
> Jan 13 12:42:23 duff acpid: starting up with netlink and the input layer
>
>
> Happens both at boot and after boot if restarting avahi-daemon service,
> and there is a 10-15" wait before the service start script prints a [FAILED]
> red tag; avahi-daemon process does start up anyways.
>
> -git7 is the latest "good" kernel
> -git8, -git9, -git11, -git13 have been reproducing the issue
>
>
> thanks, ciao,
Seems to be resolved in this thread:
http://www.spinics.net/lists/netdev/msg152762.html
Thanks,
Jarek P.
^ permalink raw reply
* Re: [PATCH v4 05/10] net/fec: add dual fec support for mx28
From: Lothar Waßmann @ 2011-01-17 8:16 UTC (permalink / raw)
To: Shawn Guo
Cc: gerg, B32542, netdev, s.hauer, bryan.wu, jamie, baruch, w.sang,
r64343, eric, Uwe Kleine-König, jamie, davem,
linux-arm-kernel
In-Reply-To: <20110114130849.GA27583@freescale.com>
Hi,
Shawn Guo writes:
> On Fri, Jan 14, 2011 at 08:52:23AM +0100, Uwe Kleine-König wrote:
> > On Fri, Jan 14, 2011 at 01:48:40PM +0800, Shawn Guo wrote:
> > > Hi Uwe,
> > >
> > > On Thu, Jan 13, 2011 at 03:48:05PM +0100, Uwe Kleine-König wrote:
> > >
> > > [...]
> > >
> > > > > +/* Controller is ENET-MAC */
> > > > > +#define FEC_QUIRK_ENET_MAC (1 << 0)
> > > > does this really qualify to be a quirk?
> > > >
> > > My understanding is that ENET-MAC is a type of "quirky" FEC
> > > controller.
> > >
> > > > > +/* Controller needs driver to swap frame */
> > > > > +#define FEC_QUIRK_SWAP_FRAME (1 << 1)
> > > > IMHO this is a bit misnamed. FEC_QUIRK_NEEDS_BE_DATA or similar would
> > > > be more accurate.
> > > >
> > > When your make this change, you may want to pick a better name for
> > > function swap_buffer too.
> > >
> > > [...]
> > >
> > > > > +static void *swap_buffer(void *bufaddr, int len)
> > > > > +{
> > > > > + int i;
> > > > > + unsigned int *buf = bufaddr;
> > > > > +
> > > > > + for (i = 0; i < (len + 3) / 4; i++, buf++)
> > > > > + *buf = cpu_to_be32(*buf);
> > > > if len isn't a multiple of 4 this accesses bytes behind len. Is this
> > > > generally OK here? (E.g. because skbs always have a length that is a
> > > > multiple of 4?)
> > > The len may not be a multiple of 4. But I believe bufaddr is always
> > > a buffer allocated in a length that is a multiple of 4, and the 1~3
> > > bytes exceeding the len very likely has no data that matters. But
> > > yes, it deserves a safer implementation.
> > Did you test what happens if bufaddr isn't aligned? Does it work at all
> > then?
> >
> I see many calls passing a len that is not a multiple of 4, but it
> works good.
>
That does not prove anything, actually.
Anyway "bufaddr isn't aligned" != "len is not a multiple of 4".
Is there any guarantee that the function cannot be called with a
non-aligned buffer address?
Lothar Waßmann
--
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info@karo-electronics.de
___________________________________________________________
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] virtio-net: fix a typo
From: Aurelien Jarno @ 2011-01-17 8:14 UTC (permalink / raw)
To: linux-kernel; +Cc: Aurelien Jarno, netdev, Michael S. Tsirkin
fitler -> filter
Cc: netdev@vger.kernel.org
Cc: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
drivers/net/virtio_net.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 90a23e4..48a4297 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -787,7 +787,7 @@ static void virtnet_set_rx_mode(struct net_device *dev)
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MAC,
VIRTIO_NET_CTRL_MAC_TABLE_SET,
sg, 2, 0))
- dev_warn(&dev->dev, "Failed to set MAC fitler table.\n");
+ dev_warn(&dev->dev, "Failed to set MAC filter table.\n");
kfree(buf);
}
--
1.7.2.3
^ permalink raw reply related
* [PATCH 1/3] vhost-net: check the support of mergeable buffer outside the receive loop
From: Jason Wang @ 2011-01-17 8:10 UTC (permalink / raw)
To: virtualization, netdev, kvm, mst; +Cc: linux-kernel
No need to check the support of mergeable buffer inside the recevie
loop as the whole handle_rx()_xx is in the read critical region. So
this patch move it ahead of the receiving loop.
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
drivers/vhost/net.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 14fc189..95e49de 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -411,7 +411,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
};
size_t total_len = 0;
- int err, headcount;
+ int err, headcount, mergeable;
size_t vhost_hlen, sock_hlen;
size_t vhost_len, sock_len;
struct socket *sock = rcu_dereference(vq->private_data);
@@ -425,6 +425,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
vq->log : NULL;
+ mergeable = vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF);
while ((sock_len = peek_head_len(sock->sk))) {
sock_len += sock_hlen;
@@ -474,7 +475,7 @@ static void handle_rx_mergeable(struct vhost_net *net)
break;
}
/* TODO: Should check and handle checksum. */
- if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF) &&
+ if (likely(mergeable) &&
memcpy_toiovecend(vq->hdr, (unsigned char *)&headcount,
offsetof(typeof(hdr), num_buffers),
sizeof hdr.num_buffers)) {
^ permalink raw reply related
* [PATCH 3/3] vhost-net: use lock_sock_fast() in peek_head_len()
From: Jason Wang @ 2011-01-17 8:11 UTC (permalink / raw)
To: virtualization, netdev, kvm, mst; +Cc: linux-kernel
In-Reply-To: <20110117081058.18900.67456.stgit@dhcp-91-7.nay.redhat.com.englab.nay.redhat.com>
We can use lock_sock_fast() instead of lock_sock() in order to get
speedup in peek_head_len().
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
drivers/vhost/net.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index c32a2e4..50b622a 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -211,12 +211,12 @@ static int peek_head_len(struct sock *sk)
{
struct sk_buff *head;
int len = 0;
+ bool slow = lock_sock_fast(sk);
- lock_sock(sk);
head = skb_peek(&sk->sk_receive_queue);
if (head)
len = head->len;
- release_sock(sk);
+ unlock_sock_fast(sk, slow);
return len;
}
^ permalink raw reply related
* [PATCH 2/3] vhost-net: Unify the code of mergeable and big buffer handling
From: Jason Wang @ 2011-01-17 8:11 UTC (permalink / raw)
To: virtualization, netdev, kvm, mst; +Cc: linux-kernel
In-Reply-To: <20110117081058.18900.67456.stgit@dhcp-91-7.nay.redhat.com.englab.nay.redhat.com>
Codes duplication were found between the handling of mergeable and big
buffers, so this patch tries to unify them. This could be easily done
by adding a quota to the get_rx_bufs() which is used to limit the
number of buffers it returns (for mergeable buffer, the quota is
simply UIO_MAXIOV, for big buffers, the quota is just 1), and then the
previous handle_rx_mergeable() could be resued also for big buffers.
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
drivers/vhost/net.c | 128 +++------------------------------------------------
1 files changed, 7 insertions(+), 121 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 95e49de..c32a2e4 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -227,6 +227,7 @@ static int peek_head_len(struct sock *sk)
* @iovcount - returned count of io vectors we fill
* @log - vhost log
* @log_num - log offset
+ * @quota - headcount quota, 1 for big buffer
* returns number of buffer heads allocated, negative on error
*/
static int get_rx_bufs(struct vhost_virtqueue *vq,
@@ -234,7 +235,8 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
int datalen,
unsigned *iovcount,
struct vhost_log *log,
- unsigned *log_num)
+ unsigned *log_num,
+ unsigned int quota)
{
unsigned int out, in;
int seg = 0;
@@ -242,7 +244,7 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
unsigned d;
int r, nlogs = 0;
- while (datalen > 0) {
+ while (datalen > 0 && headcount < quota) {
if (unlikely(seg >= UIO_MAXIOV)) {
r = -ENOBUFS;
goto err;
@@ -282,116 +284,7 @@ err:
/* Expects to be always run from workqueue - which acts as
* read-size critical section for our kind of RCU. */
-static void handle_rx_big(struct vhost_net *net)
-{
- struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
- unsigned out, in, log, s;
- int head;
- struct vhost_log *vq_log;
- struct msghdr msg = {
- .msg_name = NULL,
- .msg_namelen = 0,
- .msg_control = NULL, /* FIXME: get and handle RX aux data. */
- .msg_controllen = 0,
- .msg_iov = vq->iov,
- .msg_flags = MSG_DONTWAIT,
- };
-
- struct virtio_net_hdr hdr = {
- .flags = 0,
- .gso_type = VIRTIO_NET_HDR_GSO_NONE
- };
-
- size_t len, total_len = 0;
- int err;
- size_t hdr_size;
- struct socket *sock = rcu_dereference(vq->private_data);
- if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
- return;
-
- mutex_lock(&vq->mutex);
- vhost_disable_notify(vq);
- hdr_size = vq->vhost_hlen;
-
- vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
- vq->log : NULL;
-
- for (;;) {
- head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
- ARRAY_SIZE(vq->iov),
- &out, &in,
- vq_log, &log);
- /* On error, stop handling until the next kick. */
- if (unlikely(head < 0))
- break;
- /* OK, now we need to know about added descriptors. */
- if (head == vq->num) {
- if (unlikely(vhost_enable_notify(vq))) {
- /* They have slipped one in as we were
- * doing that: check again. */
- vhost_disable_notify(vq);
- continue;
- }
- /* Nothing new? Wait for eventfd to tell us
- * they refilled. */
- break;
- }
- /* We don't need to be notified again. */
- if (out) {
- vq_err(vq, "Unexpected descriptor format for RX: "
- "out %d, int %d\n",
- out, in);
- break;
- }
- /* Skip header. TODO: support TSO/mergeable rx buffers. */
- s = move_iovec_hdr(vq->iov, vq->hdr, hdr_size, in);
- msg.msg_iovlen = in;
- len = iov_length(vq->iov, in);
- /* Sanity check */
- if (!len) {
- vq_err(vq, "Unexpected header len for RX: "
- "%zd expected %zd\n",
- iov_length(vq->hdr, s), hdr_size);
- break;
- }
- err = sock->ops->recvmsg(NULL, sock, &msg,
- len, MSG_DONTWAIT | MSG_TRUNC);
- /* TODO: Check specific error and bomb out unless EAGAIN? */
- if (err < 0) {
- vhost_discard_vq_desc(vq, 1);
- break;
- }
- /* TODO: Should check and handle checksum. */
- if (err > len) {
- pr_debug("Discarded truncated rx packet: "
- " len %d > %zd\n", err, len);
- vhost_discard_vq_desc(vq, 1);
- continue;
- }
- len = err;
- err = memcpy_toiovec(vq->hdr, (unsigned char *)&hdr, hdr_size);
- if (err) {
- vq_err(vq, "Unable to write vnet_hdr at addr %p: %d\n",
- vq->iov->iov_base, err);
- break;
- }
- len += hdr_size;
- vhost_add_used_and_signal(&net->dev, vq, head, len);
- if (unlikely(vq_log))
- vhost_log_write(vq, vq_log, log, len);
- total_len += len;
- if (unlikely(total_len >= VHOST_NET_WEIGHT)) {
- vhost_poll_queue(&vq->poll);
- break;
- }
- }
-
- mutex_unlock(&vq->mutex);
-}
-
-/* Expects to be always run from workqueue - which acts as
- * read-size critical section for our kind of RCU. */
-static void handle_rx_mergeable(struct vhost_net *net)
+static void handle_rx(struct vhost_net *net)
{
struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX];
unsigned uninitialized_var(in), log;
@@ -431,7 +324,8 @@ static void handle_rx_mergeable(struct vhost_net *net)
sock_len += sock_hlen;
vhost_len = sock_len + vhost_hlen;
headcount = get_rx_bufs(vq, vq->heads, vhost_len,
- &in, vq_log, &log);
+ &in, vq_log, &log,
+ likely(mergeable) ? UIO_MAXIOV : 1);
/* On error, stop handling until the next kick. */
if (unlikely(headcount < 0))
break;
@@ -497,14 +391,6 @@ static void handle_rx_mergeable(struct vhost_net *net)
mutex_unlock(&vq->mutex);
}
-static void handle_rx(struct vhost_net *net)
-{
- if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF))
- handle_rx_mergeable(net);
- else
- handle_rx_big(net);
-}
-
static void handle_tx_kick(struct vhost_work *work)
{
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox