* [PATCH v4 08/10] ARM: mxs: add ocotp read function
From: Shawn Guo @ 2011-01-06 7:13 UTC (permalink / raw)
To: davem, gerg, baruch, eric, bryan.wu, r64343, B32542,
u.kleine-koenig
In-Reply-To: <1294297998-26930-1-git-send-email-shawn.guo@freescale.com>
Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
---
Changes for v4:
- Call cpu_relax() during polling
Changes for v2:
- Add mutex locking for mxs_read_ocotp()
- Use type size_t for count and i
- Add comment for clk_enable/disable skipping
- Add ERROR bit clearing and polling step
arch/arm/mach-mxs/Makefile | 2 +-
arch/arm/mach-mxs/include/mach/common.h | 1 +
arch/arm/mach-mxs/ocotp.c | 79 +++++++++++++++++++++++++++++++
3 files changed, 81 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-mxs/ocotp.c
diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile
index 39d3f9c..f23ebbd 100644
--- a/arch/arm/mach-mxs/Makefile
+++ b/arch/arm/mach-mxs/Makefile
@@ -1,5 +1,5 @@
# Common support
-obj-y := clock.o devices.o gpio.o icoll.o iomux.o system.o timer.o
+obj-y := clock.o devices.o gpio.o icoll.o iomux.o ocotp.o system.o timer.o
obj-$(CONFIG_SOC_IMX23) += clock-mx23.o mm-mx23.o
obj-$(CONFIG_SOC_IMX28) += clock-mx28.o mm-mx28.o
diff --git a/arch/arm/mach-mxs/include/mach/common.h b/arch/arm/mach-mxs/include/mach/common.h
index 59133eb..cf02552 100644
--- a/arch/arm/mach-mxs/include/mach/common.h
+++ b/arch/arm/mach-mxs/include/mach/common.h
@@ -13,6 +13,7 @@
struct clk;
+extern int mxs_read_ocotp(int offset, int count, u32 *values);
extern int mxs_reset_block(void __iomem *);
extern void mxs_timer_init(struct clk *, int);
diff --git a/arch/arm/mach-mxs/ocotp.c b/arch/arm/mach-mxs/ocotp.c
new file mode 100644
index 0000000..e2d39aa
--- /dev/null
+++ b/arch/arm/mach-mxs/ocotp.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+#include <mach/mxs.h>
+
+#define BM_OCOTP_CTRL_BUSY (1 << 8)
+#define BM_OCOTP_CTRL_ERROR (1 << 9)
+#define BM_OCOTP_CTRL_RD_BANK_OPEN (1 << 12)
+
+static DEFINE_MUTEX(ocotp_mutex);
+
+int mxs_read_ocotp(unsigned offset, size_t count, u32 *values)
+{
+ void __iomem *ocotp_base = MXS_IO_ADDRESS(MXS_OCOTP_BASE_ADDR);
+ int timeout = 0x400;
+ size_t i;
+
+ mutex_lock(&ocotp_mutex);
+
+ /*
+ * clk_enable(hbus_clk) for ocotp can be skipped
+ * as it must be on when system is running.
+ */
+
+ /* try to clear ERROR bit */
+ __mxs_clrl(BM_OCOTP_CTRL_ERROR, ocotp_base);
+
+ /* check both BUSY and ERROR cleared */
+ while ((__raw_readl(ocotp_base) &
+ (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)) && --timeout)
+ cpu_relax();
+
+ if (unlikely(!timeout))
+ goto error_unlock;
+
+ /* open OCOTP banks for read */
+ __mxs_setl(BM_OCOTP_CTRL_RD_BANK_OPEN, ocotp_base);
+
+ /* approximately wait 32 hclk cycles */
+ udelay(1);
+
+ /* poll BUSY bit becoming cleared */
+ timeout = 0x400;
+ while ((__raw_readl(ocotp_base) & BM_OCOTP_CTRL_BUSY) && --timeout)
+ cpu_relax();
+
+ if (unlikely(!timeout))
+ goto error_unlock;
+
+ for (i = 0; i < count; i++, offset += 4)
+ *values++ = __raw_readl(ocotp_base + offset);
+
+ /* close banks for power saving */
+ __mxs_clrl(BM_OCOTP_CTRL_RD_BANK_OPEN, ocotp_base);
+
+ mutex_unlock(&ocotp_mutex);
+
+ return 0;
+
+error_unlock:
+ mutex_unlock(&ocotp_mutex);
+ pr_err("%s: timeout in reading OCOTP\n", __func__);
+ return -ETIMEDOUT;
+}
--
1.7.1
^ permalink raw reply related
* [PATCH v4 09/10] ARM: mx28: read fec mac address from ocotp
From: Shawn Guo @ 2011-01-06 7:13 UTC (permalink / raw)
To: davem, gerg, baruch, eric, bryan.wu, r64343, B32542,
u.kleine-koenig
In-Reply-To: <1294297998-26930-1-git-send-email-shawn.guo@freescale.com>
Read fec mac address from ocotp and save it into fec_platform_data
mac field for fec driver to use.
Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
---
Changes for v2:
- It's not necessary to remove "const" for fec_platform_data from
platform-fec.c and devices-common.h, so add it back.
- Hard-coding Freescale OUI (00:04:9f) instead of just the first
two two octets.
- Correct the return of mx28evk_fec_get_mac() and check it
with caller
arch/arm/mach-mxs/mach-mx28evk.c | 32 ++++++++++++++++++++++++++++++++
1 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c
index def6519..54fa512 100644
--- a/arch/arm/mach-mxs/mach-mx28evk.c
+++ b/arch/arm/mach-mxs/mach-mx28evk.c
@@ -129,12 +129,44 @@ static struct fec_platform_data mx28_fec_pdata[] = {
},
};
+static int __init mx28evk_fec_get_mac(void)
+{
+ int i, ret;
+ u32 val;
+
+ /*
+ * OCOTP only stores the last 4 octets for each mac address,
+ * so hard-code Freescale OUI (00:04:9f) here.
+ */
+ for (i = 0; i < 2; i++) {
+ ret = mxs_read_ocotp(0x20 + i * 0x10, 1, &val);
+ if (ret)
+ goto error;
+
+ mx28_fec_pdata[i].mac[0] = 0x00;
+ mx28_fec_pdata[i].mac[1] = 0x04;
+ mx28_fec_pdata[i].mac[2] = 0x9f;
+ mx28_fec_pdata[i].mac[3] = (val >> 16) & 0xff;
+ mx28_fec_pdata[i].mac[4] = (val >> 8) & 0xff;
+ mx28_fec_pdata[i].mac[5] = (val >> 0) & 0xff;
+ }
+
+ return 0;
+
+error:
+ pr_err("%s: timeout when reading fec mac from OCOTP\n", __func__);
+ return ret;
+}
+
static void __init mx28evk_init(void)
{
mxs_iomux_setup_multiple_pads(mx28evk_pads, ARRAY_SIZE(mx28evk_pads));
mx28_add_duart();
+ if (mx28evk_fec_get_mac())
+ pr_warn("%s: failed on fec mac setup\n", __func__);
+
mx28evk_fec_reset();
mx28_add_fec(0, &mx28_fec_pdata[0]);
#ifdef CONFIG_FEC2
--
1.7.1
^ permalink raw reply related
* [PATCH v4 10/10] ARM: mxs: add initial pm support
From: Shawn Guo @ 2011-01-06 7:13 UTC (permalink / raw)
To: davem, gerg, baruch, eric, bryan.wu, r64343, B32542,
u.kleine-koenig
In-Reply-To: <1294297998-26930-1-git-send-email-shawn.guo@freescale.com>
This is a very initial pm support and basically does nothing.
With this pm support entry, drivers can start testing their own
pm functions.
Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
---
Changes for v2:
- Let build of pm.c depend on CONFIG_PM
- Remove the blank line above device_initcall in pm.c
arch/arm/mach-mxs/Makefile | 2 ++
arch/arm/mach-mxs/pm.c | 43 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-mxs/pm.c
diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile
index f23ebbd..45a2925 100644
--- a/arch/arm/mach-mxs/Makefile
+++ b/arch/arm/mach-mxs/Makefile
@@ -1,6 +1,8 @@
# Common support
obj-y := clock.o devices.o gpio.o icoll.o iomux.o ocotp.o system.o timer.o
+obj-$(CONFIG_PM) += pm.o
+
obj-$(CONFIG_SOC_IMX23) += clock-mx23.o mm-mx23.o
obj-$(CONFIG_SOC_IMX28) += clock-mx28.o mm-mx28.o
diff --git a/arch/arm/mach-mxs/pm.c b/arch/arm/mach-mxs/pm.c
new file mode 100644
index 0000000..fb042da
--- /dev/null
+++ b/arch/arm/mach-mxs/pm.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+#include <linux/io.h>
+#include <mach/system.h>
+
+static int mxs_suspend_enter(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ arch_idle();
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct platform_suspend_ops mxs_suspend_ops = {
+ .enter = mxs_suspend_enter,
+ .valid = suspend_valid_only_mem,
+};
+
+static int __init mxs_pm_init(void)
+{
+ suspend_set_ops(&mxs_suspend_ops);
+ return 0;
+}
+device_initcall(mxs_pm_init);
--
1.7.1
^ permalink raw reply related
* patch ethtool 2.6.37 classificationoptions
From: Kelly Anderson @ 2011-01-06 7:00 UTC (permalink / raw)
To: bhutchings, netdev
[-- Attachment #1: Type: text/plain, Size: 86 bytes --]
Attached is a fix for the missing space in classificationoptions when
using --help.
[-- Attachment #2: EthTool-2.6.37-fix-classificationoptions.patch --]
[-- Type: text/plain, Size: 806 bytes --]
--- ./ethtool.c.orig 2010-12-16 17:37:59.000000000 -0700
+++ ./ethtool.c 2011-01-05 23:55:00.363077978 -0700
@@ -231,11 +231,11 @@ static struct option {
" [ online | offline ]\n" },
{ "-S", "--statistics", MODE_GSTATS, "Show adapter statistics" },
{ "-n", "--show-nfc", MODE_GNFC, "Show Rx network flow classification"
- "options",
+ " options",
" [ rx-flow-hash tcp4|udp4|ah4|sctp4|"
"tcp6|udp6|ah6|sctp6 ]\n" },
{ "-f", "--flash", MODE_FLASHDEV, "FILENAME " "Flash firmware image "
- "from the specified file to a region on the device",
+ "from the specified file to a region on the device",
" [ REGION-NUMBER-TO-FLASH ]\n" },
{ "-N", "--config-nfc", MODE_SNFC, "Configure Rx network flow "
"classification options",
^ permalink raw reply
* [RFC] Connection oriented routing
From: Michael Blizek @ 2011-01-06 7:34 UTC (permalink / raw)
To: netdev; +Cc: davem, chambilkethakur
Hi!
I am implementing a connection oriented source routed layer 3/4 protocol for
zero administration (community/mesh) networks. It takes care of things like
fair bandwidth allocation and has higher security/privacy so that it can really
run without administration. It is basically running, but not entirely stable
and with some loose ends. You can find some more info here:
http://michaelblizek.twilightparadox.com/projects/cor/index.html
git repo (based on 2.6.28):
http://repo.or.cz/w/cor.git?a=tree
My 2 previous queries to netdev:
http://article.gmane.org/gmane.linux.network/80232
http://article.gmane.org/gmane.linux.network/144915
-Michi
^ permalink raw reply
* Re: Bad TCP timestamps on non-PC platforms
From: Eric Dumazet @ 2011-01-06 8:09 UTC (permalink / raw)
To: Alex Dubov; +Cc: netdev
In-Reply-To: <219643.83181.qm@web37604.mail.mud.yahoo.com>
Le mercredi 05 janvier 2011 à 22:55 -0800, Alex Dubov a écrit :
> Greetings.
> I'm dealing with 2.6.37-rc7 kernel on MPC8548 platform.
>
> It so appears, that recent kernels have sysctl_tcp_timestamps set to "1"
> by default.
>
> On embedded platforms, where real time clock is initialized lately or
> absent outright, this causes TSval field of outgoing TCP packets to be
> set to some garbage value, in my case in the vicinity of 0xffffffff. As a
> result, other Linux machines silently drop such packets, preventing normal
> completion of network boot or any other TCP dependent operation.
>
> Therefore, I feel, two changes are necessary (I can send in a patch):
> 1. Make sysctl_tcp_timestamps value config-time selectable (it must be
> disabled by default on machines without RTC).
> 2. When re-enabling tcp_timestamps through sysctl, reset the timestamp
> counter to the current system clock value.
>
> And an optional, tricky one:
> 3. Postpone TCP timestamp counter initialization until RTC is actually
> available (if RTC is connected to i2c bus, TCP is initialized well ahead
> of it).
>
I am trying to understand the problem, but I guess you hit something not
related to RTC at all.
#define tcp_time_stamp ((__u32)(jiffies))
#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))
jiffies can be ZERO 300 seconds after boot.
It should not matter with RFC1323. Some TCP session could in theory be
done without TCP timestamps being activated (because SYN paquet would
carry a ZERO tsval/tsecr), but this should not prevents normal network
operation.
Are you sure the "other linux machines" dont have
strange /etc/sysctl.conf settings ?
^ permalink raw reply
* Re: [PATCH v3 08/10] ARM: mxs: add ocotp read function
From: Russell King - ARM Linux @ 2011-01-06 9:13 UTC (permalink / raw)
To: Jamie Lokier
Cc: Jamie Iles, gerg, B32542, netdev, s.hauer, bryan.wu, baruch,
w.sang, r64343, Shawn Guo, eric, Uwe Kleine-König, davem,
linux-arm-kernel, lw
In-Reply-To: <20110106005052.GA4476@shareable.org>
On Thu, Jan 06, 2011 at 12:50:52AM +0000, Jamie Lokier wrote:
> Russell King - ARM Linux wrote:
> > On Wed, Jan 05, 2011 at 07:44:18PM +0000, Jamie Lokier wrote:
> > > 'git show 534be1d5' explains how it works: cpu_relax() flushes buffered
> > > writes from _this_ CPU, so that other CPUs which are polling can make
> > > progress, which avoids this CPU getting stuck if there is an indirect
> > > dependency (no matter how convoluted) between what it's polling and which
> > > it wrote just before.
> > >
> > > So cpu_relax() is *essential* in some polling loops, not a hint.
> > >
> > > In principle that could happen for I/O polling, if (a) buffered memory
> > > writes are delayed by I/O read transactions, and (b) the device state we're
> > > waiting on depends on I/O yet to be done on another CPU, which could be
> > > polling memory first (e.g. a spinlock).
> > >
> > > I doubt (a) in practice - but what about buses that block during I/O read?
> > > (I have a chip like that here, but it's ARMv4T.)
> >
> > Let's be clear - ARMv5 and below generally are well ordered architectures
> > within the limits of caching. There are cases where the write buffer
> > allows two writes to pass each other. However, for IO we generally map
> > these - especially for ARMv4 and below - as 'uncacheable unbufferable'.
> > So on these, if the program says "read this location" the pipeline will
> > stall until the read has been issued - and if you use the result in the
> > next instruction, it will stall until the data is available. So really,
> > it's not a problem here.
> >
> > ARMv6 and above have a weakly ordered memory model with speculative
> > prefetching, so memory reads/writes can be completely unordered. Device
> > accesses can pass memory accesses, but device accesses are always visible
> > in program order with respect to each other.
> >
> > So, if you're spinning in a loop reading an IO device, all previous IO
> > accesses will be completed (in all ARM architectures) before the result
> > of your read is evaluated.
>
> No, that wasn't the scenario - it was:
>
> You're spinning reading an IO device, whose state depends indirectly
> on a *CPU memory* write that is forever buffered.
>
> (Go and re-read 'git show 534be1d5' if you haven't already.)
I know what that's about, and it's about memory based accesses _only_.
What you're talking about is a programming error. Such errors cause
data corruption if you're talking about DMA stuff.
At the moment, the solution to that is to put whatever's necessary into
readl/writel to ensure that they behave as ordered operations with
respect to everything else. You'll find that on ARM, writel has a
barrier before it to ensure memory writes are visible before the device
write, and on readl there's a barrier to ensure that no memory read can
happen before the IO device read.
cpu_relax() has nothing to do with ensuring ordering with devices.
With relaxed IO operations, the responsibility for ensuring proper ordering
between memory and IO falls to the programmer.
^ permalink raw reply
* Flow Control and Port Mirroring Revisited
From: Simon Horman @ 2011-01-06 9:33 UTC (permalink / raw)
To: Rusty Russell
Cc: virtualization, Jesse Gross, dev, virtualization, netdev, kvm,
Michael S. Tsirkin
Hi,
Back in October I reported that I noticed a problem whereby flow control
breaks down when openvswitch is configured to mirror a port[1].
I have (finally) looked into this further and the problem appears to relate
to cloning of skbs, as Jesse Gross originally suspected.
More specifically, in do_execute_actions[2] the first n-1 times that an skb
needs to be transmitted it is cloned first and the final time the original
skb is used.
In the case that there is only one action, which is the normal case, then
the original skb will be used. But in the case of mirroring the cloning
comes into effect. And in my case the cloned skb seems to go to the (slow)
eth1 interface while the original skb goes to the (fast) dummy0 interface
that I set up to be a mirror. The result is that dummy0 "paces" the flow,
and its a cracking pace at that.
As an experiment I hacked do_execute_actions() to use the original skb
for the first action instead of the last one. In my case the result was
that eth1 "paces" the flow, and things work reasonably nicely.
Well, sort of. Things work well for non-GSO skbs but extremely poorly for
GSO skbs where only 3 (yes 3, not 3%) end up at the remote host running
netserv. I'm unsure why, but I digress.
It seems to me that my hack illustrates the point that the flow ends up
being "paced" by one interface. However I think that what would be
desirable is that the flow is "paced" by the slowest link. Unfortunately
I'm unsure how to achieve that.
One idea that I had was to skb_get() the original skb each time it is
cloned - that is easy enough. But unfortunately it seems to me that
approach would require some sort of callback mechanism in kfree_skb() so
that the cloned skbs can kfree_skb() the original skb.
Ideas would be greatly appreciated.
[1] http://openvswitch.org/pipermail/dev_openvswitch.org/2010-October/003806.html
[2] http://openvswitch.org/cgi-bin/gitweb.cgi?p=openvswitch;a=blob;f=datapath/actions.c;h=5e16143ca402f7da0ee8fc18ee5eb16c3b7598e6;hb=HEAD
^ permalink raw reply
* [PATCH] net: ppp: use {get,put}_unaligned_be{16,32}
From: Changli Gao @ 2011-01-06 9:37 UTC (permalink / raw)
To: David S. Miller; +Cc: Paul Mackerras, linux-ppp, netdev, Changli Gao
Signed-off-by: Changli Gao <xiaosuo@gmail.com>
---
drivers/net/ppp_async.c | 10 +++++-----
drivers/net/ppp_deflate.c | 9 ++++-----
drivers/net/ppp_generic.c | 9 ++++-----
drivers/net/ppp_mppe.c | 7 +++----
drivers/net/ppp_synctty.c | 3 ++-
5 files changed, 18 insertions(+), 20 deletions(-)
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 78d70a6..cbe1e13 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -32,6 +32,7 @@
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
+#include <linux/unaligned/be_struct.h>
#include <asm/uaccess.h>
#include <asm/string.h>
@@ -542,7 +543,7 @@ ppp_async_encode(struct asyncppp *ap)
data = ap->tpkt->data;
count = ap->tpkt->len;
fcs = ap->tfcs;
- proto = (data[0] << 8) + data[1];
+ proto = get_unaligned_be16(data);
/*
* LCP packets with code values between 1 (configure-reqest)
@@ -963,7 +964,7 @@ static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
code = data[0];
if (code != CONFACK && code != CONFREQ)
return;
- dlen = (data[2] << 8) + data[3];
+ dlen = get_unaligned_be16(data + 2);
if (len < dlen)
return; /* packet got truncated or length is bogus */
@@ -997,15 +998,14 @@ static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
while (dlen >= 2 && dlen >= data[1] && data[1] >= 2) {
switch (data[0]) {
case LCP_MRU:
- val = (data[2] << 8) + data[3];
+ val = get_unaligned_be16(data + 2);
if (inbound)
ap->mru = val;
else
ap->chan.mtu = val;
break;
case LCP_ASYNCMAP:
- val = (data[2] << 24) + (data[3] << 16)
- + (data[4] << 8) + data[5];
+ val = get_unaligned_be32(data + 2);
if (inbound)
ap->raccm = val;
else
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
index 695bc83..df3ce78 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp_deflate.c
@@ -41,6 +41,7 @@
#include <linux/ppp-comp.h>
#include <linux/zlib.h>
+#include <linux/unaligned/be_struct.h>
/*
* State for a Deflate (de)compressor.
@@ -232,11 +233,9 @@ static int z_compress(void *arg, unsigned char *rptr, unsigned char *obuf,
*/
wptr[0] = PPP_ADDRESS(rptr);
wptr[1] = PPP_CONTROL(rptr);
- wptr[2] = PPP_COMP >> 8;
- wptr[3] = PPP_COMP;
+ put_unaligned_be16(PPP_COMP, wptr + 2);
wptr += PPP_HDRLEN;
- wptr[0] = state->seqno >> 8;
- wptr[1] = state->seqno;
+ put_unaligned_be16(state->seqno, wptr);
wptr += DEFLATE_OVHD;
olen = PPP_HDRLEN + DEFLATE_OVHD;
state->strm.next_out = wptr;
@@ -451,7 +450,7 @@ static int z_decompress(void *arg, unsigned char *ibuf, int isize,
}
/* Check the sequence number. */
- seq = (ibuf[PPP_HDRLEN] << 8) + ibuf[PPP_HDRLEN+1];
+ seq = get_unaligned_be16(ibuf + PPP_HDRLEN);
if (seq != (state->seqno & 0xffff)) {
if (state->debug)
printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n",
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 6456484..0a81f0b 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -46,6 +46,7 @@
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/unaligned/be_struct.h>
#include <net/slhc_vj.h>
#include <asm/atomic.h>
@@ -210,7 +211,7 @@ struct ppp_net {
};
/* Get the PPP protocol number from a skb */
-#define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1])
+#define PPP_PROTO(skb) get_unaligned_be16((skb)->data)
/* We limit the length of ppp->file.rq to this (arbitrary) value */
#define PPP_MAX_RQLEN 32
@@ -964,8 +965,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
pp = skb_push(skb, 2);
proto = npindex_to_proto[npi];
- pp[0] = proto >> 8;
- pp[1] = proto;
+ put_unaligned_be16(proto, pp);
netif_stop_queue(dev);
skb_queue_tail(&ppp->file.xq, skb);
@@ -1473,8 +1473,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
q = skb_put(frag, flen + hdrlen);
/* make the MP header */
- q[0] = PPP_MP >> 8;
- q[1] = PPP_MP;
+ put_unaligned_be16(PPP_MP, q);
if (ppp->flags & SC_MP_XSHORTSEQ) {
q[2] = bits + ((ppp->nxseq >> 8) & 0xf);
q[3] = ppp->nxseq;
diff --git a/drivers/net/ppp_mppe.c b/drivers/net/ppp_mppe.c
index 6d1a1b8..44dab65 100644
--- a/drivers/net/ppp_mppe.c
+++ b/drivers/net/ppp_mppe.c
@@ -55,6 +55,7 @@
#include <linux/ppp_defs.h>
#include <linux/ppp-comp.h>
#include <linux/scatterlist.h>
+#include <linux/unaligned/be_struct.h>
#include "ppp_mppe.h"
@@ -395,16 +396,14 @@ mppe_compress(void *arg, unsigned char *ibuf, unsigned char *obuf,
*/
obuf[0] = PPP_ADDRESS(ibuf);
obuf[1] = PPP_CONTROL(ibuf);
- obuf[2] = PPP_COMP >> 8; /* isize + MPPE_OVHD + 1 */
- obuf[3] = PPP_COMP; /* isize + MPPE_OVHD + 2 */
+ put_unaligned_be16(PPP_COMP, obuf + 2);
obuf += PPP_HDRLEN;
state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
if (state->debug >= 7)
printk(KERN_DEBUG "mppe_compress[%d]: ccount %d\n", state->unit,
state->ccount);
- obuf[0] = state->ccount >> 8;
- obuf[1] = state->ccount & 0xff;
+ put_unaligned_be16(state->ccount, obuf);
if (!state->stateful || /* stateless mode */
((state->ccount & 0xff) == 0xff) || /* "flag" packet */
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
index 4c95ec3..fe86826 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp_synctty.c
@@ -45,6 +45,7 @@
#include <linux/completion.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/unaligned/be_struct.h>
#include <asm/uaccess.h>
#define PPP_VERSION "2.4.2"
@@ -563,7 +564,7 @@ ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
int islcp;
data = skb->data;
- proto = (data[0] << 8) + data[1];
+ proto = get_unaligned_be16(data);
/* LCP packets with codes between 1 (configure-request)
* and 7 (code-reject) must be sent as though no options
^ permalink raw reply related
* Re: Flow Control and Port Mirroring Revisited
From: Eric Dumazet @ 2011-01-06 10:22 UTC (permalink / raw)
To: Simon Horman
Cc: Rusty Russell, virtualization, Jesse Gross, dev, virtualization,
netdev, kvm, Michael S. Tsirkin
In-Reply-To: <20110106093312.GA1564@verge.net.au>
Le jeudi 06 janvier 2011 à 18:33 +0900, Simon Horman a écrit :
> Hi,
>
> Back in October I reported that I noticed a problem whereby flow control
> breaks down when openvswitch is configured to mirror a port[1].
>
> I have (finally) looked into this further and the problem appears to relate
> to cloning of skbs, as Jesse Gross originally suspected.
>
> More specifically, in do_execute_actions[2] the first n-1 times that an skb
> needs to be transmitted it is cloned first and the final time the original
> skb is used.
>
> In the case that there is only one action, which is the normal case, then
> the original skb will be used. But in the case of mirroring the cloning
> comes into effect. And in my case the cloned skb seems to go to the (slow)
> eth1 interface while the original skb goes to the (fast) dummy0 interface
> that I set up to be a mirror. The result is that dummy0 "paces" the flow,
> and its a cracking pace at that.
>
> As an experiment I hacked do_execute_actions() to use the original skb
> for the first action instead of the last one. In my case the result was
> that eth1 "paces" the flow, and things work reasonably nicely.
>
> Well, sort of. Things work well for non-GSO skbs but extremely poorly for
> GSO skbs where only 3 (yes 3, not 3%) end up at the remote host running
> netserv. I'm unsure why, but I digress.
>
> It seems to me that my hack illustrates the point that the flow ends up
> being "paced" by one interface. However I think that what would be
> desirable is that the flow is "paced" by the slowest link. Unfortunately
> I'm unsure how to achieve that.
>
Hi Simon !
"pacing" is done because skb is attached to a socket, and a socket has a
limited (but configurable) sndbuf. sk->sk_wmem_alloc is the current sum
of all truesize skbs in flight.
When you enter something that :
1) Get a clone of the skb, queue the clone to device X
2) queue the original skb to device Y
Then : Socket sndbuf is not affected at all by device X queue.
This is speed on device Y that matters.
You want to get servo control on both X and Y
You could try to
1) Get a clone of skb
Attach it to socket too (so that socket get a feedback of final
orphaning for the clone) with skb_set_owner_w()
queue the clone to device X
Unfortunatly, stacked skb->destructor() makes this possible only for
known destructor (aka sock_wfree())
> One idea that I had was to skb_get() the original skb each time it is
> cloned - that is easy enough. But unfortunately it seems to me that
> approach would require some sort of callback mechanism in kfree_skb() so
> that the cloned skbs can kfree_skb() the original skb.
>
> Ideas would be greatly appreciated.
>
> [1] http://openvswitch.org/pipermail/dev_openvswitch.org/2010-October/003806.html
> [2] http://openvswitch.org/cgi-bin/gitweb.cgi?p=openvswitch;a=blob;f=datapath/actions.c;h=5e16143ca402f7da0ee8fc18ee5eb16c3b7598e6;hb=HEAD
> --
^ permalink raw reply
* Re: Flow Control and Port Mirroring Revisited
From: Michael S. Tsirkin @ 2011-01-06 10:27 UTC (permalink / raw)
To: Simon Horman
Cc: Rusty Russell, virtualization, Jesse Gross, dev, virtualization,
netdev, kvm
In-Reply-To: <20110106093312.GA1564@verge.net.au>
On Thu, Jan 06, 2011 at 06:33:12PM +0900, Simon Horman wrote:
> Hi,
>
> Back in October I reported that I noticed a problem whereby flow control
> breaks down when openvswitch is configured to mirror a port[1].
Apropos the UDP flow control. See this
http://www.spinics.net/lists/netdev/msg150806.html
for some problems it introduces.
Unfortunately UDP does not have built-in flow control.
At some level it's just conceptually broken:
it's not present in physical networks so why should
we try and emulate it in a virtual network?
Specifically, when you do:
# netperf -c -4 -t UDP_STREAM -H 172.17.60.218 -l 30 -- -m 1472
You are asking: what happens if I push data faster than it can be received?
But why is this an interesting question?
Ask 'what is the maximum rate at which I can send data with %X packet
loss' or 'what is the packet loss at rate Y Gb/s'. netperf has
-b and -w flags for this. It needs to be configured
with --enable-intervals=yes for them to work.
If you pose the questions this way the problem of pacing
the execution just goes away.
>
> I have (finally) looked into this further and the problem appears to relate
> to cloning of skbs, as Jesse Gross originally suspected.
>
> More specifically, in do_execute_actions[2] the first n-1 times that an skb
> needs to be transmitted it is cloned first and the final time the original
> skb is used.
>
> In the case that there is only one action, which is the normal case, then
> the original skb will be used. But in the case of mirroring the cloning
> comes into effect. And in my case the cloned skb seems to go to the (slow)
> eth1 interface while the original skb goes to the (fast) dummy0 interface
> that I set up to be a mirror. The result is that dummy0 "paces" the flow,
> and its a cracking pace at that.
>
> As an experiment I hacked do_execute_actions() to use the original skb
> for the first action instead of the last one. In my case the result was
> that eth1 "paces" the flow, and things work reasonably nicely.
>
> Well, sort of. Things work well for non-GSO skbs but extremely poorly for
> GSO skbs where only 3 (yes 3, not 3%) end up at the remote host running
> netserv. I'm unsure why, but I digress.
>
> It seems to me that my hack illustrates the point that the flow ends up
> being "paced" by one interface. However I think that what would be
> desirable is that the flow is "paced" by the slowest link. Unfortunately
> I'm unsure how to achieve that.
What if you have multiple UDP sockets with different targets
in the guest?
> One idea that I had was to skb_get() the original skb each time it is
> cloned - that is easy enough. But unfortunately it seems to me that
> approach would require some sort of callback mechanism in kfree_skb() so
> that the cloned skbs can kfree_skb() the original skb.
>
> Ideas would be greatly appreciated.
>
> [1] http://openvswitch.org/pipermail/dev_openvswitch.org/2010-October/003806.html
> [2] http://openvswitch.org/cgi-bin/gitweb.cgi?p=openvswitch;a=blob;f=datapath/actions.c;h=5e16143ca402f7da0ee8fc18ee5eb16c3b7598e6;hb=HEAD
^ permalink raw reply
* Re: Flow Control and Port Mirroring Revisited
From: Simon Horman @ 2011-01-06 11:30 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Rusty Russell, virtualization, Jesse Gross, dev, virtualization,
netdev, kvm
In-Reply-To: <20110106102755.GC12142@redhat.com>
On Thu, Jan 06, 2011 at 12:27:55PM +0200, Michael S. Tsirkin wrote:
> On Thu, Jan 06, 2011 at 06:33:12PM +0900, Simon Horman wrote:
> > Hi,
> >
> > Back in October I reported that I noticed a problem whereby flow control
> > breaks down when openvswitch is configured to mirror a port[1].
>
> Apropos the UDP flow control. See this
> http://www.spinics.net/lists/netdev/msg150806.html
> for some problems it introduces.
> Unfortunately UDP does not have built-in flow control.
> At some level it's just conceptually broken:
> it's not present in physical networks so why should
> we try and emulate it in a virtual network?
>
>
> Specifically, when you do:
> # netperf -c -4 -t UDP_STREAM -H 172.17.60.218 -l 30 -- -m 1472
> You are asking: what happens if I push data faster than it can be received?
> But why is this an interesting question?
> Ask 'what is the maximum rate at which I can send data with %X packet
> loss' or 'what is the packet loss at rate Y Gb/s'. netperf has
> -b and -w flags for this. It needs to be configured
> with --enable-intervals=yes for them to work.
>
> If you pose the questions this way the problem of pacing
> the execution just goes away.
I am aware that UDP inherently lacks flow control.
The aspect of flow control that I am interested in is situations where the
guest can create large amounts of work for the host. However, it seems that
in the case of virtio with vhostnet that the CPU utilisation seems to be
almost entirely attributable to the vhost and qemu-system processes. And
in the case of virtio without vhost net the CPU is used by the qemu-system
process. In both case I assume that I could use a cgroup or something
similar to limit the guests.
Assuming all of that is true then from a resource control problem point of
view, which is mostly what I am concerned about, the problem goes away.
However, I still think that it would be nice to resolve the situation I
described.
^ permalink raw reply
* via-velocity: corrupted mac, deadlock on link
From: Janne Karhunen @ 2011-01-06 11:42 UTC (permalink / raw)
To: netdev
Hey,
Just got my hands on via-velocity pci-e card and tried to give it
a go on new Asus Nvidia Ion2 board. Driver loads fine and everything
seems good until device gets a link - kernel deadlocks instantly. During
bootup hang seems to happen when persistent-net rules rename device
to eth1, after bootup instantly when cable is plugged (first interrupt?).
Any other quick suggestions other than to throw it away as DOA?
Linux rivendell 2.6.35-23-generic #41-Ubuntu SMP Wed Nov 24 10:18:49
UTC 2010 i686 GNU/Linux
eth1 Link encap:Ethernet HWaddr 00:00:00:00:00:04 (??????????)
[ 1.171890] eth0: VIA Networking Velocity Family Gigabit Ethernet Adapter
[ 18.468029] via-velocity 0000:0b:00.0: BAR 0: set to [io
0xe800-0xe8ff] (PCI address [0xe800-0xe8ff]
[ 18.468048] via-velocity 0000:0b:00.0: BAR 1: set to [mem
0xfbfffc00-0xfbfffcff 64bit] (PCI address [0xfbfffc00-0xfbfffcff]
0b:00.0 Ethernet controller: VIA Technologies, Inc.
VT6120/VT6121/VT6122 Gigabit Ethernet Adapter (rev 82)
16: 0 0 0 0 IO-APIC-fasteoi
uhci_hcd:usb5
17: 0 0 0 0 IO-APIC-fasteoi eth1
18: 4639 4703 4637 4690 IO-APIC-fasteoi
uhci_hcd:usb4, ahci, hda_intel, nvidia
19: 0 0 0 1 IO-APIC-fasteoi
uhci_hcd:usb3, xhci_hcd:usb6
23: 14772 14895 14872 14730 IO-APIC-fasteoi
ehci_hcd:usb1, uhci_hcd:usb2
40: 0 0 0 0 PCI-MSI-edge pciehp
41: 0 0 0 0 PCI-MSI-edge pciehp
42: 0 0 0 0 PCI-MSI-edge pciehp
43: 0 0 0 0 PCI-MSI-edge pciehp
52: 10951 10844 10829 10926 PCI-MSI-edge eth0
--
// Janne
^ permalink raw reply
* Re: Flow Control and Port Mirroring Revisited
From: Michael S. Tsirkin @ 2011-01-06 12:07 UTC (permalink / raw)
To: Simon Horman
Cc: Rusty Russell, virtualization, Jesse Gross, dev, virtualization,
netdev, kvm
In-Reply-To: <20110106113052.GA2541@verge.net.au>
On Thu, Jan 06, 2011 at 08:30:52PM +0900, Simon Horman wrote:
> On Thu, Jan 06, 2011 at 12:27:55PM +0200, Michael S. Tsirkin wrote:
> > On Thu, Jan 06, 2011 at 06:33:12PM +0900, Simon Horman wrote:
> > > Hi,
> > >
> > > Back in October I reported that I noticed a problem whereby flow control
> > > breaks down when openvswitch is configured to mirror a port[1].
> >
> > Apropos the UDP flow control. See this
> > http://www.spinics.net/lists/netdev/msg150806.html
> > for some problems it introduces.
> > Unfortunately UDP does not have built-in flow control.
> > At some level it's just conceptually broken:
> > it's not present in physical networks so why should
> > we try and emulate it in a virtual network?
> >
> >
> > Specifically, when you do:
> > # netperf -c -4 -t UDP_STREAM -H 172.17.60.218 -l 30 -- -m 1472
> > You are asking: what happens if I push data faster than it can be received?
> > But why is this an interesting question?
> > Ask 'what is the maximum rate at which I can send data with %X packet
> > loss' or 'what is the packet loss at rate Y Gb/s'. netperf has
> > -b and -w flags for this. It needs to be configured
> > with --enable-intervals=yes for them to work.
> >
> > If you pose the questions this way the problem of pacing
> > the execution just goes away.
>
> I am aware that UDP inherently lacks flow control.
Everyone's is aware of that, but this is always followed by a 'however'
:).
> The aspect of flow control that I am interested in is situations where the
> guest can create large amounts of work for the host. However, it seems that
> in the case of virtio with vhostnet that the CPU utilisation seems to be
> almost entirely attributable to the vhost and qemu-system processes. And
> in the case of virtio without vhost net the CPU is used by the qemu-system
> process. In both case I assume that I could use a cgroup or something
> similar to limit the guests.
cgroups, yes. the vhost process inherits the cgroups
from the qemu process so you can limit them all.
If you are after limiting the max troughput of the guest
you can do this with cgroups as well.
> Assuming all of that is true then from a resource control problem point of
> view, which is mostly what I am concerned about, the problem goes away.
> However, I still think that it would be nice to resolve the situation I
> described.
We need to articulate what's wrong here, otherwise we won't
be able to resolve the situation. We are sending UDP packets
as fast as we can and some receivers can't cope. Is this the problem?
We have made attempts to add a pseudo flow control in the past
in an attempt to make UDP on the same host work better.
Maybe they help some but they also sure introduce problems.
--
MST
^ permalink raw reply
* Re: Flow Control and Port Mirroring Revisited
From: Simon Horman @ 2011-01-06 12:29 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Rusty Russell, virtualization, Jesse Gross, dev, virtualization,
netdev, kvm
In-Reply-To: <20110106120722.GD12142@redhat.com>
On Thu, Jan 06, 2011 at 02:07:22PM +0200, Michael S. Tsirkin wrote:
> On Thu, Jan 06, 2011 at 08:30:52PM +0900, Simon Horman wrote:
> > On Thu, Jan 06, 2011 at 12:27:55PM +0200, Michael S. Tsirkin wrote:
> > > On Thu, Jan 06, 2011 at 06:33:12PM +0900, Simon Horman wrote:
> > > > Hi,
> > > >
> > > > Back in October I reported that I noticed a problem whereby flow control
> > > > breaks down when openvswitch is configured to mirror a port[1].
> > >
> > > Apropos the UDP flow control. See this
> > > http://www.spinics.net/lists/netdev/msg150806.html
> > > for some problems it introduces.
> > > Unfortunately UDP does not have built-in flow control.
> > > At some level it's just conceptually broken:
> > > it's not present in physical networks so why should
> > > we try and emulate it in a virtual network?
> > >
> > >
> > > Specifically, when you do:
> > > # netperf -c -4 -t UDP_STREAM -H 172.17.60.218 -l 30 -- -m 1472
> > > You are asking: what happens if I push data faster than it can be received?
> > > But why is this an interesting question?
> > > Ask 'what is the maximum rate at which I can send data with %X packet
> > > loss' or 'what is the packet loss at rate Y Gb/s'. netperf has
> > > -b and -w flags for this. It needs to be configured
> > > with --enable-intervals=yes for them to work.
> > >
> > > If you pose the questions this way the problem of pacing
> > > the execution just goes away.
> >
> > I am aware that UDP inherently lacks flow control.
>
> Everyone's is aware of that, but this is always followed by a 'however'
> :).
>
> > The aspect of flow control that I am interested in is situations where the
> > guest can create large amounts of work for the host. However, it seems that
> > in the case of virtio with vhostnet that the CPU utilisation seems to be
> > almost entirely attributable to the vhost and qemu-system processes. And
> > in the case of virtio without vhost net the CPU is used by the qemu-system
> > process. In both case I assume that I could use a cgroup or something
> > similar to limit the guests.
>
> cgroups, yes. the vhost process inherits the cgroups
> from the qemu process so you can limit them all.
>
> If you are after limiting the max troughput of the guest
> you can do this with cgroups as well.
Do you mean a CPU cgroup or something else?
> > Assuming all of that is true then from a resource control problem point of
> > view, which is mostly what I am concerned about, the problem goes away.
> > However, I still think that it would be nice to resolve the situation I
> > described.
>
> We need to articulate what's wrong here, otherwise we won't
> be able to resolve the situation. We are sending UDP packets
> as fast as we can and some receivers can't cope. Is this the problem?
> We have made attempts to add a pseudo flow control in the past
> in an attempt to make UDP on the same host work better.
> Maybe they help some but they also sure introduce problems.
In the case where port mirroring is not active, which is the
usual case, to some extent there is flow control in place due to
(as Eric Dumazet pointed out) the socket buffer.
When port mirroring is activated the flow control operates based
only on one port - which can't be controlled by the administrator
in an obvious way.
I think that it would be more intuitive if flow control was
based on sending a packet to all ports rather than just one.
Though now I think about it some more, perhaps this isn't the best either.
For instance the case where data was being sent to dummy0 and suddenly
adding a mirror on eth1 slowed everything down.
So perhaps there needs to be another knob to tune when setting
up port-mirroring. Or perhaps the current situation isn't so bad.
^ permalink raw reply
* Re: [RFC v2 PATCH] m68knommu: added dm9000 support
From: Angelo Dureghello @ 2011-01-06 12:39 UTC (permalink / raw)
To: Arnaud Lacombe; +Cc: linux-kernel, netdev, m68k
In-Reply-To: <AANLkTi=E0hkTUSd-rXWh5tkct0wwYX_75E-oaSWaa2bn@mail.gmail.com>
thanks for the review,
- sure, i agree the sw byte swap could be related to a BIG_ENDIAN config option only.
- about HW, can be replaced with something like "dm9000 to cpu bus wiring", i will be more clear in my next patch version.
- i will remove blank lines
- readsX and writesX was completely missing for m68knommu, i have seen that the most appropriate file where to create them was probably "linux/arch/m68k/include/asm/io_no.h" but any comment is appreciated.
- i am adding m68k guys list from now.
thanks
angelo
On 06/01/2011 00:51, Arnaud Lacombe wrote:
> Hi,
>
> [disclamer: I have no power whatsoever on the dm9k driver, and the
> only dm9000 chip I've access to belongs to hardware whose parent
> company is now defunct.]
>
> Btw, Linux 68k folks is missing from the CC list.
>
> On Wed, Jan 5, 2011 at 1:03 PM, Angelo Dureghello <angelo70@gmail.com> wrote:
>> This patch allows to use the dm9000 network chip with a m68knommu
>> big-endian cpu. From the HW point of view,
> what hardware ?
>
>> the cpu data bus connected to
>> the dm9000 chip should be hardware-byte-swapped, crossing the bytes
>> wires (D0:7 to D24:31, etc.). In anyway, has been also added an option
>> to swap the bytes in the driver, if some cpu has been wired straight
>> D0:D31 to dm9000.
>>
>> Signed-off-by: Angelo Dureghello <angelo70@gmail.com>
>> ---
>>
>> --- linux/drivers/net/Kconfig.orig 2011-01-05 17:11:37.992376124 +0100
>> +++ linux/drivers/net/Kconfig 2011-01-04 22:33:14.132301872 +0100
>> @@ -960,7 +960,7 @@ config TI_DAVINCI_EMAC
>>
>> config DM9000
>> tristate "DM9000 support"
>> - depends on ARM || BLACKFIN || MIPS
>> + depends on COLDFIRE || ARM || BLACKFIN || MIPS
>> select CRC32
>> select MII
>> ---help---
>> @@ -986,6 +986,14 @@ config DM9000_FORCE_SIMPLE_PHY_POLL
>> costly MII PHY reads. Note, this will not work if the chip is
>> operating with an external PHY.
>>
>> +config DM9000_32BIT_SW_SWAP
>> + bool "Software byte swap for 32 bit data bus"
>> + depends on DM9000 && COLDFIRE
>> + ---help---
>> + This configuration allows to swap data bytes from the dm9000
>> + driver itself, when the big endian cpu is wired straight to
>> + the dm9000 32 bit data bus.
>> +
> I'm not sure COLDFIRE is the best dependency. AFAICS, it should
> depends on endianness, and potentially board specific settings.
>
>> config ENC28J60
>> tristate "ENC28J60 support"
>> depends on EXPERIMENTAL && SPI && NET_ETHERNET
>> @@ -3347,4 +3355,3 @@ config VMXNET3
>> module will be called vmxnet3.
>>
>> endif # NETDEVICES
>> -
>>
> useless whitespace change ...
>
>> --- linux/drivers/net/dm9000.c.orig 2010-12-30 23:19:39.747836070 +0100
>> +++ linux/drivers/net/dm9000.c 2011-01-05 16:30:48.636116500 +0100
>> [...]
>> @@ -981,7 +1032,11 @@ dm9000_rx(struct net_device *dev)
>> ior(db, DM9000_MRCMDX); /* Dummy read */
>>
>> /* Get most updated data */
>> - rxbyte = readb(db->io_data);
>> +#ifdef CONFIG_DM9000_32BIT_SW_SWAP
>> + rxbyte = (u8)readl(db->io_data);
>> +#else
>> + rxbyte = readb(db->io_data);
>> +#endif
>>
>> /* Status check: this byte must be 0 or 1 */
>> if (rxbyte & DM9000_PKT_ERR) {
>> @@ -996,8 +1051,13 @@ dm9000_rx(struct net_device *dev)
>>
>> /* A packet ready now & Get status/length */
>> GoodPacket = true;
>> - writeb(DM9000_MRCMD, db->io_addr);
>>
>> +#ifdef CONFIG_DM9000_32BIT_SW_SWAP
>> + writel(DM9000_MRCMD, db->io_addr);
>> +#else
>> + writeb(DM9000_MRCMD, db->io_addr);
>> +#endif
>> +
> All these #ifdef make the driver quite horrible to read.
>
>> (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));
>>
>> RxLen = le16_to_cpu(rxhdr.RxLen);
>> @@ -1077,7 +1137,7 @@ static irqreturn_t dm9000_interrupt(int
>> unsigned long flags;
>> u8 reg_save;
>>
>> - dm9000_dbg(db, 3, "entering %s\n", __func__);
>> + //dm9000_dbg(db, 3, "entering %s\n", __func__);
>>
> C++ comment ... Why do you comment this ?
>
>> /* Disable all interrupts */
>> iow(db, DM9000_IMR, IMR_PAR);
>> @@ -1100,7 +1164,7 @@ static irqreturn_t dm9000_interrupt(int
>> /* Received the coming packet */
>> if (int_status & ISR_PRS)
>> dm9000_rx(dev);
>> -
>> +
> whitespace ...
>
>> @@ -1233,11 +1301,15 @@ dm9000_phy_read(struct net_device *dev,
>> int ret;
>>
>> mutex_lock(&db->addr_lock);
>> -
>> +
> whitespace ...
>
>> @@ -1713,4 +1811,3 @@ MODULE_AUTHOR("Sascha Hauer, Ben Dooks")
>> MODULE_DESCRIPTION("Davicom DM9000 network driver");
>> MODULE_LICENSE("GPL");
>> MODULE_ALIAS("platform:dm9000");
>> -
> whitespace ...
>
>> --- linux/arch/m68k/include/asm/io_no.h.orig 2011-01-05 16:53:55.904905038 +0100
>> +++ linux/arch/m68k/include/asm/io_no.h 2011-01-04 23:45:08.893049554 +0100
>> @@ -47,6 +47,91 @@ static inline unsigned int _swapl(volati
>> #define writew(b,addr) (void)((*(volatile unsigned short *) (addr)) = (b))
>> #define writel(b,addr) (void)((*(volatile unsigned int *) (addr)) = (b))
>>
>> +static inline void writesb (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned char *p = (unsigned char*) data;
>> +
>> + while (count--) writeb(*p++, reg);
>> +}
>> +
>> +static inline void writesbsw (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned char *p = (unsigned char *) data;
>> +
>> + while (count--) writel((int)(*p++), reg);
>> +}
>> +
>> +static inline void writesw (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned short *p = (unsigned short*) data;
>> +
>> + while (count--) writew(*p++, reg);
>> +}
>> +
>> +static inline void writeswsw (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned short *p = (unsigned short *) data;
>> +
>> + while (count--) writel((int)(_swapw(*p++)), reg);
>> +}
>> +
>> +static inline void writesl (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned long *p = (unsigned long*) data;
>> +
>> + while (count--) writel(*p++, reg);
>> +}
>> +
>> +static inline void writeslsw (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned long *p = (unsigned long *) data;
>> +
>> + while (count--) writel((int)(_swapl(*p++)), reg);
>> +}
>> +
>> +static inline void readsb (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned char *p = (unsigned char *) data;
>> +
>> + while (count--) *p++ = readb(reg);
>> +}
>> +
>> +static inline void readsbsw (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned char *p = (unsigned char *) data;
>> +
>> + while (count--) *p++ = (unsigned char)readl(reg);
>> +}
>> +
>> +static inline void readsw (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned short *p = (unsigned short *) data;
>> +
>> + while (count--) *p++ = readb(reg);
>> +}
>> +
>> +static inline void readswsw (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned short *p = (unsigned short *) data;
>> +
>> + while (count--) *p++ = _swapw((unsigned short)readw(reg));
>> +}
>> +
>> +static inline void readsl (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned long *p = (unsigned long *) data;
>> +
>> + while (count--) *p++ = readb(reg);
>> +}
>> +
>> +static inline void readslsw (void __iomem *reg, void *data, int count)
>> +{
>> + unsigned long *p = (unsigned long *) data;
>> +
>> + while (count--) *p++ = _swapl(readl(reg));
>> +}
>> +
>> +
>> #define __raw_readb readb
>> #define __raw_readw readw
>> #define __raw_readl readl
>> @@ -180,4 +265,3 @@ extern void iounmap(void *addr);
>> #endif /* __KERNEL__ */
>>
>> #endif /* _M68KNOMMU_IO_H */
> Could not this be moved to a separate patch ? That way arch specific
> changes are separated from the network arch-indep changes.
>
> - Arnaud
>
>> -
>> --
>> 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: Simon Horman @ 2011-01-06 12:44 UTC (permalink / raw)
To: Eric Dumazet
Cc: Rusty Russell, virtualization, Jesse Gross, dev, virtualization,
netdev, kvm, Michael S. Tsirkin
In-Reply-To: <1294309362.3074.11.camel@edumazet-laptop>
On Thu, Jan 06, 2011 at 11:22:42AM +0100, Eric Dumazet wrote:
> Le jeudi 06 janvier 2011 à 18:33 +0900, Simon Horman a écrit :
> > Hi,
> >
> > Back in October I reported that I noticed a problem whereby flow control
> > breaks down when openvswitch is configured to mirror a port[1].
> >
> > I have (finally) looked into this further and the problem appears to relate
> > to cloning of skbs, as Jesse Gross originally suspected.
> >
> > More specifically, in do_execute_actions[2] the first n-1 times that an skb
> > needs to be transmitted it is cloned first and the final time the original
> > skb is used.
> >
> > In the case that there is only one action, which is the normal case, then
> > the original skb will be used. But in the case of mirroring the cloning
> > comes into effect. And in my case the cloned skb seems to go to the (slow)
> > eth1 interface while the original skb goes to the (fast) dummy0 interface
> > that I set up to be a mirror. The result is that dummy0 "paces" the flow,
> > and its a cracking pace at that.
> >
> > As an experiment I hacked do_execute_actions() to use the original skb
> > for the first action instead of the last one. In my case the result was
> > that eth1 "paces" the flow, and things work reasonably nicely.
> >
> > Well, sort of. Things work well for non-GSO skbs but extremely poorly for
> > GSO skbs where only 3 (yes 3, not 3%) end up at the remote host running
> > netserv. I'm unsure why, but I digress.
> >
> > It seems to me that my hack illustrates the point that the flow ends up
> > being "paced" by one interface. However I think that what would be
> > desirable is that the flow is "paced" by the slowest link. Unfortunately
> > I'm unsure how to achieve that.
> >
>
> Hi Simon !
>
> "pacing" is done because skb is attached to a socket, and a socket has a
> limited (but configurable) sndbuf. sk->sk_wmem_alloc is the current sum
> of all truesize skbs in flight.
>
> When you enter something that :
>
> 1) Get a clone of the skb, queue the clone to device X
> 2) queue the original skb to device Y
>
> Then : Socket sndbuf is not affected at all by device X queue.
> This is speed on device Y that matters.
>
> You want to get servo control on both X and Y
>
> You could try to
>
> 1) Get a clone of skb
> Attach it to socket too (so that socket get a feedback of final
> orphaning for the clone) with skb_set_owner_w()
> queue the clone to device X
>
> Unfortunatly, stacked skb->destructor() makes this possible only for
> known destructor (aka sock_wfree())
Hi Eric !
Thanks for the advice. I had thought about the socket buffer but at some
point it slipped my mind.
In any case the following patch seems to implement the change that I had in
mind. However my discussions Michael Tsirkin elsewhere in this thread are
beginning to make me think that think that perhaps this change isn't the
best solution.
diff --git a/datapath/actions.c b/datapath/actions.c
index 5e16143..505f13f 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -384,7 +384,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
for (a = actions, rem = actions_len; rem > 0; a = nla_next(a, &rem)) {
if (prev_port != -1) {
- do_output(dp, skb_clone(skb, GFP_ATOMIC), prev_port);
+ struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
+ if (nskb) {
+ if (skb->sk)
+ skb_set_owner_w(nskb, skb->sk);
+ do_output(dp, nskb, prev_port);
+ }
prev_port = -1;
}
I got a rather nasty panic without the if (skb->sk),
I guess some skbs don't have a socket.
^ permalink raw reply related
* Re: Flow Control and Port Mirroring Revisited
From: Michael S. Tsirkin @ 2011-01-06 12:47 UTC (permalink / raw)
To: Simon Horman
Cc: Rusty Russell, virtualization, Jesse Gross, dev, virtualization,
netdev, kvm
In-Reply-To: <20110106122859.GA13253@verge.net.au>
On Thu, Jan 06, 2011 at 09:29:02PM +0900, Simon Horman wrote:
> On Thu, Jan 06, 2011 at 02:07:22PM +0200, Michael S. Tsirkin wrote:
> > On Thu, Jan 06, 2011 at 08:30:52PM +0900, Simon Horman wrote:
> > > On Thu, Jan 06, 2011 at 12:27:55PM +0200, Michael S. Tsirkin wrote:
> > > > On Thu, Jan 06, 2011 at 06:33:12PM +0900, Simon Horman wrote:
> > > > > Hi,
> > > > >
> > > > > Back in October I reported that I noticed a problem whereby flow control
> > > > > breaks down when openvswitch is configured to mirror a port[1].
> > > >
> > > > Apropos the UDP flow control. See this
> > > > http://www.spinics.net/lists/netdev/msg150806.html
> > > > for some problems it introduces.
> > > > Unfortunately UDP does not have built-in flow control.
> > > > At some level it's just conceptually broken:
> > > > it's not present in physical networks so why should
> > > > we try and emulate it in a virtual network?
> > > >
> > > >
> > > > Specifically, when you do:
> > > > # netperf -c -4 -t UDP_STREAM -H 172.17.60.218 -l 30 -- -m 1472
> > > > You are asking: what happens if I push data faster than it can be received?
> > > > But why is this an interesting question?
> > > > Ask 'what is the maximum rate at which I can send data with %X packet
> > > > loss' or 'what is the packet loss at rate Y Gb/s'. netperf has
> > > > -b and -w flags for this. It needs to be configured
> > > > with --enable-intervals=yes for them to work.
> > > >
> > > > If you pose the questions this way the problem of pacing
> > > > the execution just goes away.
> > >
> > > I am aware that UDP inherently lacks flow control.
> >
> > Everyone's is aware of that, but this is always followed by a 'however'
> > :).
> >
> > > The aspect of flow control that I am interested in is situations where the
> > > guest can create large amounts of work for the host. However, it seems that
> > > in the case of virtio with vhostnet that the CPU utilisation seems to be
> > > almost entirely attributable to the vhost and qemu-system processes. And
> > > in the case of virtio without vhost net the CPU is used by the qemu-system
> > > process. In both case I assume that I could use a cgroup or something
> > > similar to limit the guests.
> >
> > cgroups, yes. the vhost process inherits the cgroups
> > from the qemu process so you can limit them all.
> >
> > If you are after limiting the max troughput of the guest
> > you can do this with cgroups as well.
>
> Do you mean a CPU cgroup or something else?
net classifier cgroup
> > > Assuming all of that is true then from a resource control problem point of
> > > view, which is mostly what I am concerned about, the problem goes away.
> > > However, I still think that it would be nice to resolve the situation I
> > > described.
> >
> > We need to articulate what's wrong here, otherwise we won't
> > be able to resolve the situation. We are sending UDP packets
> > as fast as we can and some receivers can't cope. Is this the problem?
> > We have made attempts to add a pseudo flow control in the past
> > in an attempt to make UDP on the same host work better.
> > Maybe they help some but they also sure introduce problems.
>
> In the case where port mirroring is not active, which is the
> usual case, to some extent there is flow control in place due to
> (as Eric Dumazet pointed out) the socket buffer.
>
> When port mirroring is activated the flow control operates based
> only on one port - which can't be controlled by the administrator
> in an obvious way.
>
> I think that it would be more intuitive if flow control was
> based on sending a packet to all ports rather than just one.
>
> Though now I think about it some more, perhaps this isn't the best either.
> For instance the case where data was being sent to dummy0 and suddenly
> adding a mirror on eth1 slowed everything down.
>
> So perhaps there needs to be another knob to tune when setting
> up port-mirroring. Or perhaps the current situation isn't so bad.
To understand whether it's bad, you'd need to measure it.
The netperf manual says:
5.2.4 UDP_STREAM
A UDP_STREAM test is similar to a TCP_STREAM test except UDP is used as
the transport rather than TCP.
A UDP_STREAM test has no end-to-end flow control - UDP provides none
and neither does netperf. However, if you wish, you can configure netperf with
--enable-intervals=yes to enable the global command-line -b and -w options to
pace bursts of traffic onto the network.
This has a number of implications.
...
and one of the implications is that the max throughput
might not be reached when you try to send as much data as possible.
It might be confusing that this is what netperf does by default with UDP_STREAM:
if the endpoint is much faster than the network the issue might not appear.
--
MST
^ permalink raw reply
* Re: Flow Control and Port Mirroring Revisited
From: Eric Dumazet @ 2011-01-06 13:28 UTC (permalink / raw)
To: Simon Horman
Cc: Rusty Russell, virtualization, Jesse Gross, dev, virtualization,
netdev, kvm, Michael S. Tsirkin
In-Reply-To: <20110106124439.GA17004@verge.net.au>
Le jeudi 06 janvier 2011 à 21:44 +0900, Simon Horman a écrit :
> Hi Eric !
>
> Thanks for the advice. I had thought about the socket buffer but at some
> point it slipped my mind.
>
> In any case the following patch seems to implement the change that I had in
> mind. However my discussions Michael Tsirkin elsewhere in this thread are
> beginning to make me think that think that perhaps this change isn't the
> best solution.
>
> diff --git a/datapath/actions.c b/datapath/actions.c
> index 5e16143..505f13f 100644
> --- a/datapath/actions.c
> +++ b/datapath/actions.c
> @@ -384,7 +384,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
>
> for (a = actions, rem = actions_len; rem > 0; a = nla_next(a, &rem)) {
> if (prev_port != -1) {
> - do_output(dp, skb_clone(skb, GFP_ATOMIC), prev_port);
> + struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
> + if (nskb) {
> + if (skb->sk)
> + skb_set_owner_w(nskb, skb->sk);
> + do_output(dp, nskb, prev_port);
> + }
> prev_port = -1;
> }
>
> I got a rather nasty panic without the if (skb->sk),
> I guess some skbs don't have a socket.
Indeed, some packets are not linked to a socket.
(ARP packets for example)
Sorry, I should have mentioned it :)
^ permalink raw reply
* Re: genetlink misinterprets NEW as GET
From: Pablo Neira Ayuso @ 2011-01-06 13:48 UTC (permalink / raw)
To: Jan Engelhardt
Cc: Netfilter Developer Mailing List,
Linux Networking Developer Mailing List
In-Reply-To: <alpine.LNX.2.01.1101040304500.29858@obet.zrqbmnf.qr>
On 04/01/11 03:14, Jan Engelhardt wrote:
> Hey there,
>
>
> I can't really say whether it's genetlink or netlink to blame,
> but I noticed that a request with
>
> nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL
>
> to a genl-registered component can return -EOPNOTSUPP because it does
> not have a dumpit function defined in struct genl_ops. Make sense?
> Not at first sight at least.
> include/linux/netlink.h has this nice anecdote:
>
> /* Modifiers to GET request */
> #define NLM_F_ROOT 0x100
> #define NLM_F_MATCH 0x200
> #define NLM_F_ATOMIC 0x400
> #define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
>
> /* Modifiers to NEW request */
> #define NLM_F_REPLACE 0x100
> #define NLM_F_EXCL 0x200
> #define NLM_F_CREATE 0x400
> #define NLM_F_APPEND 0x800
>
> Except there is nothing that declares a particular Netlink message
> as "GET" or "NEW". Subsequently, genetlink chokes:
>
> if (nlh->nlmsg_flags & NLM_F_DUMP)
> if (ops->dumpit == NULL)
> return -EOPNOTSUPP;
>
> Because NLM_F_CREATE | NLM_F_EXCL == NLM_F_DUMP.
> That, of course, is absolutely bogus.
Hm, NLM_F_CREATE | NLM_F_EXCL is not equal to NLM_F_DUMP.
You must be hitting -EOPNOTSUPP elsewhere.
> [N.B.: I am also wondering whether
> (nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP
> may have been desired, because NLM_F_DUMP is composed of two bits.]
Someone may include NLM_F_ATOMIC to a dump operation, in that case the
checking that you propose is not valid.
^ permalink raw reply
* Re: genetlink misinterprets NEW as GET
From: Jan Engelhardt @ 2011-01-06 14:25 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: Netfilter Developer Mailing List,
Linux Networking Developer Mailing List
In-Reply-To: <4D25C82F.4010306@netfilter.org>
On Thursday 2011-01-06 14:48, Pablo Neira Ayuso wrote:
>>
>> /* Modifiers to GET request */
>> #define NLM_F_ROOT 0x100
>> #define NLM_F_MATCH 0x200
>> #define NLM_F_ATOMIC 0x400
>> #define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
>>
>> /* Modifiers to NEW request */
>> #define NLM_F_REPLACE 0x100
>> #define NLM_F_EXCL 0x200
>> #define NLM_F_CREATE 0x400
>> #define NLM_F_APPEND 0x800
>>
>> Except there is nothing that declares a particular Netlink message
>> as "GET" or "NEW". Subsequently, genetlink chokes:
>>
>> if (nlh->nlmsg_flags & NLM_F_DUMP)
>> if (ops->dumpit == NULL)
>> return -EOPNOTSUPP;
>>
>> Because NLM_F_CREATE | NLM_F_EXCL == NLM_F_DUMP.
>> That, of course, is absolutely bogus.
>
>Hm, NLM_F_CREATE | NLM_F_EXCL is not equal to NLM_F_DUMP.
>
>You must be hitting -EOPNOTSUPP elsewhere.
No, I am hitting EOPNOTSUPP here; right it's not equal, sorry.
But nlmsg_flags is tested for NLM_F_MATCH (0x200), which is provided by
NLM_F_EXCL. ipset does use NLM_F_EXCL and thus ran into this.
^ permalink raw reply
* Re: [PATCH net-next] bnx2x: Add Nic partitioning mode (57712 devices)
From: Eilon Greenstein @ 2011-01-06 14:40 UTC (permalink / raw)
To: Matt Domsch
Cc: Dimitris Michailidis, Dmitry Kravkov, davem@davemloft.net,
netdev@vger.kernel.org, narendra_k@dell.com,
jordan_hargrave@dell.com
In-Reply-To: <4D0FB233.9050501@chelsio.com>
On Mon, 2010-12-20 at 11:44 -0800, Dimitris Michailidis wrote:
> Matt Domsch wrote:
> >> You can have several interfaces with same device link and different dev_id.
> >> While the current driver doesn't do it you could also have several
> >> interfaces with different device links but same dev_id (NPAR situation,
> >> notice again that dev_ids are not per PCI function), or interfaces with
> >> different device and dev_id, or even interfaces with same device and dev_id.
> >
> > What is the scope of dev_id then? It's not per PCI device like I
> > thought.
>
> I don't think it could be that way because for these cards you can't
> statically tell which ports are controlled by a PCI function. So knowing
> that an interface is say port 0 of a function would help little.
>
> > It sounds like it's per card, but how can I know the card
> > boundary?
>
> Yes, it's per card and covers the PFs and VFs of the card.
>
> > If I have 2 cards driven by cxgb4 in the system, each with say 4
> > ports. I could see a minimum of 8 PCI devices (fine), but the dev_id
> > values would be? 0,1,2,3; 0,1,2,3 ?
>
> Correct.
>
> > How can I tell that these are
> > two different cards, with two different sets of dev_id values, rather
> > than one card with 4 ports, 8 (NPAR or SR-IOV) interfaces, with each 2
> > interfaces mapping to the same port?
>
> Doesn't the information in /sys/devices distinguish them? For example,
> something like
>
> /sys/devices/pci0000:00/0000:00:07.0/0000:04:00.0/net/eth2/dev_id == 0
> /sys/devices/pci0000:00/0000:00:07.0/0000:04:00.1/net/eth3/dev_id == 0
> /sys/devices/pci0000:00/0000:00:07.0/0000:04:01.0/net/eth5/dev_id == 0
> /sys/devices/pci0000:00/0000:00:1c.0/0000:05:00.0/net/eth4/dev_id == 0
>
> tells me there are two cards, one has eth4 on port 0, the other has eth2,
> eth3, and eth5 on its port 0 with eth5 being on a VF.
>
> > dev_id is not system-wide unique. It's not even slot unique best as I
> > can tell. If I had a PCI slot extender, with 2 PCI slots, and I put
> > two of the above cards in, I would see 0,1,2,3; 0,1,2,3. To be fair,
> > my naming scheme doesn't really account for such an extender, though
> > currently it would go pci<slot>#<12345678>.
>
> Can you give an example of what /sys/devices looks like in the case you're
> considering?
Matt,
Happy New Year!
Does the dev_id approach suits your needs? Do you want to proceed in
that direction?
Thanks,
Eilon
^ permalink raw reply
* Re: genetlink misinterprets NEW as GET
From: Pablo Neira Ayuso @ 2011-01-06 14:55 UTC (permalink / raw)
To: Jan Engelhardt
Cc: Netfilter Developer Mailing List,
Linux Networking Developer Mailing List
In-Reply-To: <alpine.LNX.2.01.1101061508550.13211@obet.zrqbmnf.qr>
On 06/01/11 15:25, Jan Engelhardt wrote:
> On Thursday 2011-01-06 14:48, Pablo Neira Ayuso wrote:
>>>
>>> /* Modifiers to GET request */
>>> #define NLM_F_ROOT 0x100
>>> #define NLM_F_MATCH 0x200
>>> #define NLM_F_ATOMIC 0x400
>>> #define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
>>>
>>> /* Modifiers to NEW request */
>>> #define NLM_F_REPLACE 0x100
>>> #define NLM_F_EXCL 0x200
>>> #define NLM_F_CREATE 0x400
>>> #define NLM_F_APPEND 0x800
>>>
>>> Except there is nothing that declares a particular Netlink message
>>> as "GET" or "NEW". Subsequently, genetlink chokes:
>>>
>>> if (nlh->nlmsg_flags & NLM_F_DUMP)
>>> if (ops->dumpit == NULL)
>>> return -EOPNOTSUPP;
>>>
>>> Because NLM_F_CREATE | NLM_F_EXCL == NLM_F_DUMP.
>>> That, of course, is absolutely bogus.
>>
>> Hm, NLM_F_CREATE | NLM_F_EXCL is not equal to NLM_F_DUMP.
>>
>> You must be hitting -EOPNOTSUPP elsewhere.
>
> No, I am hitting EOPNOTSUPP here; right it's not equal, sorry.
> But nlmsg_flags is tested for NLM_F_MATCH (0x200), which is provided by
> NLM_F_EXCL. ipset does use NLM_F_EXCL and thus ran into this.
i getting confused, so ipset is also setting NLM_F_REPLACE to match the
NLM_F_DUMP bitmask?
^ permalink raw reply
* Re: [PATCH]netdev: add driver for enc424j600 ethernet chip on SPI bus
From: Michał Mirosław @ 2011-01-06 14:56 UTC (permalink / raw)
To: Balaji Venkatachalam
Cc: netdev, mohan, blue.cube, lanconelli.claudio, Sriram Subramanian
In-Reply-To: <AANLkTi=cJuDX7RZCFD4ovgGQi7gBXchoMLx8BeuiGz8G@mail.gmail.com>
A quick look follows.
2011/1/6 Balaji Venkatachalam <balaji.v@thotakaa.com>:
> From: Balaji Venkatachalam <balaji.v@thotakaa.com>
>
> These patches add support for Microchip enc424j600 ethernet chip
> controlled via SPI.
>
> I tested it on my custom board with ARM9 (Freescale i.MX233) with
> Kernel 2.6.31.14.
> Any comments are welcome.
>
> Signed-off-by: Balaji Venkatachalam <balaji.v@thotakaa.com>
> ---
>
> diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600.c
> b/drivers/net/enc424j600.c
> --- a/drivers/net/enc424j600.c 1970-01-01 05:30:00.000000000 +0530
> +++ b/drivers/net/enc424j600.c 2011-01-06 09:39:17.000000000 +0530
> @@ -0,0 +1,1694 @@
[...]
> +static int enc424j600_spi_trans(struct enc424j600_net *priv, int len)
> +{
> + /*modified to suit half duplexed spi */
> + struct spi_transfer tt = {
> + .tx_buf = priv->spi_tx_buf,
> + .len = SPI_OPLEN,
> + };
> + struct spi_transfer tr = {
> + .rx_buf = priv->spi_rx_buf,
> + .len = len,
> + };
> + struct spi_message m;
> + int ret;
> +
> + spi_message_init(&m);
> +
> + spi_message_add_tail(&tt, &m);
> + spi_message_add_tail(&tr, &m);
> +
> + ret = spi_sync(priv->spi, &m);
> +
> + if (ret == 0)
> + memcpy(priv->spi_rx_buf, tr.rx_buf, len);
> +
> + if (ret)
> + dev_err(&priv->spi->dev,
> + "spi transfer failed: ret = %d\n", ret);
> + return ret;
> +}
> +
> +/*
> + * Read data from chip SRAM.
> + * window = 0 for Receive Buffer
> + * = 1 for User Defined area
> + * = 2 for General Purpose area
> + */
This could use an enum. And the comment does not match the code, BTW.
> +static int enc424j600_read_sram(struct enc424j600_net *priv,
> + u8 *dst, int len, u16 srcaddr, int window)
> +{
> + int ret;
> +
> + if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
> + return -EINVAL;
> +
> + /* First set the write pointer as per selected window */
> + if (window == 1)
> + priv->spi_tx_buf[0] = WRXRDPT;
> + else if (window == 2)
> + priv->spi_tx_buf[0] = WUDARDPT;
> + else if (window == 3)
> + priv->spi_tx_buf[0] = WGPRDPT;
> +
> + priv->spi_tx_buf[1] = srcaddr & 0xFF;
> + priv->spi_tx_buf[2] = srcaddr >> 8;
> + ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
> +
> + /* Transfer the data */
> + if (window == 1)
> + priv->spi_tx_buf[0] = RRXDATA;
> + else if (window == 2)
> + priv->spi_tx_buf[0] = RUDADATA;
> + else if (window == 3)
> + priv->spi_tx_buf[0] = RGPDATA;
> +
> + ret = enc424j600_spi_trans(priv, len + 1); /*READ*/
> +
> + /* Copy the data to the tx buffer */
> + memcpy(dst, &priv->spi_rx_buf[0], len);
You could avoid the copy if enc424j600_spi_trans() would get the
buffer as its arguments.
> +
> + return ret;
> +}
> +
> +/*
> + * Write data to chip SRAM.
> + * window = 1 for RX
> + * window = 2 for User Data
> + * window = 3 for GP
> + */
Enum.
> +static int enc424j600_write_sram(struct enc424j600_net *priv,
> + const u8 *src, int len, u16 dstaddr,
> + int window)
> +{
> + int ret;
> +
> + if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
> + return -EINVAL;
> +
> + /* First set the general purpose write pointer */
> + if (window == 1)
> + priv->spi_tx_buf[0] = WRXWRPT;
> + else if (window == 2)
> + priv->spi_tx_buf[0] = WUDAWRPT;
> + else if (window == 3)
> + priv->spi_tx_buf[0] = WGPWRPT;
> +
> + priv->spi_tx_buf[1] = dstaddr & 0xFF;
> + priv->spi_tx_buf[2] = dstaddr >> 8;
> + ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
> +
> + /* Copy the data to the tx buffer */
> + memcpy(&priv->spi_tx_buf[1], src, len);
> +
> + /* Transfer the data */
> + if (window == 1)
> + priv->spi_tx_buf[0] = WRXDATA;
> + else if (window == 2)
> + priv->spi_tx_buf[0] = WUDADATA;
> + else if (window == 3)
> + priv->spi_tx_buf[0] = WGPDATA;
> +
> + ret = spi_write(priv->spi, priv->spi_tx_buf, len + 1);
> +
> + return ret;
> +}
> +
[...]
> +/*
> + * Write a 16bit special function register.
> + * The @sfr parameters takes address of the low byte of the register.
> + * Takes care of the endiannes & buffers.
> + * Uses banked write instruction.
> + */
> +
> +static int enc424j600_write_16b_sfr(struct enc424j600_net *priv,
> + u8 sfr, u16 data)
> +{
> + int ret;
> + enc424j600_set_bank(priv, sfr);
> +
> + priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
> + priv->spi_tx_buf[1] = data & 0xFF;
> + priv->spi_tx_buf[2] = data >> 8;
> + ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
> + if (ret && netif_msg_drv(priv))
> + printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n",
> + __func__, ret);
netif_dbg()
It's in 2.6.35 or later, though.
> +
> + return ret;
> +}
> +
> +/*
> + * Read a 16bit special function register.
> + * The @sfr parameters takes address of the low byte of the register.
> + * Takes care of the endiannes & buffers.
> + * Uses banked read instruction.
> + */
> +static int enc424j600_read_16b_sfr(struct enc424j600_net *priv,
> + u8 sfr, u16 *data)
> +{
> + int ret;
> + enc424j600_set_bank(priv, sfr);
> +
> + priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
> + priv->spi_tx_buf[1] = 0;
> + priv->spi_tx_buf[2] = 0;
> + priv->spi_tx_buf[3] = 0;
> + ret = enc424j600_spi_trans(priv, 3); /*READ*/
> +
> + *data = priv->spi_rx_buf[0] | priv->spi_rx_buf[1] << (u16) 8;
> +
> + return ret;
> +}
> +
> +/*
> + * Reset the enc424j600.
> + * TODO: What if we get stuck on non-working spi with the initial
> + * test access to EUDAST ?
> + */
> +static void enc424j600_soft_reset(struct enc424j600_net *priv)
> +{
> + u8 estath;
> + u16 eudast;
> + if (netif_msg_hw(priv))
> + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
> +
> + do {
> + enc424j600_write_16b_sfr(priv, EUDASTL, EUDAST_TEST_VAL);
> + enc424j600_read_16b_sfr(priv, EUDASTL, &eudast);
> + } while (eudast != EUDAST_TEST_VAL);
> +
> + do {
> + enc424j600_read_8b_sfr(priv, ESTATH, &estath);
> + } while (!estath & CLKRDY);
Those loops need a timeout or you risk lockup with broken hardware.
> +
> + priv->spi_tx_buf[0] = SETETHRST;
> + enc424j600_spi_trans(priv, 1);
> +
> + udelay(50);
> +
> + enc424j600_read_16b_sfr(priv, EUDASTL, &eudast);
> + if (netif_msg_hw(priv) && eudast != 0)
> + printk(KERN_DEBUG DRV_NAME
> + ": %s() EUDASTL is not zero!\n", __func__);
> +
> + udelay(500);
Long enough for usleep() if there are no locks held.
> +}
> +
> +static unsigned long msec20_to_jiffies;
== HZ/50. It's constant AFAIR.
> +
> +/*
> + * Wait for bits in register to become equal to @readyMask, but at most 20ms.
> + */
> +static int poll_ready(struct enc424j600_net *priv,
> + u8 reg, u8 mask, u8 readyMask)
> +{
> + unsigned long timeout = jiffies + msec20_to_jiffies;
> + u8 value;
> + /* 20 msec timeout read */
> + enc424j600_read_8b_sfr(priv, reg, &value);
> + while ((value & mask) != readyMask) {
> + if (time_after(jiffies, timeout)) {
> + if (netif_msg_drv(priv))
> + dev_dbg(&priv->spi->dev,
> + "reg %02x ready timeout!\n", reg);
> + return -ETIMEDOUT;
> + }
> + cpu_relax();
> + enc424j600_read_8b_sfr(priv, reg, &value);
> + }
> +
> + return 0;
> +}
> +
[...]
> +/* Waits for autonegotiation to complete and sets FULDPX bit in macon2. */
> +static void enc424j600_wait_for_autoneg(struct enc424j600_net *priv)
> +{
> + u16 phstat1;
> + do {
> + enc424j600_phy_read(priv, PHSTAT1, &phstat1);
> + } while (!(phstat1 & ANDONE));
Timeout?
[...]
> +static int
> +enc424j600_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex)
> +{
> + struct enc424j600_net *priv = netdev_priv(ndev);
> + int ret = 0;
> + if (!priv->hw_enable) {
> + /* link is in low power mode now; duplex setting
> + * will take effect on next enc424j600_hw_init().
> + */
> + if (speed == SPEED_10 || speed == SPEED_100) {
> + priv->autoneg = (autoneg == AUTONEG_ENABLE);
> + priv->full_duplex = (duplex == DUPLEX_FULL);
> + priv->speed100 = (speed == SPEED_100);
> + } else {
> + if (netif_msg_link(priv))
> + dev_warn(&ndev->dev,
> + "unsupported link setting\n");
> + ret = -EOPNOTSUPP;
EINVAL
> + }
> + } else {
> + if (netif_msg_link(priv))
> + dev_warn(&ndev->dev, "Warning: hw must be disabled "
> + "to set link mode\n");
> + ret = -EBUSY;
> + }
> + return ret;
> +}
[...]
> +
> +/*
> + * Hardware receive function.
> + * Read the buffer memory, update the FIFO pointer to free the buffer,
> + * check the status vector and decrement the packet counter.
> + */
> +static void enc424j600_hw_rx(struct net_device *ndev)
> +{
> + struct enc424j600_net *priv = netdev_priv(ndev);
> + struct sk_buff *skb = NULL;
> + u16 erxrdpt, next_packet, rxstat;
> + u8 pkcnt;
> + u16 head, tail;
> + u8 rsv[RSV_SIZE];
> + u16 newrxtail;
> + int len;
> +
> + if (netif_msg_rx_status(priv))
> + printk(KERN_DEBUG DRV_NAME ": RX pk_addr:0x%04x\n",
> + priv->next_pk_ptr);
> + if (unlikely(priv->next_pk_ptr > RXEND_INIT)) {
> + if (netif_msg_rx_err(priv))
> + dev_err(&ndev->dev,
> + "%s() Invalid packet address!! 0x%04x\n",
> + __func__, priv->next_pk_ptr);
> + mutex_lock(&priv->lock);
> + enc424j600_clear_bits(priv, ECON1L, RXEN);
> + enc424j600_set_bits(priv, ECON2L, RXRST);
> + enc424j600_clear_bits(priv, ECON2L, RXRST);
> + nolock_rxfifo_init(priv, RXSTART, RXEND_INIT);
> + enc424j600_clear_bits(priv, EIRL, RXABTIF);
> + enc424j600_set_bits(priv, ECON1L, RXEN);
> + mutex_unlock(&priv->lock);
> + ndev->stats.rx_errors++;
> + return;
> + }
> +
> + /* Read next packet pointer and rx status vector */
> + enc424j600_read_sram(priv, rsv, sizeof(rsv), priv->next_pk_ptr, 1);
> +
> + next_packet = rsv[1];
> + next_packet <<= 8;
> + next_packet |= rsv[0];
> +
> + len = rsv[3];
> + len <<= 8;
> + len |= rsv[2];
> +
> + rxstat = rsv[5];
> + rxstat <<= 8;
> + rxstat |= rsv[4];
> +
> + if (netif_msg_rx_status(priv))
> + enc424j600_dump_rsv(priv, __func__, next_packet, len, rxstat);
> +
> + if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
> + if (netif_msg_rx_err(priv))
> + dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat);
> + ndev->stats.rx_errors++;
> + if (RSV_GETBIT(rxstat, RSV_CRCERROR))
> + ndev->stats.rx_crc_errors++;
> + if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
> + ndev->stats.rx_frame_errors++;
> + if (len > MAX_FRAMELEN)
> + ndev->stats.rx_over_errors++;
> + } else {
> + skb = dev_alloc_skb(len + NET_IP_ALIGN);
> + if (!skb) {
> + if (netif_msg_rx_err(priv))
> + dev_err(&ndev->dev,
> + "out of memory for Rx'd frame\n");
> + ndev->stats.rx_dropped++;
> + } else {
> + skb->dev = ndev;
> + skb_reserve(skb, NET_IP_ALIGN);
> +
> + /* copy the packet from the receive buffer */
> + enc424j600_read_sram(priv, skb_put(skb, len), len,
> + rx_packet_start(priv->next_pk_ptr),
> + 1);
> +
> + if (netif_msg_pktdata(priv))
> + dump_packet(__func__, skb->len, skb->data);
> + skb->protocol = eth_type_trans(skb, ndev);
> + /* update statistics */
> + ndev->stats.rx_packets++;
> + ndev->stats.rx_bytes += len;
> + netif_rx_ni(skb);
I assume that this is not an IRQ context because of (slow) SPI
accesses. So use netif_rx() or better netif_receive_skb().
> + }
> + }
> + newrxtail = next_packet - 2;
> + if (next_packet == RXSTART)
> + newrxtail = SRAMSIZE - 2;
> +
> + enc424j600_write_16b_sfr(priv, ERXTAILL, newrxtail);
> + /*
> + * Move the RX read pointer to the start of the next
> + * received packet.
> + * This frees the memory we just read out
> + */
> + erxrdpt = erxrdpt_workaround(next_packet, RXSTART, RXEND_INIT);
> + if (netif_msg_hw(priv))
> + printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT:0x%04x\n", __func__,
> + erxrdpt);
> +
> + mutex_lock(&priv->lock);
Why do you need the lock here, but not on previous accesses to the hardware?
> + enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt);
> +
> +#ifdef CONFIG_ENC28J60_WRITEVERIFY
> + if (netif_msg_drv(priv)) {
> + u16 reg;
> +
> + enc424j600_read_16b_sfr(priv, ERXRDPTL, ®);
> + if (reg != erxrdpt)
> + printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT verify "
> + "error (0x%04x - 0x%04x)\n", __func__, reg,
> + erxrdpt);
> + }
> +#endif
> +
> + priv->next_pk_ptr = next_packet;
> + enc424j600_read_8b_sfr(priv, ESTATL, &pkcnt);
> + enc424j600_read_16b_sfr(priv, ERXHEADL, &head);
> + enc424j600_read_16b_sfr(priv, ERXTAILL, &tail);
> + /* we are done with this packet, decrement the packet counter */
> + enc424j600_set_bits(priv, ECON1H, PKTDEC);
> +
> + mutex_unlock(&priv->lock);
> +}
[...]
> +static irqreturn_t enc424j600_irq(int irq, void *dev_id)
> +{
> + struct enc424j600_net *priv = dev_id;
> + /*
> + * Can't do anything in interrupt context because we need to
> + * block (spi_sync() is blocking) so fire of the interrupt
> + * handling workqueue.
> + * Remember that we access enc424j600 registers through SPI bus
> + * via spi_sync() call.
> + */
> + schedule_work(&priv->irq_work);
> +
> + return IRQ_HANDLED;
> +}
You can use threaded interrupts here and get rid of irq_work. And
definitely disable source interrupt before returning. And after you do
that, you have implemented most of what's necessary for NAPI rx.
[...]
> +static void enc424j600_setrx_work_handler(struct work_struct *work)
> +{
> + u16 macon1;
> + struct enc424j600_net *priv =
> + container_of(work, struct enc424j600_net, setrx_work);
> +
> + if (priv->rxfilter == RXFILTER_PROMISC) {
> + if (netif_msg_drv(priv))
> + printk(KERN_DEBUG DRV_NAME ": promiscuous mode\n");
> + enc424j600_read_16b_sfr(priv, MACON1L, &macon1);
> + macon1 = macon1 | PASSALL;
> + enc424j600_write_16b_sfr(priv, MACON1L, macon1);
> + locked_regb_write(priv, ERXFCONL, UCEN | MCEN | NOTMEEN);
> + } else if (priv->rxfilter == RXFILTER_MULTI) {
> + if (netif_msg_drv(priv))
> + printk(KERN_DEBUG DRV_NAME ": multicast mode\n");
> + locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN | MCEN);
> +
> + } else {
> + if (netif_msg_drv(priv))
> + printk(KERN_DEBUG DRV_NAME ": normal mode\n");
> + locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN);
> +
> + }
> +}
> +
> +static void enc424j600_restart_work_handler(struct work_struct *work)
> +{
> + struct enc424j600_net *priv =
> + container_of(work, struct enc424j600_net, restart_work);
> + struct net_device *ndev = priv->netdev;
> + int ret;
> +
> + rtnl_lock();
> + if (netif_running(ndev)) {
> + enc424j600_net_close(ndev);
> + ret = enc424j600_net_open(ndev);
> + if (unlikely(ret)) {
> + dev_info(&ndev->dev, " could not restart %d\n", ret);
> + dev_close(ndev);
> + }
> + }
> + rtnl_unlock();
> +}
Are these work handlers guaranteed to not be scheduled concurrently?
[...]
> + /* If requested, allocate DMA buffers */
> + if (enc424j600_enable_dma) {
> + spi->dev.coherent_dma_mask = ~0;
> +
> + /*
> + * Minimum coherent DMA allocation is PAGE_SIZE, so allocate
> + * that much and share it between Tx and Rx DMA buffers.
> + */
> +#if SPI_TRANSFER_BUF_LEN > PAGE_SIZE / 2
> +#error "A problem in DMA buffer allocation"
> +#endif
> + priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
> + PAGE_SIZE,
> + &priv->spi_tx_dma,
> + GFP_DMA);
> +
> + if (priv->spi_tx_buf) {
> + priv->spi_rx_buf = (u8 *) (priv->spi_tx_buf +
> + (PAGE_SIZE / 2));
> + priv->spi_rx_dma = (dma_addr_t) (priv->spi_tx_dma +
> + (PAGE_SIZE / 2));
> + } else {
> + /* Fall back to non-DMA */
> + enc424j600_enable_dma = 0;
> + }
> + }
You don't use DMA addresses anywhere. And don't do anything with this
flag except for freeing the buffers. You should use streaming DMA
calls for skb data to avoid copying anyway and let the arch code care
about bounce buffers if needed.
[...]
MAINTAINTERS entry would be nice also.
Best Regards,
Michał Mirosław
^ permalink raw reply
* Re: [PATCH net-next-2.6 v2 1/2] can: add driver for Softing card
From: Kurt Van Dijck @ 2011-01-06 15:05 UTC (permalink / raw)
To: Wolfgang Grandegger; +Cc: netdev, socketcan-core
In-Reply-To: <4D24DB2C.9040104@grandegger.com>
Wolfgang,
On Wed, Jan 05, 2011 at 09:57:16PM +0100, Wolfgang Grandegger wrote:
>
> here comes my review... First some general remarks. As Mark already
> pointed out, there are still some coding style issues:
Oops, I tried to eliminate those.
>
> - Please use the following style for multi line comments:
shame on me. I should have done them all after Mark pointed me to it.
>
> - Please avoid alignment of expressions and structure members.
I see:
diff --git a/include/linux/can.h b/include/linux/can.h
index d183333..6b1e5a6 100644
--- a/include/linux/can.h
+++ b/include/linux/can.h
@@ -56,18 +56,18 @@ typedef __u32 can_err_mask_t;
*/
struct can_frame {
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
- __u8 can_dlc; /* data length code: 0 .. 8 */
- __u8 data[8] __attribute__((aligned(8)));
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
};
/* particular protocols of the protocol family PF_CAN */
-#define CAN_RAW 1 /* RAW sockets */
-#define CAN_BCM 2 /* Broadcast Manager */
-#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
-#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
-#define CAN_MCNET 5 /* Bosch MCNet */
-#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
-#define CAN_NPROTO 7
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
#define SOL_CAN_BASE 100
@@ -79,7 +79,7 @@ struct can_frame {
*/
struct sockaddr_can {
sa_family_t can_family;
- int can_ifindex;
+ int can_ifindex;
union {
/* transport protocol class address information (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
I applied a search pattern on this, since I seem incapable of finding
alignment problems in my own code :-).
I assume alignment is ok for definitions, but not within functions?
I consulted the Documentation/Coding-style, but I did not find
the exact answer.
>
> More comments inline...
More to process for me too ...
Kurt
^ 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