* Re: netlink: 12 bytes leftover after parsing attributes - triggered by iproute2 libnetlink's rtnl_dump_request()
From: Bruno Prémont @ 2012-04-03 10:17 UTC (permalink / raw)
To: Thomas Graf, libnl; +Cc: Stephen Hemminger, Ben Hutchings, netdev, Greg Rose
In-Reply-To: <20120403100157.GA8611@canuck.infradead.org>
[-- Attachment #1: Type: text/plain, Size: 4642 bytes --]
Thomas,
On Tue, 3 Apr 2012 06:01:57 Thomas Graf <tgraf@infradead.org> wrote:
> Can you send a full bug report to libnl@lists.infradead.org and I'll make sure
> this gets addressed in libnl.
Here it comes (mostly initial mail with some adjustments):
Starting with linux-3.3 when using collectd's netlink plugin to monitor
interface statistics I'm seeing 3 lines of complaint in kernel log per
monitoring loop (10s interval)
[64951.027953] netlink: 12 bytes leftover after parsing attributes.
The same userspace code running on 3.2 does not produce the lines in
kernel log.
Basic source code to reproduce (netlink subset of collectd's netlink plugin):
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <libnetlink.h>
int link_filter (const struct sockaddr_nl *sa, struct nlmsghdr *nmh, void *args) {
return 0; /* would present the data */
}
int main(int argc, char **argv) {
struct rtnl_handle rth;
struct ifinfomsg im;
struct tcmsg tm;
memset(&rth, 0, sizeof(rth));
rtnl_open(&rth, 0);
memset(&im, 0, sizeof(im));
im.ifi_type = AF_UNSPEC;
rtnl_dump_request(&rth, RTM_GETLINK, &im, sizeof(im));
rtnl_dump_filter(&rth, link_filter, NULL, NULL, NULL);
rtnl_close(&rth);
return 0;
}
Compile with
$CC -o test test.c -lnetlink
(here using libnetlink.a from iproute2-2.6.38)
Strace of test code shows the following:
sendmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(2)=[{" \0\0\0\22\0\1\3\272[hO\0\0\0\0", 16}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16}], msg_controllen=0, msg_flags=0}, 0) = 32
recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{..., 16384}], msg_controllen=0, msg_flags=0}, 0) = 2980
recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{..., 16384}], msg_controllen=0, msg_flags=0}, 0) = 20
Note: when omitting the rtnl_dump_filter() call only two lines appear
in kernel log.
Comparing to iproute2 call (ip -s link list) which does not trigger the same
message in kernel log I have:
send(3, "\24\0\0\0\22\0\1\3\225]hO\0\0\0\0\21\0\0\0", 20, 0) = 20
recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{..., 16384}], msg_controllen=0, msg_flags=0}, 0) = 2980
recvmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{..., 16384}], msg_controllen=0, msg_flags=0}, 0) = 20
Looking at kernel history related to net/netlink I've seen the following
commit which introduced the warning (or rather started using kernel's
nla_parse() function in this path - and that function complains):
commit 115c9b81928360d769a76c632bae62d15206a94a
Author: Greg Rose <gregory.v.rose@intel.com>
Date: Tue Feb 21 16:54:48 2012 -0500
rtnetlink: Fix problem with buffer allocation
Implement a new netlink attribute type IFLA_EXT_MASK. The mask
is a 32 bit value that can be used to indicate to the kernel that
certain extended ifinfo values are requested by the user application.
At this time the only mask value defined is RTEXT_FILTER_VF to
indicate that the user wants the ifinfo dump to send information
about the VFs belonging to the interface.
This patch fixes a bug in which certain applications do not have
large enough buffers to accommodate the extra information returned
by the kernel with large numbers of SR-IOV virtual functions.
Those applications will not send the new netlink attribute with
the interface info dump request netlink messages so they will
not get unexpectedly large request buffers returned by the kernel.
Modifies the rtnl_calcit function to traverse the list of net
devices and compute the minimum buffer size that can hold the
info dumps of all matching devices based upon the filter passed
in via the new netlink attribute filter mask. If no filter
mask is sent then the buffer allocation defaults to NLMSG_GOODSIZE.
With this change it is possible to add yet to be defined netlink
attributes to the dump request which should make it fairly extensible
in the future.
A kernel at preceding commit 84338a6c9dbb6ff3de4749864020f8f25d86fc81 (neighbour:
Fixed race condition at tbl->nht) does not show the log message,
starting with that commit the message appears.
A working adjustment to collectd (or above test code) is to call
rtnl_wilddump_request() instead of rtnl_dump_request() in order to
get the information.
rtnl_wilddump_request() is also the function used internally by iproute2's
ip command to fetch the data.
Bruno
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: collectd-netlink-kernwarn-fix.patch --]
[-- Type: text/x-patch, Size: 1650 bytes --]
Starting with linux-3.3-rc5 kernel at each interval netlink plugin triggers
three lines of kernel log:
netlink: 12 bytes leftover after parsing attributes.
Change the libnetlink function used to query link statistics to match
iproute2's behavior and thus not trip on the kernel's new parsing of
optional attributes for RTM_GETLINK.
While at it, also fix libnetlink's complaint to stderr:
!!!Deficit 16, rta_len=996
caused by incorrect calculation of msg_len.
Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
This should get applied to 5.0 and 4.10 series!
The two hunks may get applied separately.
--- collectd-a/src/netlink.c 2012-03-21 19:20:15.304227640 +0100
+++ collectd-b/src/netlink.c 2012-03-21 19:21:20.454323202 +0100
@@ -223,7 +223,7 @@ static int link_filter (const struct soc
msg = NLMSG_DATA (nmh);
- msg_len = nmh->nlmsg_len - sizeof (struct ifinfomsg);
+ msg_len = nmh->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg));
if (msg_len < 0)
{
ERROR ("netlink plugin: link_filter: msg_len = %i < 0;", msg_len);
@@ -554,17 +554,13 @@ static int ir_init (void)
static int ir_read (void)
{
- struct ifinfomsg im;
struct tcmsg tm;
int ifindex;
static const int type_id[] = { RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER };
static const char *type_name[] = { "qdisc", "class", "filter" };
- memset (&im, '\0', sizeof (im));
- im.ifi_type = AF_UNSPEC;
-
- if (rtnl_dump_request (&rth, RTM_GETLINK, &im, sizeof (im)) < 0)
+ if (rtnl_wilddump_request (&rth, AF_UNSPEC, RTM_GETLINK) < 0)
{
ERROR ("netlink plugin: ir_read: rtnl_dump_request failed.");
return (-1);
^ permalink raw reply
* Re: [PATCH 3/3] netfilter: xt_CT: remove a compile warning
From: Tetsuo Handa @ 2012-04-03 10:27 UTC (permalink / raw)
To: pablo; +Cc: netdev
In-Reply-To: <1333448001-2507-4-git-send-email-pablo@netfilter.org>
pablo@netfilter.org:
> From: Pablo Neira Ayuso <pablo@netfilter.org>
>
> If CONFIG_NF_CONNTRACK_TIMEOUT=n we have following warning :
>
> CC [M] net/netfilter/xt_CT.o
> net/netfilter/xt_CT.c: In function ‘xt_ct_tg_check_v1’:
> net/netfilter/xt_CT.c:284: warning: label ‘err4’ defined but not used
>
> Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> ---
> net/netfilter/xt_CT.c | 2 ++
> 1 files changed, 2 insertions(+), 0 deletions(-)
>
> diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
> index 0c8e438..138b75e 100644
> --- a/net/netfilter/xt_CT.c
> +++ b/net/netfilter/xt_CT.c
> @@ -281,8 +281,10 @@ out:
> info->ct = ct;
> return 0;
>
> +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
> err4:
> rcu_read_unlock();
> +#endif
> err3:
> nf_conntrack_free(ct);
> err2:
> --
> 1.7.2.5
Looking at that function:
216 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
217 if (info->timeout) {
218 typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
219 struct ctnl_timeout *timeout;
220 struct nf_conn_timeout *timeout_ext;
221
222 rcu_read_lock();
223 timeout_find_get =
224 rcu_dereference(nf_ct_timeout_find_get_hook);
225
226 if (timeout_find_get) {
227 const struct ipt_entry *e = par->entryinfo;
228 struct nf_conntrack_l4proto *l4proto;
229
230 if (e->ip.invflags & IPT_INV_PROTO) {
231 ret = -EINVAL;
232 pr_info("You cannot use inversion on "
233 "L4 protocol\n");
234 goto err4;
235 }
236 timeout = timeout_find_get(info->timeout);
237 if (timeout == NULL) {
238 ret = -ENOENT;
239 pr_info("No such timeout policy \"%s\"\n",
240 info->timeout);
241 goto err4;
242 }
243 if (timeout->l3num != par->family) {
244 ret = -EINVAL;
245 pr_info("Timeout policy `%s' can only be "
246 "used by L3 protocol number %d\n",
247 info->timeout, timeout->l3num);
248 goto err4;
249 }
250 /* Make sure the timeout policy matches any existing
251 * protocol tracker, otherwise default to generic.
252 */
253 l4proto = __nf_ct_l4proto_find(par->family,
254 e->ip.proto);
255 if (timeout->l4proto->l4proto != l4proto->l4proto) {
256 ret = -EINVAL;
257 pr_info("Timeout policy `%s' can only be "
258 "used by L4 protocol number %d\n",
259 info->timeout,
260 timeout->l4proto->l4proto);
261 goto err4;
262 }
263 timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
264 GFP_KERNEL);
We are under rcu_read_lock() here.
265 if (timeout_ext == NULL) {
266 ret = -ENOMEM;
267 goto err4;
268 }
269 } else {
270 ret = -ENOENT;
271 pr_info("Timeout policy base is empty\n");
272 goto err4;
273 }
274 rcu_read_unlock();
275 }
276 #endif
^ permalink raw reply
* Re: [PATCH 3/3] netfilter: xt_CT: remove a compile warning
From: Pablo Neira Ayuso @ 2012-04-03 10:31 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: netdev
In-Reply-To: <201204031927.AIJ09351.FQFMJOOVFSOtHL@I-love.SAKURA.ne.jp>
On Tue, Apr 03, 2012 at 07:27:50PM +0900, Tetsuo Handa wrote:
> pablo@netfilter.org:
> > From: Pablo Neira Ayuso <pablo@netfilter.org>
> >
> > If CONFIG_NF_CONNTRACK_TIMEOUT=n we have following warning :
> >
> > CC [M] net/netfilter/xt_CT.o
> > net/netfilter/xt_CT.c: In function ‘xt_ct_tg_check_v1’:
> > net/netfilter/xt_CT.c:284: warning: label ‘err4’ defined but not used
> >
> > Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
> > Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> > ---
> > net/netfilter/xt_CT.c | 2 ++
> > 1 files changed, 2 insertions(+), 0 deletions(-)
> >
> > diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
> > index 0c8e438..138b75e 100644
> > --- a/net/netfilter/xt_CT.c
> > +++ b/net/netfilter/xt_CT.c
> > @@ -281,8 +281,10 @@ out:
> > info->ct = ct;
> > return 0;
> >
> > +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
> > err4:
> > rcu_read_unlock();
> > +#endif
> > err3:
> > nf_conntrack_free(ct);
> > err2:
> > --
> > 1.7.2.5
>
> Looking at that function:
>
> 216 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
> 217 if (info->timeout) {
> 218 typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
> 219 struct ctnl_timeout *timeout;
> 220 struct nf_conn_timeout *timeout_ext;
> 221
> 222 rcu_read_lock();
> 223 timeout_find_get =
> 224 rcu_dereference(nf_ct_timeout_find_get_hook);
> 225
> 226 if (timeout_find_get) {
> 227 const struct ipt_entry *e = par->entryinfo;
> 228 struct nf_conntrack_l4proto *l4proto;
> 229
> 230 if (e->ip.invflags & IPT_INV_PROTO) {
> 231 ret = -EINVAL;
> 232 pr_info("You cannot use inversion on "
> 233 "L4 protocol\n");
> 234 goto err4;
> 235 }
> 236 timeout = timeout_find_get(info->timeout);
> 237 if (timeout == NULL) {
> 238 ret = -ENOENT;
> 239 pr_info("No such timeout policy \"%s\"\n",
> 240 info->timeout);
> 241 goto err4;
> 242 }
> 243 if (timeout->l3num != par->family) {
> 244 ret = -EINVAL;
> 245 pr_info("Timeout policy `%s' can only be "
> 246 "used by L3 protocol number %d\n",
> 247 info->timeout, timeout->l3num);
> 248 goto err4;
> 249 }
> 250 /* Make sure the timeout policy matches any existing
> 251 * protocol tracker, otherwise default to generic.
> 252 */
> 253 l4proto = __nf_ct_l4proto_find(par->family,
> 254 e->ip.proto);
> 255 if (timeout->l4proto->l4proto != l4proto->l4proto) {
> 256 ret = -EINVAL;
> 257 pr_info("Timeout policy `%s' can only be "
> 258 "used by L4 protocol number %d\n",
> 259 info->timeout,
> 260 timeout->l4proto->l4proto);
> 261 goto err4;
> 262 }
> 263 timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
> 264 GFP_KERNEL);
> We are under rcu_read_lock() here.
Good catch, that needs to be GFP_ATOMIC. I'll send a follow-up patch
for this.
Thanks.
^ permalink raw reply
* Re: [PATCH 3/3] netfilter: xt_CT: remove a compile warning
From: Pablo Neira Ayuso @ 2012-04-03 10:39 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: netdev, David Miller
In-Reply-To: <201204031927.AIJ09351.FQFMJOOVFSOtHL@I-love.SAKURA.ne.jp>
[-- Attachment #1: Type: text/plain, Size: 1356 bytes --]
On Tue, Apr 03, 2012 at 07:27:50PM +0900, Tetsuo Handa wrote:
> pablo@netfilter.org:
> > From: Pablo Neira Ayuso <pablo@netfilter.org>
> >
> > If CONFIG_NF_CONNTRACK_TIMEOUT=n we have following warning :
> >
> > CC [M] net/netfilter/xt_CT.o
> > net/netfilter/xt_CT.c: In function ‘xt_ct_tg_check_v1’:
> > net/netfilter/xt_CT.c:284: warning: label ‘err4’ defined but not used
> >
> > Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
> > Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> > ---
> > net/netfilter/xt_CT.c | 2 ++
> > 1 files changed, 2 insertions(+), 0 deletions(-)
> >
> > diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
> > index 0c8e438..138b75e 100644
> > --- a/net/netfilter/xt_CT.c
> > +++ b/net/netfilter/xt_CT.c
> > @@ -281,8 +281,10 @@ out:
> > info->ct = ct;
> > return 0;
> >
> > +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
> > err4:
> > rcu_read_unlock();
> > +#endif
> > err3:
> > nf_conntrack_free(ct);
> > err2:
> > --
> > 1.7.2.5
>
> Looking at that function:
>
[...]
> 262 }
> 263 timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
> 264 GFP_KERNEL);
> We are under rcu_read_lock() here.
David, can you also apply the following patch?
Thanks.
[-- Attachment #2: 0001-netfilter-xt_CT-allocation-has-to-be-GFP_ATOMIC-unde.patch --]
[-- Type: text/x-diff, Size: 890 bytes --]
>From e21e9622a46a60bd2b8dfb9791bb63dfdd4940d7 Mon Sep 17 00:00:00 2001
From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue, 3 Apr 2012 12:32:15 +0200
Subject: [PATCH] netfilter: xt_CT: allocation has to be GFP_ATOMIC under rcu_read_lock section
Reported-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/xt_CT.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index 138b75e..4babb27 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -261,7 +261,7 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
goto err4;
}
timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
- GFP_KERNEL);
+ GFP_ATOMIC);
if (timeout_ext == NULL) {
ret = -ENOMEM;
goto err4;
--
1.7.2.5
^ permalink raw reply related
* [PATCH v6 0/2] Ethernet drivers for WIZnet chips
From: Mike Sinkovsky @ 2012-04-03 10:58 UTC (permalink / raw)
To: netdev, linux-kernel; +Cc: Mike Sinkovsky
In-Reply-To: <1332752876-1650-1-git-send-email-msink@permonline.ru>
Based on original driver from chip manufacturer, but nearly full rewite.
Tested and used in production with Blackfin BF531 embedded processor.
Signed-off-by: Mike Sinkovsky <msink@permonline.ru>
---
v6:
- remove (S0_TX_FSR < skb->len) check in TX handler, it doesn't work
anyway. Chip can transmit only one frame with MTU 1500 at a time,
and tx buffer size is bigger.
- remove dependency (ARM || BLACKFIN) - should work on any arch
- interrupt handler returns IRQ_NONE if status register is zero
- use devm_request_threaded_irq for request detect_link handler
- remove dev_info if probe failed
v5:
- added driver for WIZnet W5100
- disabled VLAN (NETIF_F_VLAN_CHALLENGED)
v4:
- netif_stop_queue if tx fifo is full, netif_wake_queue in interrupt
- use netif_info macro
v3:
- reading macaddr from platform_data
- ethtool: get_link, get_msglevel, set_msglevel
- more cleanups
v2:
- corrected handling of NET_ADDR_RANDOM flag
- support for WIZNET_BUS_ANY mode
- link detection using gpio
- registers read using ethtool
- more cleanups
^ permalink raw reply
* [PATCH v6 2/2] Ethernet driver for the WIZnet W5100 chip
From: Mike Sinkovsky @ 2012-04-03 10:58 UTC (permalink / raw)
To: netdev, linux-kernel; +Cc: Mike Sinkovsky
In-Reply-To: <1332752876-1650-1-git-send-email-msink@permonline.ru>
Based on original driver from chip manufacturer, but nearly full rewite.
Tested and used in production with Blackfin BF531 embedded processor.
Signed-off-by: Mike Sinkovsky <msink@permonline.ru>
---
drivers/net/ethernet/wiznet/Kconfig | 12 +
drivers/net/ethernet/wiznet/Makefile | 1 +
drivers/net/ethernet/wiznet/w5100.c | 792 ++++++++++++++++++++++++++++++++++
3 files changed, 805 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/ethernet/wiznet/w5100.c
diff --git a/drivers/net/ethernet/wiznet/Kconfig b/drivers/net/ethernet/wiznet/Kconfig
index d7a7b1f..2fc9194 100644
--- a/drivers/net/ethernet/wiznet/Kconfig
+++ b/drivers/net/ethernet/wiznet/Kconfig
@@ -17,6 +17,18 @@ config NET_VENDOR_WIZNET
if NET_VENDOR_WIZNET
+config WIZNET_W5100
+ tristate "WIZnet W5100 Ethernet support"
+ ---help---
+ Support for WIZnet W5100 chips.
+
+ W5100 is a single chip with integrated 10/100 Ethernet MAC,
+ PHY and hardware TCP/IP stack, but this driver is limited to
+ the MAC and PHY functions only, onchip TCP/IP is unused.
+
+ To compile this driver as a module, choose M here: the module
+ will be called w5100.
+
config WIZNET_W5300
tristate "WIZnet W5300 Ethernet support"
---help---
diff --git a/drivers/net/ethernet/wiznet/Makefile b/drivers/net/ethernet/wiznet/Makefile
index 88e0a3e..c614535 100644
--- a/drivers/net/ethernet/wiznet/Makefile
+++ b/drivers/net/ethernet/wiznet/Makefile
@@ -1 +1,2 @@
+obj-$(CONFIG_WIZNET_W5100) += w5100.o
obj-$(CONFIG_WIZNET_W5300) += w5300.o
diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
new file mode 100644
index 0000000..d6bf99b
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/w5100.c
@@ -0,0 +1,792 @@
+/*
+ * Ethernet driver for the WIZnet W5100 chip.
+ *
+ * Copyright (C) 2006-2008 WIZnet Co.,Ltd.
+ * Copyright (C) 2012 Mike Sinkovsky <msink@permonline.ru>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/wiznet.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#define DRV_NAME "w5100"
+#define DRV_VERSION "2012-03-30"
+
+MODULE_DESCRIPTION("WIZnet W5100 Ethernet driver v"DRV_VERSION);
+MODULE_AUTHOR("Mike Sinkovsky <msink@permonline.ru>");
+MODULE_ALIAS("platform:"DRV_NAME);
+MODULE_LICENSE("GPL");
+
+/*
+ * Registers
+ */
+#define W5100_COMMON_REGS 0x0000
+#define W5100_MR 0x0000 /* Mode Register */
+#define MR_RST 0x80 /* S/W reset */
+#define MR_PB 0x10 /* Ping block */
+#define MR_AI 0x02 /* Address Auto-Increment */
+#define MR_IND 0x01 /* Indirect mode */
+#define W5100_SHAR 0x0009 /* Source MAC address */
+#define W5100_IR 0x0015 /* Interrupt Register */
+#define W5100_IMR 0x0016 /* Interrupt Mask Register */
+#define IR_S0 0x01 /* S0 interrupt */
+#define W5100_RTR 0x0017 /* Retry Time-value Register */
+#define RTR_DEFAULT 2000 /* =0x07d0 (2000) */
+#define W5100_RMSR 0x001a /* Receive Memory Size */
+#define W5100_TMSR 0x001b /* Transmit Memory Size */
+#define W5100_COMMON_REGS_LEN 0x0040
+
+#define W5100_S0_REGS 0x0400
+#define W5100_S0_MR 0x0400 /* S0 Mode Register */
+#define S0_MR_MACRAW 0x04 /* MAC RAW mode (promiscous) */
+#define S0_MR_MACRAW_MF 0x44 /* MAC RAW mode (filtered) */
+#define W5100_S0_CR 0x0401 /* S0 Command Register */
+#define S0_CR_OPEN 0x01 /* OPEN command */
+#define S0_CR_CLOSE 0x10 /* CLOSE command */
+#define S0_CR_SEND 0x20 /* SEND command */
+#define S0_CR_RECV 0x40 /* RECV command */
+#define W5100_S0_IR 0x0402 /* S0 Interrupt Register */
+#define S0_IR_SENDOK 0x10 /* complete sending */
+#define S0_IR_RECV 0x04 /* receiving data */
+#define W5100_S0_SR 0x0403 /* S0 Status Register */
+#define S0_SR_MACRAW 0x42 /* mac raw mode */
+#define W5100_S0_TX_FSR 0x0420 /* S0 Transmit free memory size */
+#define W5100_S0_TX_RD 0x0422 /* S0 Transmit memory read pointer */
+#define W5100_S0_TX_WR 0x0424 /* S0 Transmit memory write pointer */
+#define W5100_S0_RX_RSR 0x0426 /* S0 Receive free memory size */
+#define W5100_S0_RX_RD 0x0428 /* S0 Receive memory read pointer */
+#define W5100_S0_REGS_LEN 0x0040
+
+#define W5100_TX_MEM_START 0x4000
+#define W5100_TX_MEM_END 0x5fff
+#define W5100_TX_MEM_MASK 0x1fff
+#define W5100_RX_MEM_START 0x6000
+#define W5100_RX_MEM_END 0x7fff
+#define W5100_RX_MEM_MASK 0x1fff
+
+/*
+ * Device driver private data structure
+ */
+struct w5100_priv {
+ void __iomem *base;
+ spinlock_t reg_lock;
+ bool indirect;
+ u8 (*read)(struct w5100_priv *priv, u16 addr);
+ void (*write)(struct w5100_priv *priv, u16 addr, u8 data);
+ u16 (*read16)(struct w5100_priv *priv, u16 addr);
+ void (*write16)(struct w5100_priv *priv, u16 addr, u16 data);
+ void (*readbuf)(struct w5100_priv *priv, u16 addr, u8 *buf, int len);
+ void (*writebuf)(struct w5100_priv *priv, u16 addr, u8 *buf, int len);
+ int irq;
+ int link_irq;
+ int link_gpio;
+
+ struct napi_struct napi;
+ struct net_device *ndev;
+ bool promisc;
+ u32 msg_enable;
+};
+
+/************************************************************************
+ *
+ * Lowlevel I/O functions
+ *
+ ***********************************************************************/
+
+/*
+ * In direct address mode host system can directly access W5100 registers
+ * after mapping to Memory-Mapped I/O space.
+ *
+ * 0x8000 bytes are required for memory space.
+ */
+static inline u8 w5100_read_direct(struct w5100_priv *priv, u16 addr)
+{
+ return ioread8(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+}
+
+static inline void w5100_write_direct(struct w5100_priv *priv,
+ u16 addr, u8 data)
+{
+ iowrite8(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+}
+
+static u16 w5100_read16_direct(struct w5100_priv *priv, u16 addr)
+{
+ u16 data;
+ data = w5100_read_direct(priv, addr) << 8;
+ data |= w5100_read_direct(priv, addr + 1);
+ return data;
+}
+
+static void w5100_write16_direct(struct w5100_priv *priv, u16 addr, u16 data)
+{
+ w5100_write_direct(priv, addr, data >> 8);
+ w5100_write_direct(priv, addr + 1, data);
+}
+
+static void w5100_readbuf_direct(struct w5100_priv *priv,
+ u16 offset, u8 *buf, int len)
+{
+ u16 addr = W5100_RX_MEM_START + (offset & W5100_RX_MEM_MASK);
+ int i;
+
+ for (i = 0; i < len; i++, addr++) {
+ if (unlikely(addr > W5100_RX_MEM_END))
+ addr = W5100_RX_MEM_START;
+ *buf++ = w5100_read_direct(priv, addr);
+ }
+}
+
+static void w5100_writebuf_direct(struct w5100_priv *priv,
+ u16 offset, u8 *buf, int len)
+{
+ u16 addr = W5100_TX_MEM_START + (offset & W5100_TX_MEM_MASK);
+ int i;
+
+ for (i = 0; i < len; i++, addr++) {
+ if (unlikely(addr > W5100_TX_MEM_END))
+ addr = W5100_TX_MEM_START;
+ w5100_write_direct(priv, addr, *buf++);
+ }
+}
+
+/*
+ * In indirect address mode host system indirectly accesses registers by
+ * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data
+ * Register (IDM_DR), which are directly mapped to Memory-Mapped I/O space.
+ * Mode Register (MR) is directly accessible.
+ *
+ * Only 0x04 bytes are required for memory space.
+ */
+#define W5100_IDM_AR 0x01 /* Indirect Mode Address Register */
+#define W5100_IDM_DR 0x03 /* Indirect Mode Data Register */
+
+static u8 w5100_read_indirect(struct w5100_priv *priv, u16 addr)
+{
+ unsigned long flags;
+ u8 data;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ mmiowb();
+ data = w5100_read_direct(priv, W5100_IDM_DR);
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+
+ return data;
+}
+
+static void w5100_write_indirect(struct w5100_priv *priv, u16 addr, u8 data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ mmiowb();
+ w5100_write_direct(priv, W5100_IDM_DR, data);
+ mmiowb();
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+}
+
+static u16 w5100_read16_indirect(struct w5100_priv *priv, u16 addr)
+{
+ unsigned long flags;
+ u16 data;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ mmiowb();
+ data = w5100_read_direct(priv, W5100_IDM_DR) << 8;
+ data |= w5100_read_direct(priv, W5100_IDM_DR);
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+
+ return data;
+}
+
+static void w5100_write16_indirect(struct w5100_priv *priv, u16 addr, u16 data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ mmiowb();
+ w5100_write_direct(priv, W5100_IDM_DR, data >> 8);
+ w5100_write_direct(priv, W5100_IDM_DR, data);
+ mmiowb();
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+}
+
+static void w5100_readbuf_indirect(struct w5100_priv *priv,
+ u16 offset, u8 *buf, int len)
+{
+ u16 addr = W5100_RX_MEM_START + (offset & W5100_RX_MEM_MASK);
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ mmiowb();
+
+ for (i = 0; i < len; i++, addr++) {
+ if (unlikely(addr > W5100_RX_MEM_END)) {
+ addr = W5100_RX_MEM_START;
+ w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ mmiowb();
+ }
+ *buf++ = w5100_read_direct(priv, W5100_IDM_DR);
+ }
+ mmiowb();
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+}
+
+static void w5100_writebuf_indirect(struct w5100_priv *priv,
+ u16 offset, u8 *buf, int len)
+{
+ u16 addr = W5100_TX_MEM_START + (offset & W5100_TX_MEM_MASK);
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ mmiowb();
+
+ for (i = 0; i < len; i++, addr++) {
+ if (unlikely(addr > W5100_TX_MEM_END)) {
+ addr = W5100_TX_MEM_START;
+ w5100_write16_direct(priv, W5100_IDM_AR, addr);
+ mmiowb();
+ }
+ w5100_write_direct(priv, W5100_IDM_DR, *buf++);
+ }
+ mmiowb();
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+}
+
+#if defined(CONFIG_WIZNET_BUS_DIRECT)
+#define w5100_read w5100_read_direct
+#define w5100_write w5100_write_direct
+#define w5100_read16 w5100_read16_direct
+#define w5100_write16 w5100_write16_direct
+#define w5100_readbuf w5100_readbuf_direct
+#define w5100_writebuf w5100_writebuf_direct
+
+#elif defined(CONFIG_WIZNET_BUS_INDIRECT)
+#define w5100_read w5100_read_indirect
+#define w5100_write w5100_write_indirect
+#define w5100_read16 w5100_read16_indirect
+#define w5100_write16 w5100_write16_indirect
+#define w5100_readbuf w5100_readbuf_indirect
+#define w5100_writebuf w5100_writebuf_indirect
+
+#else /* CONFIG_WIZNET_BUS_ANY */
+#define w5100_read priv->read
+#define w5100_write priv->write
+#define w5100_read16 priv->read16
+#define w5100_write16 priv->write16
+#define w5100_readbuf priv->readbuf
+#define w5100_writebuf priv->writebuf
+#endif
+
+static int w5100_command(struct w5100_priv *priv, u16 cmd)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+ w5100_write(priv, W5100_S0_CR, cmd);
+ mmiowb();
+
+ while (w5100_read(priv, W5100_S0_CR) != 0) {
+ if (time_after(jiffies, timeout))
+ return -EIO;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static void w5100_write_macaddr(struct w5100_priv *priv)
+{
+ struct net_device *ndev = priv->ndev;
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ w5100_write(priv, W5100_SHAR + i, ndev->dev_addr[i]);
+ mmiowb();
+}
+
+static void w5100_hw_reset(struct w5100_priv *priv)
+{
+ w5100_write_direct(priv, W5100_MR, MR_RST);
+ mmiowb();
+ mdelay(5);
+ w5100_write_direct(priv, W5100_MR, priv->indirect ?
+ MR_PB | MR_AI | MR_IND :
+ MR_PB);
+ mmiowb();
+ w5100_write(priv, W5100_IMR, 0);
+ w5100_write_macaddr(priv);
+
+ /* Configure 16K of internal memory
+ * as 8K RX buffer and 8K TX buffer
+ */
+ w5100_write(priv, W5100_RMSR, 0x03);
+ w5100_write(priv, W5100_TMSR, 0x03);
+ mmiowb();
+}
+
+static void w5100_hw_start(struct w5100_priv *priv)
+{
+ w5100_write(priv, W5100_S0_MR, priv->promisc ?
+ S0_MR_MACRAW : S0_MR_MACRAW_MF);
+ mmiowb();
+ w5100_command(priv, S0_CR_OPEN);
+ w5100_write(priv, W5100_IMR, IR_S0);
+ mmiowb();
+}
+
+static void w5100_hw_close(struct w5100_priv *priv)
+{
+ w5100_write(priv, W5100_IMR, 0);
+ mmiowb();
+ w5100_command(priv, S0_CR_CLOSE);
+}
+
+/***********************************************************************
+ *
+ * Device driver functions / callbacks
+ *
+ ***********************************************************************/
+
+static void w5100_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, dev_name(ndev->dev.parent),
+ sizeof(info->bus_info));
+}
+
+static u32 w5100_get_link(struct net_device *ndev)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ if (gpio_is_valid(priv->link_gpio))
+ return !!gpio_get_value(priv->link_gpio);
+
+ return 1;
+}
+
+static u32 w5100_get_msglevel(struct net_device *ndev)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ return priv->msg_enable;
+}
+
+static void w5100_set_msglevel(struct net_device *ndev, u32 value)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ priv->msg_enable = value;
+}
+
+static int w5100_get_regs_len(struct net_device *ndev)
+{
+ return W5100_COMMON_REGS_LEN + W5100_S0_REGS_LEN;
+}
+
+static void w5100_get_regs(struct net_device *ndev,
+ struct ethtool_regs *regs, void *_buf)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+ u8 *buf = _buf;
+ u16 i;
+
+ regs->version = 1;
+ for (i = 0; i < W5100_COMMON_REGS_LEN; i++)
+ *buf++ = w5100_read(priv, W5100_COMMON_REGS + i);
+ for (i = 0; i < W5100_S0_REGS_LEN; i++)
+ *buf++ = w5100_read(priv, W5100_S0_REGS + i);
+}
+
+static void w5100_tx_timeout(struct net_device *ndev)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ w5100_hw_reset(priv);
+ w5100_hw_start(priv);
+ ndev->stats.tx_errors++;
+ ndev->trans_start = jiffies;
+ netif_wake_queue(ndev);
+}
+
+static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+ u16 offset;
+
+ offset = w5100_read16(priv, W5100_S0_TX_WR);
+ w5100_writebuf(priv, offset, skb->data, skb->len);
+ w5100_write16(priv, W5100_S0_TX_WR, offset + skb->len);
+ mmiowb();
+ ndev->stats.tx_bytes += skb->len;
+ ndev->stats.tx_packets++;
+ dev_kfree_skb(skb);
+
+ w5100_command(priv, S0_CR_SEND);
+
+ return NETDEV_TX_OK;
+}
+
+static int w5100_napi_poll(struct napi_struct *napi, int budget)
+{
+ struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);
+ struct net_device *ndev = priv->ndev;
+ struct sk_buff *skb;
+ int rx_count;
+ u16 rx_len;
+ u16 offset;
+ u8 header[2];
+
+ for (rx_count = 0; rx_count < budget; rx_count++) {
+ u16 rx_buf_len = w5100_read16(priv, W5100_S0_RX_RSR);
+ if (rx_buf_len == 0)
+ break;
+
+ offset = w5100_read16(priv, W5100_S0_RX_RD);
+ w5100_readbuf(priv, offset, header, 2);
+ rx_len = get_unaligned_be16(header) - 2;
+
+ skb = netdev_alloc_skb_ip_align(ndev, rx_len);
+ if (unlikely(!skb)) {
+ w5100_write16(priv, W5100_S0_RX_RD,
+ offset + rx_buf_len);
+ w5100_command(priv, S0_CR_RECV);
+ ndev->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ skb_put(skb, rx_len);
+ w5100_readbuf(priv, offset + 2, skb->data, rx_len);
+ w5100_write16(priv, W5100_S0_RX_RD, offset + 2 + rx_len);
+ mmiowb();
+ w5100_command(priv, S0_CR_RECV);
+ skb->protocol = eth_type_trans(skb, ndev);
+
+ netif_receive_skb(skb);
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += rx_len;
+ }
+
+ if (rx_count < budget) {
+ w5100_write(priv, W5100_IMR, IR_S0);
+ mmiowb();
+ napi_complete(napi);
+ }
+
+ return rx_count;
+}
+
+static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
+{
+ struct net_device *ndev = ndev_instance;
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ int ir = w5100_read(priv, W5100_S0_IR);
+ if (!ir)
+ return IRQ_NONE;
+ w5100_write(priv, W5100_S0_IR, ir);
+ mmiowb();
+
+ if (ir & S0_IR_RECV) {
+ if (napi_schedule_prep(&priv->napi)) {
+ w5100_write(priv, W5100_IMR, 0);
+ mmiowb();
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t w5100_detect_link(int irq, void *ndev_instance)
+{
+ struct net_device *ndev = ndev_instance;
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ if (gpio_get_value(priv->link_gpio) != 0) {
+ netif_info(priv, link, ndev, "link is up\n");
+ netif_carrier_on(ndev);
+ } else {
+ netif_info(priv, link, ndev, "link is down\n");
+ netif_carrier_off(ndev);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void w5100_set_rx_mode(struct net_device *ndev)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+ bool set_promisc = (ndev->flags & IFF_PROMISC) != 0;
+
+ if (priv->promisc != set_promisc) {
+ priv->promisc = set_promisc;
+ w5100_hw_start(priv);
+ }
+}
+
+static int w5100_set_macaddr(struct net_device *ndev, void *addr)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+ struct sockaddr *sock_addr = addr;
+
+ if (!is_valid_ether_addr(sock_addr->sa_data))
+ return -EADDRNOTAVAIL;
+ memcpy(ndev->dev_addr, sock_addr->sa_data, ETH_ALEN);
+ ndev->addr_assign_type &= ~NET_ADDR_RANDOM;
+ w5100_write_macaddr(priv);
+ return 0;
+}
+
+static int w5100_open(struct net_device *ndev)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ netif_info(priv, ifup, ndev, "enabling\n");
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EINVAL;
+ w5100_hw_start(priv);
+ napi_enable(&priv->napi);
+ netif_start_queue(ndev);
+ if (!gpio_is_valid(priv->link_gpio) ||
+ gpio_get_value(priv->link_gpio) != 0)
+ netif_carrier_on(ndev);
+ return 0;
+}
+
+static int w5100_stop(struct net_device *ndev)
+{
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ netif_info(priv, ifdown, ndev, "shutting down\n");
+ w5100_hw_close(priv);
+ netif_carrier_off(ndev);
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ return 0;
+}
+
+static const struct ethtool_ops w5100_ethtool_ops = {
+ .get_drvinfo = w5100_get_drvinfo,
+ .get_msglevel = w5100_get_msglevel,
+ .set_msglevel = w5100_set_msglevel,
+ .get_link = w5100_get_link,
+ .get_regs_len = w5100_get_regs_len,
+ .get_regs = w5100_get_regs,
+};
+
+static const struct net_device_ops w5100_netdev_ops = {
+ .ndo_open = w5100_open,
+ .ndo_stop = w5100_stop,
+ .ndo_start_xmit = w5100_start_tx,
+ .ndo_tx_timeout = w5100_tx_timeout,
+ .ndo_set_rx_mode = w5100_set_rx_mode,
+ .ndo_set_mac_address = w5100_set_macaddr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int __devinit w5100_hw_probe(struct platform_device *pdev)
+{
+ struct wiznet_platform_data *data = pdev->dev.platform_data;
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5100_priv *priv = netdev_priv(ndev);
+ const char *name = netdev_name(ndev);
+ struct resource *mem;
+ int mem_size;
+ int irq;
+ int ret;
+
+ if (data && is_valid_ether_addr(data->mac_addr)) {
+ memcpy(ndev->dev_addr, data->mac_addr, ETH_ALEN);
+ } else {
+ random_ether_addr(ndev->dev_addr);
+ ndev->addr_assign_type |= NET_ADDR_RANDOM;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -ENXIO;
+ mem_size = resource_size(mem);
+ if (!devm_request_mem_region(&pdev->dev, mem->start, mem_size, name))
+ return -EBUSY;
+ priv->base = devm_ioremap(&pdev->dev, mem->start, mem_size);
+ if (!priv->base)
+ return -EBUSY;
+
+ spin_lock_init(&priv->reg_lock);
+ priv->indirect = mem_size < W5100_BUS_DIRECT_SIZE;
+ if (priv->indirect) {
+ priv->read = w5100_read_indirect;
+ priv->write = w5100_write_indirect;
+ priv->read16 = w5100_read16_indirect;
+ priv->write16 = w5100_write16_indirect;
+ priv->readbuf = w5100_readbuf_indirect;
+ priv->writebuf = w5100_writebuf_indirect;
+ } else {
+ priv->read = w5100_read_direct;
+ priv->write = w5100_write_direct;
+ priv->read16 = w5100_read16_direct;
+ priv->write16 = w5100_write16_direct;
+ priv->readbuf = w5100_readbuf_direct;
+ priv->writebuf = w5100_writebuf_direct;
+ }
+
+ w5100_hw_reset(priv);
+ if (w5100_read16(priv, W5100_RTR) != RTR_DEFAULT)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ ret = devm_request_irq(&pdev->dev, irq, w5100_interrupt,
+ IRQ_TYPE_LEVEL_LOW, name, ndev);
+ if (ret < 0)
+ return ret;
+ priv->irq = irq;
+
+ priv->link_gpio = data->link_gpio;
+ if (gpio_is_valid(priv->link_gpio)) {
+ char *link_name = devm_kzalloc(&pdev->dev, 16, GFP_KERNEL);
+ if (!link_name)
+ return -ENOMEM;
+ snprintf(link_name, 16, "%s-link", name);
+ priv->link_irq = gpio_to_irq(priv->link_gpio);
+ if (devm_request_threaded_irq(&pdev->dev,
+ priv->link_irq, NULL, w5100_detect_link,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ link_name, priv->ndev) < 0)
+ priv->link_gpio = -EINVAL;
+ }
+
+ netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, irq);
+ return 0;
+}
+
+static int __devinit w5100_probe(struct platform_device *pdev)
+{
+ struct w5100_priv *priv;
+ struct net_device *ndev;
+ int err;
+
+ ndev = alloc_etherdev(sizeof(*priv));
+ if (!ndev)
+ return -ENOMEM;
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ platform_set_drvdata(pdev, ndev);
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &w5100_netdev_ops;
+ ndev->ethtool_ops = &w5100_ethtool_ops;
+ ndev->watchdog_timeo = HZ;
+ netif_napi_add(ndev, &priv->napi, w5100_napi_poll, 16);
+
+ /* This chip doesn't support VLAN packets with normal MTU,
+ * so disable VLAN for this device.
+ */
+ ndev->features |= NETIF_F_VLAN_CHALLENGED;
+
+ err = register_netdev(ndev);
+ if (err < 0)
+ goto err_register;
+
+ err = w5100_hw_probe(pdev);
+ if (err < 0)
+ goto err_hw_probe;
+
+ return 0;
+
+err_hw_probe:
+ unregister_netdev(ndev);
+err_register:
+ free_netdev(ndev);
+ platform_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static int __devexit w5100_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int w5100_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ netif_carrier_off(ndev);
+ netif_device_detach(ndev);
+
+ w5100_hw_close(priv);
+ }
+ return 0;
+}
+
+static int w5100_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5100_priv *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ w5100_hw_reset(priv);
+ w5100_hw_start(priv);
+
+ netif_device_attach(ndev);
+ if (!gpio_is_valid(priv->link_gpio) ||
+ gpio_get_value(priv->link_gpio) != 0)
+ netif_carrier_on(ndev);
+ }
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume);
+
+static struct platform_driver w5100_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &w5100_pm_ops,
+ },
+ .probe = w5100_probe,
+ .remove = __devexit_p(w5100_remove),
+};
+
+module_platform_driver(w5100_driver);
--
1.6.3.3
^ permalink raw reply related
* [PATCH v6 1/2] Ethernet driver for the WIZnet W5300 chip
From: Mike Sinkovsky @ 2012-04-03 10:58 UTC (permalink / raw)
To: netdev, linux-kernel; +Cc: Mike Sinkovsky
In-Reply-To: <1332752876-1650-1-git-send-email-msink@permonline.ru>
Based on original driver from chip manufacturer, but nearly full rewite.
Tested and used in production with Blackfin BF531 embedded processor.
Signed-off-by: Mike Sinkovsky <msink@permonline.ru>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/wiznet/Kconfig | 58 +++
drivers/net/ethernet/wiznet/Makefile | 1 +
drivers/net/ethernet/wiznet/w5300.c | 703 ++++++++++++++++++++++++++++++++++
include/linux/platform_data/wiznet.h | 24 ++
6 files changed, 788 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/ethernet/wiznet/Kconfig
create mode 100644 drivers/net/ethernet/wiznet/Makefile
create mode 100644 drivers/net/ethernet/wiznet/w5300.c
create mode 100644 include/linux/platform_data/wiznet.h
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index c63a64c..a11af5c 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -174,6 +174,7 @@ source "drivers/net/ethernet/tile/Kconfig"
source "drivers/net/ethernet/toshiba/Kconfig"
source "drivers/net/ethernet/tundra/Kconfig"
source "drivers/net/ethernet/via/Kconfig"
+source "drivers/net/ethernet/wiznet/Kconfig"
source "drivers/net/ethernet/xilinx/Kconfig"
source "drivers/net/ethernet/xircom/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 9676a51..878ad32 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -73,5 +73,6 @@ obj-$(CONFIG_TILE_NET) += tile/
obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/
obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
obj-$(CONFIG_NET_VENDOR_VIA) += via/
+obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/
obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
diff --git a/drivers/net/ethernet/wiznet/Kconfig b/drivers/net/ethernet/wiznet/Kconfig
new file mode 100644
index 0000000..d7a7b1f
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/Kconfig
@@ -0,0 +1,58 @@
+#
+# WIZnet devices configuration
+#
+
+config NET_VENDOR_WIZNET
+ bool "WIZnet devices"
+ default y
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about WIZnet devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_WIZNET
+
+config WIZNET_W5300
+ tristate "WIZnet W5300 Ethernet support"
+ ---help---
+ Support for WIZnet W5300 chips.
+
+ W5300 is a single chip with integrated 10/100 Ethernet MAC,
+ PHY and hardware TCP/IP stack, but this driver is limited to
+ the MAC and PHY functions only, onchip TCP/IP is unused.
+
+ To compile this driver as a module, choose M here: the module
+ will be called w5300.
+
+choice
+ prompt "WIZnet interface mode"
+ default WIZNET_BUS_ANY
+
+config WIZNET_BUS_DIRECT
+ bool "Direct address bus mode"
+ ---help---
+ In direct address mode host system can directly access all registers
+ after mapping to Memory-Mapped I/O space.
+
+config WIZNET_BUS_INDIRECT
+ bool "Indirect address bus mode"
+ ---help---
+ In indirect address mode host system indirectly accesses registers
+ using Indirect Mode Address Register and Indirect Mode Data Register,
+ which are directly mapped to Memory-Mapped I/O space.
+
+config WIZNET_BUS_ANY
+ bool "Select interface mode in runtime"
+ ---help---
+ If interface mode is unknown in compile time, it can be selected
+ in runtime from board/platform resources configuration.
+
+ Performance may decrease compared to explicitly selected bus mode.
+endchoice
+
+endif # NET_VENDOR_WIZNET
diff --git a/drivers/net/ethernet/wiznet/Makefile b/drivers/net/ethernet/wiznet/Makefile
new file mode 100644
index 0000000..88e0a3e
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_WIZNET_W5300) += w5300.o
diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c
new file mode 100644
index 0000000..113ef7c
--- /dev/null
+++ b/drivers/net/ethernet/wiznet/w5300.c
@@ -0,0 +1,703 @@
+/*
+ * Ethernet driver for the WIZnet W5300 chip.
+ *
+ * Copyright (C) 2008-2009 WIZnet Co.,Ltd.
+ * Copyright (C) 2011 Taehun Kim <kth3321 <at> gmail.com>
+ * Copyright (C) 2012 Mike Sinkovsky <msink@permonline.ru>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/wiznet.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#define DRV_NAME "w5300"
+#define DRV_VERSION "2012-03-30"
+
+MODULE_DESCRIPTION("WIZnet W5300 Ethernet driver v"DRV_VERSION);
+MODULE_AUTHOR("Mike Sinkovsky <msink@permonline.ru>");
+MODULE_ALIAS("platform:"DRV_NAME);
+MODULE_LICENSE("GPL");
+
+/*
+ * Registers
+ */
+#define W5300_MR 0x0000 /* Mode Register */
+#define MR_DBW (1 << 15) /* Data bus width */
+#define MR_MPF (1 << 14) /* Mac layer pause frame */
+#define MR_WDF(n) (((n)&7)<<11) /* Write data fetch time */
+#define MR_RDH (1 << 10) /* Read data hold time */
+#define MR_FS (1 << 8) /* FIFO swap */
+#define MR_RST (1 << 7) /* S/W reset */
+#define MR_PB (1 << 4) /* Ping block */
+#define MR_DBS (1 << 2) /* Data bus swap */
+#define MR_IND (1 << 0) /* Indirect mode */
+#define W5300_IR 0x0002 /* Interrupt Register */
+#define W5300_IMR 0x0004 /* Interrupt Mask Register */
+#define IR_S0 0x0001 /* S0 interrupt */
+#define W5300_SHARL 0x0008 /* Source MAC address (0123) */
+#define W5300_SHARH 0x000c /* Source MAC address (45) */
+#define W5300_TMSRL 0x0020 /* Transmit Memory Size (0123) */
+#define W5300_TMSRH 0x0024 /* Transmit Memory Size (4567) */
+#define W5300_RMSRL 0x0028 /* Receive Memory Size (0123) */
+#define W5300_RMSRH 0x002c /* Receive Memory Size (4567) */
+#define W5300_MTYPE 0x0030 /* Memory Type */
+#define W5300_IDR 0x00fe /* Chip ID register */
+#define IDR_W5300 0x5300 /* =0x5300 for WIZnet W5300 */
+#define W5300_S0_MR 0x0200 /* S0 Mode Register */
+#define S0_MR_CLOSED 0x0000 /* Close mode */
+#define S0_MR_MACRAW 0x0004 /* MAC RAW mode (promiscous) */
+#define S0_MR_MACRAW_MF 0x0044 /* MAC RAW mode (filtered) */
+#define W5300_S0_CR 0x0202 /* S0 Command Register */
+#define S0_CR_OPEN 0x0001 /* OPEN command */
+#define S0_CR_CLOSE 0x0010 /* CLOSE command */
+#define S0_CR_SEND 0x0020 /* SEND command */
+#define S0_CR_RECV 0x0040 /* RECV command */
+#define W5300_S0_IMR 0x0204 /* S0 Interrupt Mask Register */
+#define W5300_S0_IR 0x0206 /* S0 Interrupt Register */
+#define S0_IR_RECV 0x0004 /* Receive interrupt */
+#define S0_IR_SENDOK 0x0010 /* Send OK interrupt */
+#define W5300_S0_SSR 0x0208 /* S0 Socket Status Register */
+#define W5300_S0_TX_WRSR 0x0220 /* S0 TX Write Size Register */
+#define W5300_S0_TX_FSR 0x0224 /* S0 TX Free Size Register */
+#define W5300_S0_RX_RSR 0x0228 /* S0 Received data Size */
+#define W5300_S0_TX_FIFO 0x022e /* S0 Transmit FIFO */
+#define W5300_S0_RX_FIFO 0x0230 /* S0 Receive FIFO */
+#define W5300_REGS_LEN 0x0400
+
+/*
+ * Device driver private data structure
+ */
+struct w5300_priv {
+ void __iomem *base;
+ spinlock_t reg_lock;
+ bool indirect;
+ u16 (*read) (struct w5300_priv *priv, u16 addr);
+ void (*write)(struct w5300_priv *priv, u16 addr, u16 data);
+ int irq;
+ int link_irq;
+ int link_gpio;
+
+ struct napi_struct napi;
+ struct net_device *ndev;
+ bool promisc;
+ u32 msg_enable;
+};
+
+/************************************************************************
+ *
+ * Lowlevel I/O functions
+ *
+ ***********************************************************************/
+
+/*
+ * In direct address mode host system can directly access W5300 registers
+ * after mapping to Memory-Mapped I/O space.
+ *
+ * 0x400 bytes are required for memory space.
+ */
+static inline u16 w5300_read_direct(struct w5300_priv *priv, u16 addr)
+{
+ return ioread16(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+}
+
+static inline void w5300_write_direct(struct w5300_priv *priv,
+ u16 addr, u16 data)
+{
+ iowrite16(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT));
+}
+
+/*
+ * In indirect address mode host system indirectly accesses registers by
+ * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data
+ * Register (IDM_DR), which are directly mapped to Memory-Mapped I/O space.
+ * Mode Register (MR) is directly accessible.
+ *
+ * Only 0x06 bytes are required for memory space.
+ */
+#define W5300_IDM_AR 0x0002 /* Indirect Mode Address */
+#define W5300_IDM_DR 0x0004 /* Indirect Mode Data */
+
+static u16 w5300_read_indirect(struct w5300_priv *priv, u16 addr)
+{
+ unsigned long flags;
+ u16 data;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ w5300_write_direct(priv, W5300_IDM_AR, addr);
+ mmiowb();
+ data = w5300_read_direct(priv, W5300_IDM_DR);
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+
+ return data;
+}
+
+static void w5300_write_indirect(struct w5300_priv *priv, u16 addr, u16 data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->reg_lock, flags);
+ w5300_write_direct(priv, W5300_IDM_AR, addr);
+ mmiowb();
+ w5300_write_direct(priv, W5300_IDM_DR, data);
+ mmiowb();
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
+}
+
+#if defined(CONFIG_WIZNET_BUS_DIRECT)
+#define w5300_read w5300_read_direct
+#define w5300_write w5300_write_direct
+
+#elif defined(CONFIG_WIZNET_BUS_INDIRECT)
+#define w5300_read w5300_read_indirect
+#define w5300_write w5300_write_indirect
+
+#else /* CONFIG_WIZNET_BUS_ANY */
+#define w5300_read priv->read
+#define w5300_write priv->write
+#endif
+
+static u32 w5300_read32(struct w5300_priv *priv, u16 addr)
+{
+ u32 data;
+ data = w5300_read(priv, addr) << 16;
+ data |= w5300_read(priv, addr + 2);
+ return data;
+}
+
+static void w5300_write32(struct w5300_priv *priv, u16 addr, u32 data)
+{
+ w5300_write(priv, addr, data >> 16);
+ w5300_write(priv, addr + 2, data);
+}
+
+static int w5300_command(struct w5300_priv *priv, u16 cmd)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+ w5300_write(priv, W5300_S0_CR, cmd);
+ mmiowb();
+
+ while (w5300_read(priv, W5300_S0_CR) != 0) {
+ if (time_after(jiffies, timeout))
+ return -EIO;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static void w5300_read_frame(struct w5300_priv *priv, u8 *buf, int len)
+{
+ u16 fifo;
+ int i;
+
+ for (i = 0; i < len; i += 2) {
+ fifo = w5300_read(priv, W5300_S0_RX_FIFO);
+ *buf++ = fifo >> 8;
+ *buf++ = fifo;
+ }
+ fifo = w5300_read(priv, W5300_S0_RX_FIFO);
+ fifo = w5300_read(priv, W5300_S0_RX_FIFO);
+}
+
+static void w5300_write_frame(struct w5300_priv *priv, u8 *buf, int len)
+{
+ u16 fifo;
+ int i;
+
+ for (i = 0; i < len; i += 2) {
+ fifo = *buf++ << 8;
+ fifo |= *buf++;
+ w5300_write(priv, W5300_S0_TX_FIFO, fifo);
+ }
+ w5300_write32(priv, W5300_S0_TX_WRSR, len);
+}
+
+static void w5300_write_macaddr(struct w5300_priv *priv)
+{
+ struct net_device *ndev = priv->ndev;
+ w5300_write32(priv, W5300_SHARL,
+ ndev->dev_addr[0] << 24 |
+ ndev->dev_addr[1] << 16 |
+ ndev->dev_addr[2] << 8 |
+ ndev->dev_addr[3]);
+ w5300_write(priv, W5300_SHARH,
+ ndev->dev_addr[4] << 8 |
+ ndev->dev_addr[5]);
+ mmiowb();
+}
+
+static void w5300_hw_reset(struct w5300_priv *priv)
+{
+ w5300_write_direct(priv, W5300_MR, MR_RST);
+ mmiowb();
+ mdelay(5);
+ w5300_write_direct(priv, W5300_MR, priv->indirect ?
+ MR_WDF(7) | MR_PB | MR_IND :
+ MR_WDF(7) | MR_PB);
+ mmiowb();
+ w5300_write(priv, W5300_IMR, 0);
+ w5300_write_macaddr(priv);
+
+ /* Configure 128K of internal memory
+ * as 64K RX fifo and 64K TX fifo
+ */
+ w5300_write32(priv, W5300_RMSRL, 64 << 24);
+ w5300_write32(priv, W5300_RMSRH, 0);
+ w5300_write32(priv, W5300_TMSRL, 64 << 24);
+ w5300_write32(priv, W5300_TMSRH, 0);
+ w5300_write(priv, W5300_MTYPE, 0x00ff);
+ mmiowb();
+}
+
+static void w5300_hw_start(struct w5300_priv *priv)
+{
+ w5300_write(priv, W5300_S0_MR, priv->promisc ?
+ S0_MR_MACRAW : S0_MR_MACRAW_MF);
+ mmiowb();
+ w5300_command(priv, S0_CR_OPEN);
+ w5300_write(priv, W5300_S0_IMR, S0_IR_RECV);
+ w5300_write(priv, W5300_IMR, IR_S0);
+ mmiowb();
+}
+
+static void w5300_hw_close(struct w5300_priv *priv)
+{
+ w5300_write(priv, W5300_IMR, 0);
+ mmiowb();
+ w5300_command(priv, S0_CR_CLOSE);
+}
+
+/***********************************************************************
+ *
+ * Device driver functions / callbacks
+ *
+ ***********************************************************************/
+
+static void w5300_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, dev_name(ndev->dev.parent),
+ sizeof(info->bus_info));
+}
+
+static u32 w5300_get_link(struct net_device *ndev)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ if (gpio_is_valid(priv->link_gpio))
+ return !!gpio_get_value(priv->link_gpio);
+
+ return 1;
+}
+
+static u32 w5300_get_msglevel(struct net_device *ndev)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ return priv->msg_enable;
+}
+
+static void w5300_set_msglevel(struct net_device *ndev, u32 value)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ priv->msg_enable = value;
+}
+
+static int w5300_get_regs_len(struct net_device *ndev)
+{
+ return W5300_REGS_LEN;
+}
+
+static void w5300_get_regs(struct net_device *ndev,
+ struct ethtool_regs *regs, void *_buf)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+ u8 *buf = _buf;
+ u16 addr;
+ u16 data;
+
+ regs->version = 1;
+ for (addr = 0; addr < W5300_REGS_LEN; addr += 2) {
+ switch (addr & 0x23f) {
+ case W5300_S0_TX_FIFO: /* cannot read TX_FIFO */
+ case W5300_S0_RX_FIFO: /* cannot read RX_FIFO */
+ data = 0xffff;
+ break;
+ default:
+ data = w5300_read(priv, addr);
+ break;
+ }
+ *buf++ = data >> 8;
+ *buf++ = data;
+ }
+}
+
+static void w5300_tx_timeout(struct net_device *ndev)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ w5300_hw_reset(priv);
+ w5300_hw_start(priv);
+ ndev->stats.tx_errors++;
+ ndev->trans_start = jiffies;
+ netif_wake_queue(ndev);
+}
+
+static int w5300_start_tx(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ w5300_write_frame(priv, skb->data, skb->len);
+ mmiowb();
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ dev_kfree_skb(skb);
+
+ w5300_command(priv, S0_CR_SEND);
+
+ return NETDEV_TX_OK;
+}
+
+static int w5300_napi_poll(struct napi_struct *napi, int budget)
+{
+ struct w5300_priv *priv = container_of(napi, struct w5300_priv, napi);
+ struct net_device *ndev = priv->ndev;
+ struct sk_buff *skb;
+ int rx_count;
+ u16 rx_len;
+
+ for (rx_count = 0; rx_count < budget; rx_count++) {
+ u32 rx_fifo_len = w5300_read32(priv, W5300_S0_RX_RSR);
+ if (rx_fifo_len == 0)
+ break;
+
+ rx_len = w5300_read(priv, W5300_S0_RX_FIFO);
+
+ skb = netdev_alloc_skb_ip_align(ndev, roundup(rx_len, 2));
+ if (unlikely(!skb)) {
+ u32 i;
+ for (i = 0; i < rx_fifo_len; i += 2)
+ w5300_read(priv, W5300_S0_RX_FIFO);
+ ndev->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ skb_put(skb, rx_len);
+ w5300_read_frame(priv, skb->data, rx_len);
+ skb->protocol = eth_type_trans(skb, ndev);
+
+ netif_receive_skb(skb);
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += rx_len;
+ }
+
+ if (rx_count < budget) {
+ w5300_write(priv, W5300_IMR, IR_S0);
+ mmiowb();
+ napi_complete(napi);
+ }
+
+ return rx_count;
+}
+
+static irqreturn_t w5300_interrupt(int irq, void *ndev_instance)
+{
+ struct net_device *ndev = ndev_instance;
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ int ir = w5300_read(priv, W5300_S0_IR);
+ if (!ir)
+ return IRQ_NONE;
+ w5300_write(priv, W5300_S0_IR, ir);
+ mmiowb();
+
+ if (ir & S0_IR_RECV) {
+ if (napi_schedule_prep(&priv->napi)) {
+ w5300_write(priv, W5300_IMR, 0);
+ mmiowb();
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t w5300_detect_link(int irq, void *ndev_instance)
+{
+ struct net_device *ndev = ndev_instance;
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ if (gpio_get_value(priv->link_gpio) != 0) {
+ netif_info(priv, link, ndev, "link is up\n");
+ netif_carrier_on(ndev);
+ } else {
+ netif_info(priv, link, ndev, "link is down\n");
+ netif_carrier_off(ndev);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void w5300_set_rx_mode(struct net_device *ndev)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+ bool set_promisc = (ndev->flags & IFF_PROMISC) != 0;
+
+ if (priv->promisc != set_promisc) {
+ priv->promisc = set_promisc;
+ w5300_hw_start(priv);
+ }
+}
+
+static int w5300_set_macaddr(struct net_device *ndev, void *addr)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+ struct sockaddr *sock_addr = addr;
+
+ if (!is_valid_ether_addr(sock_addr->sa_data))
+ return -EADDRNOTAVAIL;
+ memcpy(ndev->dev_addr, sock_addr->sa_data, ETH_ALEN);
+ ndev->addr_assign_type &= ~NET_ADDR_RANDOM;
+ w5300_write_macaddr(priv);
+ return 0;
+}
+
+static int w5300_open(struct net_device *ndev)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ netif_info(priv, ifup, ndev, "enabling\n");
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EINVAL;
+ w5300_hw_start(priv);
+ napi_enable(&priv->napi);
+ netif_start_queue(ndev);
+ if (!gpio_is_valid(priv->link_gpio) ||
+ gpio_get_value(priv->link_gpio) != 0)
+ netif_carrier_on(ndev);
+ return 0;
+}
+
+static int w5300_stop(struct net_device *ndev)
+{
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ netif_info(priv, ifdown, ndev, "shutting down\n");
+ w5300_hw_close(priv);
+ netif_carrier_off(ndev);
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ return 0;
+}
+
+static const struct ethtool_ops w5300_ethtool_ops = {
+ .get_drvinfo = w5300_get_drvinfo,
+ .get_msglevel = w5300_get_msglevel,
+ .set_msglevel = w5300_set_msglevel,
+ .get_link = w5300_get_link,
+ .get_regs_len = w5300_get_regs_len,
+ .get_regs = w5300_get_regs,
+};
+
+static const struct net_device_ops w5300_netdev_ops = {
+ .ndo_open = w5300_open,
+ .ndo_stop = w5300_stop,
+ .ndo_start_xmit = w5300_start_tx,
+ .ndo_tx_timeout = w5300_tx_timeout,
+ .ndo_set_rx_mode = w5300_set_rx_mode,
+ .ndo_set_mac_address = w5300_set_macaddr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int __devinit w5300_hw_probe(struct platform_device *pdev)
+{
+ struct wiznet_platform_data *data = pdev->dev.platform_data;
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5300_priv *priv = netdev_priv(ndev);
+ const char *name = netdev_name(ndev);
+ struct resource *mem;
+ int mem_size;
+ int irq;
+ int ret;
+
+ if (data && is_valid_ether_addr(data->mac_addr)) {
+ memcpy(ndev->dev_addr, data->mac_addr, ETH_ALEN);
+ } else {
+ random_ether_addr(ndev->dev_addr);
+ ndev->addr_assign_type |= NET_ADDR_RANDOM;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -ENXIO;
+ mem_size = resource_size(mem);
+ if (!devm_request_mem_region(&pdev->dev, mem->start, mem_size, name))
+ return -EBUSY;
+ priv->base = devm_ioremap(&pdev->dev, mem->start, mem_size);
+ if (!priv->base)
+ return -EBUSY;
+
+ spin_lock_init(&priv->reg_lock);
+ priv->indirect = mem_size < W5300_BUS_DIRECT_SIZE;
+ if (priv->indirect) {
+ priv->read = w5300_read_indirect;
+ priv->write = w5300_write_indirect;
+ } else {
+ priv->read = w5300_read_direct;
+ priv->write = w5300_write_direct;
+ }
+
+ w5300_hw_reset(priv);
+ if (w5300_read(priv, W5300_IDR) != IDR_W5300)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ ret = devm_request_irq(&pdev->dev, irq, w5300_interrupt,
+ IRQ_TYPE_LEVEL_LOW, name, ndev);
+ if (ret < 0)
+ return ret;
+ priv->irq = irq;
+
+ priv->link_gpio = data->link_gpio;
+ if (gpio_is_valid(priv->link_gpio)) {
+ char *link_name = devm_kzalloc(&pdev->dev, 16, GFP_KERNEL);
+ if (!link_name)
+ return -ENOMEM;
+ snprintf(link_name, 16, "%s-link", name);
+ priv->link_irq = gpio_to_irq(priv->link_gpio);
+ if (devm_request_threaded_irq(&pdev->dev,
+ priv->link_irq, NULL, w5300_detect_link,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ link_name, priv->ndev) < 0)
+ priv->link_gpio = -EINVAL;
+ }
+
+ netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, irq);
+ return 0;
+}
+
+static int __devinit w5300_probe(struct platform_device *pdev)
+{
+ struct w5300_priv *priv;
+ struct net_device *ndev;
+ int err;
+
+ ndev = alloc_etherdev(sizeof(*priv));
+ if (!ndev)
+ return -ENOMEM;
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ platform_set_drvdata(pdev, ndev);
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &w5300_netdev_ops;
+ ndev->ethtool_ops = &w5300_ethtool_ops;
+ ndev->watchdog_timeo = HZ;
+ netif_napi_add(ndev, &priv->napi, w5300_napi_poll, 16);
+
+ /* This chip doesn't support VLAN packets with normal MTU,
+ * so disable VLAN for this device.
+ */
+ ndev->features |= NETIF_F_VLAN_CHALLENGED;
+
+ err = register_netdev(ndev);
+ if (err < 0)
+ goto err_register;
+
+ err = w5300_hw_probe(pdev);
+ if (err < 0)
+ goto err_hw_probe;
+
+ return 0;
+
+err_hw_probe:
+ unregister_netdev(ndev);
+err_register:
+ free_netdev(ndev);
+ platform_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static int __devexit w5300_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_netdev(ndev);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int w5300_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ netif_carrier_off(ndev);
+ netif_device_detach(ndev);
+
+ w5300_hw_close(priv);
+ }
+ return 0;
+}
+
+static int w5300_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct w5300_priv *priv = netdev_priv(ndev);
+
+ if (!netif_running(ndev)) {
+ w5300_hw_reset(priv);
+ w5300_hw_start(priv);
+
+ netif_device_attach(ndev);
+ if (!gpio_is_valid(priv->link_gpio) ||
+ gpio_get_value(priv->link_gpio) != 0)
+ netif_carrier_on(ndev);
+ }
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(w5300_pm_ops, w5300_suspend, w5300_resume);
+
+static struct platform_driver w5300_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &w5300_pm_ops,
+ },
+ .probe = w5300_probe,
+ .remove = __devexit_p(w5300_remove),
+};
+
+module_platform_driver(w5300_driver);
diff --git a/include/linux/platform_data/wiznet.h b/include/linux/platform_data/wiznet.h
new file mode 100644
index 0000000..b5d8c19
--- /dev/null
+++ b/include/linux/platform_data/wiznet.h
@@ -0,0 +1,24 @@
+/*
+ * Ethernet driver for the WIZnet W5x00 chip.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef PLATFORM_DATA_WIZNET_H
+#define PLATFORM_DATA_WIZNET_H
+
+#include <linux/if_ether.h>
+
+struct wiznet_platform_data {
+ int link_gpio;
+ u8 mac_addr[ETH_ALEN];
+};
+
+#ifndef CONFIG_WIZNET_BUS_SHIFT
+#define CONFIG_WIZNET_BUS_SHIFT 0
+#endif
+
+#define W5100_BUS_DIRECT_SIZE (0x8000 << CONFIG_WIZNET_BUS_SHIFT)
+#define W5300_BUS_DIRECT_SIZE (0x0400 << CONFIG_WIZNET_BUS_SHIFT)
+
+#endif /* PLATFORM_DATA_WIZNET_H */
--
1.6.3.3
^ permalink raw reply related
* Re: [PATCH 1/2] TCP: update ip_local_port_range documentation
From: fernando @ 2012-04-03 11:08 UTC (permalink / raw)
To: David Miller; +Cc: shemminger@vyatta.com, netdev@vger.kernel.org
In-Reply-To: <20120403.005428.1951197638640326467.davem@davemloft.net>
Quoting David Miller <davem@davemloft.net>:
> From: fernando@intellilink.co.jp
> Date: Tue, 3 Apr 2012 09:48:40 +0900
>
>> + These two numbers determine the number of active connections
>> + which this system can issue simultaneously to systems not
>> + supporting TCP extensions (timestamps).
>
> This paragraph is false altogether, it's only true if we have
> one configured IP address.
>
> I'd remove it entirely, it doesn't help anyone and just adds
> confusion.
Done. I will be replying to this email with an updated patch.
^ permalink raw reply
* [PATCH net-next V5 5/8] net/route: export symbol ip_tos2prio
From: Amir Vadai @ 2012-04-03 11:10 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai
In-Reply-To: <1333451407-5293-1-git-send-email-amirv@mellanox.com>
Need to export this to enable drivers use rt_tos2priority()
Signed-off-by: Amir Vadai <amirv@mellanox.com>
---
net/ipv4/route.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 12ccf88..69e0b86 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -230,7 +230,7 @@ const __u8 ip_tos2prio[16] = {
TC_PRIO_INTERACTIVE_BULK,
ECN_OR_COST(INTERACTIVE_BULK)
};
-
+EXPORT_SYMBOL(ip_tos2prio);
/*
* Route cache.
--
1.7.8.2
^ permalink raw reply related
* [PATCH net-next V5 4/8] net/mlx4_en: sk_prio <=> UP for untagged traffic
From: Amir Vadai @ 2012-04-03 11:10 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai
In-Reply-To: <1333451407-5293-1-git-send-email-amirv@mellanox.com>
Since vlan egress map is only good for tagged traffic, need to have other
mapping to be used by untagged traffic.
For that, the driver uses sch_mqprio mapping. This mapping could be set by
using tc tool from iproute2 package.
Mapped UP will be used by the HW for QoS purposes, but won't go out on the
wire.
Signed-off-by: Amir Vadai <amirv@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 18 ++++++++++++++++++
drivers/net/ethernet/mellanox/mlx4/en_tx.c | 2 +-
2 files changed, 19 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 107f005..35003ad 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -45,6 +45,14 @@
#include "mlx4_en.h"
#include "en_port.h"
+static int mlx4_en_setup_tc(struct net_device *dev, u8 up)
+{
+ if (up != MLX4_EN_NUM_UP)
+ return -EINVAL;
+
+ return 0;
+}
+
static int mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -1038,6 +1046,7 @@ static const struct net_device_ops mlx4_netdev_ops = {
.ndo_poll_controller = mlx4_en_netpoll,
#endif
.ndo_set_features = mlx4_en_set_features,
+ .ndo_setup_tc = mlx4_en_setup_tc,
};
int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
@@ -1119,6 +1128,15 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
+ netdev_set_num_tc(dev, MLX4_EN_NUM_UP);
+
+ /* First 9 rings are for UP 0 */
+ netdev_set_tc_queue(dev, 0, MLX4_EN_NUM_TX_RINGS + 1, 0);
+
+ /* Partition Tx queues evenly amongst UP's 1-7 */
+ for (i = 1; i < MLX4_EN_NUM_UP; i++)
+ netdev_set_tc_queue(dev, i, 1, MLX4_EN_NUM_TX_RINGS + i);
+
SET_ETHTOOL_OPS(dev, &mlx4_en_ethtool_ops);
/* Set defualt MAC */
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 94a605a..d9bab53 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -577,7 +577,7 @@ u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb)
return MLX4_EN_NUM_TX_RINGS + (vlan_tag >> 13);
}
- return __skb_tx_hash(dev, skb, MLX4_EN_NUM_TX_RINGS);
+ return skb_tx_hash(dev, skb);
}
static void mlx4_bf_copy(void __iomem *dst, unsigned long *src, unsigned bytecnt)
--
1.7.8.2
^ permalink raw reply related
* [PATCH net-next V5 7/8] net/dcb: Add an optional max rate attribute
From: Amir Vadai @ 2012-04-03 11:10 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai
In-Reply-To: <1333451407-5293-1-git-send-email-amirv@mellanox.com>
Although not specified in 8021Qaz spec, it could be useful to enable drivers
whose HW supports setting a rate limit for an ETS TC. This patch adds this
optional attribute to DCB netlink. To use it, drivers should implement and
register the callbacks ieee_setmaxrate and ieee_getmaxrate. The units are 64
bits long and specified in Kbps to enable usage over both slow and very fast
networks.
Signed-off-by: Amir Vadai <amirv@mellanox.com>
---
include/linux/dcbnl.h | 5 +++++
include/net/dcbnl.h | 2 ++
net/dcb/dcbnl.c | 18 ++++++++++++++++++
3 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h
index 65a2562..ec8e372 100644
--- a/include/linux/dcbnl.h
+++ b/include/linux/dcbnl.h
@@ -67,6 +67,10 @@ struct ieee_ets {
__u8 reco_prio_tc[IEEE_8021QAZ_MAX_TCS];
};
+struct ieee_maxrate {
+ __u64 tc_maxrate[IEEE_8021QAZ_MAX_TCS];
+};
+
/* This structure contains the IEEE 802.1Qaz PFC managed object
*
* @pfc_cap: Indicates the number of traffic classes on the local device
@@ -321,6 +325,7 @@ enum ieee_attrs {
DCB_ATTR_IEEE_PEER_ETS,
DCB_ATTR_IEEE_PEER_PFC,
DCB_ATTR_IEEE_PEER_APP,
+ DCB_ATTR_IEEE_MAXRATE,
__DCB_ATTR_IEEE_MAX
};
#define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1)
diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h
index f55c980..fc5d5dc 100644
--- a/include/net/dcbnl.h
+++ b/include/net/dcbnl.h
@@ -48,6 +48,8 @@ struct dcbnl_rtnl_ops {
/* IEEE 802.1Qaz std */
int (*ieee_getets) (struct net_device *, struct ieee_ets *);
int (*ieee_setets) (struct net_device *, struct ieee_ets *);
+ int (*ieee_getmaxrate) (struct net_device *, struct ieee_maxrate *);
+ int (*ieee_setmaxrate) (struct net_device *, struct ieee_maxrate *);
int (*ieee_getpfc) (struct net_device *, struct ieee_pfc *);
int (*ieee_setpfc) (struct net_device *, struct ieee_pfc *);
int (*ieee_getapp) (struct net_device *, struct dcb_app *);
diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
index d860530..77dbc1d 100644
--- a/net/dcb/dcbnl.c
+++ b/net/dcb/dcbnl.c
@@ -178,6 +178,8 @@ static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = {
[DCB_ATTR_IEEE_ETS] = {.len = sizeof(struct ieee_ets)},
[DCB_ATTR_IEEE_PFC] = {.len = sizeof(struct ieee_pfc)},
[DCB_ATTR_IEEE_APP_TABLE] = {.type = NLA_NESTED},
+ [DCB_ATTR_IEEE_MAXRATE] = {.len = sizeof(struct ieee_maxrate)},
+
};
static const struct nla_policy dcbnl_ieee_app[DCB_ATTR_IEEE_APP_MAX + 1] = {
@@ -1243,6 +1245,14 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
NLA_PUT(skb, DCB_ATTR_IEEE_ETS, sizeof(ets), &ets);
}
+ if (ops->ieee_getmaxrate) {
+ struct ieee_maxrate maxrate;
+ err = ops->ieee_getmaxrate(netdev, &maxrate);
+ if (!err)
+ NLA_PUT(skb, DCB_ATTR_IEEE_MAXRATE, sizeof(maxrate),
+ &maxrate);
+ }
+
if (ops->ieee_getpfc) {
struct ieee_pfc pfc;
err = ops->ieee_getpfc(netdev, &pfc);
@@ -1589,6 +1599,14 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlattr **tb,
goto err;
}
+ if (ieee[DCB_ATTR_IEEE_MAXRATE] && ops->ieee_setmaxrate) {
+ struct ieee_maxrate *maxrate =
+ nla_data(ieee[DCB_ATTR_IEEE_MAXRATE]);
+ err = ops->ieee_setmaxrate(netdev, maxrate);
+ if (err)
+ goto err;
+ }
+
if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) {
struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]);
err = ops->ieee_setpfc(netdev, pfc);
--
1.7.8.2
^ permalink raw reply related
* [PATCH net-next V5 2/8] net/mlx4_core: set port QoS attributes
From: Amir Vadai @ 2012-04-03 11:10 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai
In-Reply-To: <1333451407-5293-1-git-send-email-amirv@mellanox.com>
Adding QoS firmware commands:
- mlx4_en_SET_PORT_PRIO2TC - set UP <=> TC
- mlx4_en_SET_PORT_SCHEDULER - set promised BW, max BW and PG number
Signed-off-by: Amir Vadai <amirv@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx4/en_port.h | 2 +
drivers/net/ethernet/mellanox/mlx4/mlx4.h | 20 ++++++++
drivers/net/ethernet/mellanox/mlx4/port.c | 62 ++++++++++++++++++++++++++
include/linux/mlx4/cmd.h | 4 ++
include/linux/mlx4/device.h | 3 +
5 files changed, 91 insertions(+), 0 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.h b/drivers/net/ethernet/mellanox/mlx4/en_port.h
index 6934fd7..745090b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_port.h
+++ b/drivers/net/ethernet/mellanox/mlx4/en_port.h
@@ -39,6 +39,8 @@
#define SET_PORT_PROMISC_SHIFT 31
#define SET_PORT_MC_PROMISC_SHIFT 30
+#define MLX4_EN_NUM_TC 8
+
#define VLAN_FLTR_SIZE 128
struct mlx4_set_vlan_fltr_mbox {
__be32 entry[VLAN_FLTR_SIZE];
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index 2a0ff2c..cd56f1a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -53,6 +53,26 @@
#define DRV_VERSION "1.1"
#define DRV_RELDATE "Dec, 2011"
+#define MLX4_NUM_UP 8
+#define MLX4_NUM_TC 8
+#define MLX4_RATELIMIT_UNITS 3 /* 100 Mbps */
+#define MLX4_RATELIMIT_DEFAULT 0xffff
+
+struct mlx4_set_port_prio2tc_context {
+ u8 prio2tc[4];
+};
+
+struct mlx4_port_scheduler_tc_cfg_be {
+ __be16 pg;
+ __be16 bw_precentage;
+ __be16 max_bw_units; /* 3-100Mbps, 4-1Gbps, other values - reserved */
+ __be16 max_bw_value;
+};
+
+struct mlx4_set_port_scheduler_context {
+ struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC];
+};
+
enum {
MLX4_HCR_BASE = 0x80680,
MLX4_HCR_SIZE = 0x0001c,
diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c
index 77535ff..55b12e6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/port.c
@@ -834,6 +834,68 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
}
EXPORT_SYMBOL(mlx4_SET_PORT_qpn_calc);
+int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc)
+{
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_set_port_prio2tc_context *context;
+ int err;
+ u32 in_mod;
+ int i;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+ context = mailbox->buf;
+ memset(context, 0, sizeof *context);
+
+ for (i = 0; i < MLX4_NUM_UP; i += 2)
+ context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1];
+
+ in_mod = MLX4_SET_PORT_PRIO2TC << 8 | port;
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+EXPORT_SYMBOL(mlx4_SET_PORT_PRIO2TC);
+
+int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
+ u8 *pg, u16 *ratelimit)
+{
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_set_port_scheduler_context *context;
+ int err;
+ u32 in_mod;
+ int i;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+ context = mailbox->buf;
+ memset(context, 0, sizeof *context);
+
+ for (i = 0; i < MLX4_NUM_TC; i++) {
+ struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i];
+ u16 r = ratelimit && ratelimit[i] ? ratelimit[i] :
+ MLX4_RATELIMIT_DEFAULT;
+
+ tc->pg = htons(pg[i]);
+ tc->bw_precentage = htons(tc_tx_bw[i]);
+
+ tc->max_bw_units = htons(MLX4_RATELIMIT_UNITS);
+ tc->max_bw_value = htons(r);
+ }
+
+ in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port;
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER);
+
int mlx4_SET_MCAST_FLTR_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h
index 9958ff2..1f3860a 100644
--- a/include/linux/mlx4/cmd.h
+++ b/include/linux/mlx4/cmd.h
@@ -150,6 +150,10 @@ enum {
/* statistics commands */
MLX4_CMD_QUERY_IF_STAT = 0X54,
MLX4_CMD_SET_IF_STAT = 0X55,
+
+ /* set port opcode modifiers */
+ MLX4_SET_PORT_PRIO2TC = 0x8,
+ MLX4_SET_PORT_SCHEDULER = 0x9,
};
enum {
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index 834c96c..6d02824 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -628,6 +628,9 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu,
u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx);
int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
u8 promisc);
+int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc);
+int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
+ u8 *pg, u16 *ratelimit);
int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx);
int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index);
void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index);
--
1.7.8.2
^ permalink raw reply related
* [PATCH net-next V5 1/8] net/mlx4_en: Force user priority by QP attribute
From: Amir Vadai @ 2012-04-03 11:10 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai
In-Reply-To: <1333451407-5293-1-git-send-email-amirv@mellanox.com>
Instead of relying on HW to change schedule queue by UP, schedule
queue is fixed for a tx_ring, and UP in WQE is ignored in this aspect. This
resolves two issues with untagged traffic:
1. untagged traffic has no UP in packet which is needed for QoS. The change
above allows setting the schedule queue (and by that the UP) of such a stream.
2. BlueFlame uses the same field used by vlan tag. So forcing UP from QPC
allows using BF for untagged but prioritized traffic.
In old firmware that force UP is not supported, untagged traffic will not subject to
QoS.
Because UP is set by QP, need to always have a tx ring per UP, even if pfcrx
module paramter is false.
Signed-off-by: Amir Vadai <amirv@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx4/en_main.c | 2 +-
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 3 ++-
drivers/net/ethernet/mellanox/mlx4/en_resources.c | 6 +++++-
drivers/net/ethernet/mellanox/mlx4/en_rx.c | 4 ++--
drivers/net/ethernet/mellanox/mlx4/en_tx.c | 12 ++++--------
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 6 +++---
include/linux/mlx4/qp.h | 3 ++-
7 files changed, 19 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c
index 2097a7d..346fdb2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c
@@ -114,7 +114,7 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
params->prof[i].tx_ring_size = MLX4_EN_DEF_TX_RING_SIZE;
params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE;
params->prof[i].tx_ring_num = MLX4_EN_NUM_TX_RINGS +
- (!!pfcrx) * MLX4_EN_NUM_PPP_RINGS;
+ MLX4_EN_NUM_PPP_RINGS;
params->prof[i].rss_rings = 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 31b455a..2322622 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -650,7 +650,8 @@ int mlx4_en_start_port(struct net_device *dev)
/* Configure ring */
tx_ring = &priv->tx_ring[i];
- err = mlx4_en_activate_tx_ring(priv, tx_ring, cq->mcq.cqn);
+ err = mlx4_en_activate_tx_ring(priv, tx_ring, cq->mcq.cqn,
+ max(0, i - MLX4_EN_NUM_TX_RINGS));
if (err) {
en_err(priv, "Failed allocating Tx ring\n");
mlx4_en_deactivate_cq(priv, cq);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_resources.c b/drivers/net/ethernet/mellanox/mlx4/en_resources.c
index bcbc54c..10c24c7 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_resources.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_resources.c
@@ -39,7 +39,7 @@
void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride,
int is_tx, int rss, int qpn, int cqn,
- struct mlx4_qp_context *context)
+ int user_prio, struct mlx4_qp_context *context)
{
struct mlx4_en_dev *mdev = priv->mdev;
@@ -57,6 +57,10 @@ void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride,
context->local_qpn = cpu_to_be32(qpn);
context->pri_path.ackto = 1 & 0x07;
context->pri_path.sched_queue = 0x83 | (priv->port - 1) << 6;
+ if (user_prio >= 0) {
+ context->pri_path.sched_queue |= user_prio << 3;
+ context->pri_path.feup = 1 << 6;
+ }
context->pri_path.counter_index = 0xff;
context->cqn_send = cpu_to_be32(cqn);
context->cqn_recv = cpu_to_be32(cqn);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index 9adbd53..d49a7ac 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -823,7 +823,7 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn,
memset(context, 0, sizeof *context);
mlx4_en_fill_qp_context(priv, ring->actual_size, ring->stride, 0, 0,
- qpn, ring->cqn, context);
+ qpn, ring->cqn, -1, context);
context->db_rec_addr = cpu_to_be64(ring->wqres.db.dma);
/* Cancel FCS removal if FW allows */
@@ -890,7 +890,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv)
}
rss_map->indir_qp.event = mlx4_en_sqp_event;
mlx4_en_fill_qp_context(priv, 0, 0, 0, 1, priv->base_qpn,
- priv->rx_ring[0].cqn, &context);
+ priv->rx_ring[0].cqn, -1, &context);
if (!priv->prof->rss_rings || priv->prof->rss_rings > priv->rx_ring_num)
rss_rings = priv->rx_ring_num;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 1796824..94a605a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -156,7 +156,7 @@ void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv,
int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_tx_ring *ring,
- int cq)
+ int cq, int user_prio)
{
struct mlx4_en_dev *mdev = priv->mdev;
int err;
@@ -174,7 +174,7 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
ring->doorbell_qpn = ring->qp.qpn << 8;
mlx4_en_fill_qp_context(priv, ring->size, ring->stride, 1, 0, ring->qpn,
- ring->cqn, &ring->context);
+ ring->cqn, user_prio, &ring->context);
if (ring->bf_enabled)
ring->context.usr_page = cpu_to_be32(ring->bf.uar->index);
@@ -570,18 +570,14 @@ static void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc, struct sk_buff *sk
u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb)
{
- struct mlx4_en_priv *priv = netdev_priv(dev);
u16 vlan_tag = 0;
- /* If we support per priority flow control and the packet contains
- * a vlan tag, send the packet to the TX ring assigned to that priority
- */
- if (priv->prof->rx_ppp && vlan_tx_tag_present(skb)) {
+ if (vlan_tx_tag_present(skb)) {
vlan_tag = vlan_tx_tag_get(skb);
return MLX4_EN_NUM_TX_RINGS + (vlan_tag >> 13);
}
- return skb_tx_hash(dev, skb);
+ return __skb_tx_hash(dev, skb, MLX4_EN_NUM_TX_RINGS);
}
static void mlx4_bf_copy(void __iomem *dst, unsigned long *src, unsigned bytecnt)
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 9e2b911..5bd7c2a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -521,7 +521,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ri
void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring);
int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_tx_ring *ring,
- int cq);
+ int cq, int user_prio);
void mlx4_en_deactivate_tx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_tx_ring *ring);
@@ -539,8 +539,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev,
int budget);
int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget);
void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride,
- int is_tx, int rss, int qpn, int cqn,
- struct mlx4_qp_context *context);
+ int is_tx, int rss, int qpn, int cqn, int user_prio,
+ struct mlx4_qp_context *context);
void mlx4_en_sqp_event(struct mlx4_qp *qp, enum mlx4_event event);
int mlx4_en_map_buffer(struct mlx4_buf *buf);
void mlx4_en_unmap_buffer(struct mlx4_buf *buf);
diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h
index 091f9e7..96005d7 100644
--- a/include/linux/mlx4/qp.h
+++ b/include/linux/mlx4/qp.h
@@ -139,7 +139,8 @@ struct mlx4_qp_path {
u8 rgid[16];
u8 sched_queue;
u8 vlan_index;
- u8 reserved3[2];
+ u8 feup;
+ u8 reserved3;
u8 reserved4[2];
u8 dmac[6];
};
--
1.7.8.2
^ permalink raw reply related
* [PATCH net-next V5 3/8] net/mlx4_en: DCB QoS support
From: Amir Vadai @ 2012-04-03 11:10 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai
In-Reply-To: <1333451407-5293-1-git-send-email-amirv@mellanox.com>
Set TSA, promised BW and PFC using IEEE 802.1qaz netlink commands.
Signed-off-by: Amir Vadai <amirv@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx4/Kconfig | 12 ++
drivers/net/ethernet/mellanox/mlx4/Makefile | 1 +
drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c | 209 ++++++++++++++++++++++++
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 5 +
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 21 +++
5 files changed, 248 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
diff --git a/drivers/net/ethernet/mellanox/mlx4/Kconfig b/drivers/net/ethernet/mellanox/mlx4/Kconfig
index 1bb9353..5f027f9 100644
--- a/drivers/net/ethernet/mellanox/mlx4/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx4/Kconfig
@@ -11,6 +11,18 @@ config MLX4_EN
This driver supports Mellanox Technologies ConnectX Ethernet
devices.
+config MLX4_EN_DCB
+ bool "Data Center Bridging (DCB) Support"
+ default y
+ depends on MLX4_EN && DCB
+ ---help---
+ Say Y here if you want to use Data Center Bridging (DCB) in the
+ driver.
+ If set to N, will not be able to configure QoS and ratelimit attributes.
+ This flag is depended on the kernel's DCB support.
+
+ If unsure, set to Y
+
config MLX4_CORE
tristate
depends on PCI
diff --git a/drivers/net/ethernet/mellanox/mlx4/Makefile b/drivers/net/ethernet/mellanox/mlx4/Makefile
index 4a40ab9..293127d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx4/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_MLX4_EN) += mlx4_en.o
mlx4_en-y := en_main.o en_tx.o en_rx.o en_ethtool.o en_port.o en_cq.o \
en_resources.o en_netdev.o en_selftest.o
+mlx4_en-$(CONFIG_MLX4_EN_DCB) += en_dcb_nl.o
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
new file mode 100644
index 0000000..6892320
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2011 Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/dcbnl.h>
+
+#include "mlx4_en.h"
+
+static int mlx4_en_dcbnl_ieee_getets(struct net_device *dev,
+ struct ieee_ets *ets)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ struct ieee_ets *my_ets = &priv->ets;
+
+ /* No IEEE PFC settings available */
+ if (!my_ets)
+ return -EINVAL;
+
+ ets->ets_cap = IEEE_8021QAZ_MAX_TCS;
+ ets->cbs = my_ets->cbs;
+ memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw));
+ memcpy(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa));
+ memcpy(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc));
+
+ return 0;
+}
+
+static int mlx4_en_ets_validate(struct mlx4_en_priv *priv, struct ieee_ets *ets)
+{
+ int i;
+ int total_ets_bw = 0;
+ int has_ets_tc = 0;
+
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+ if (ets->prio_tc[i] > MLX4_EN_NUM_UP) {
+ en_err(priv, "Bad priority in UP <=> TC mapping. TC: %d, UP: %d\n",
+ i, ets->prio_tc[i]);
+ return -EINVAL;
+ }
+
+ switch (ets->tc_tsa[i]) {
+ case IEEE_8021QAZ_TSA_STRICT:
+ break;
+ case IEEE_8021QAZ_TSA_ETS:
+ has_ets_tc = 1;
+ total_ets_bw += ets->tc_tx_bw[i];
+ break;
+ default:
+ en_err(priv, "TC[%d]: Not supported TSA: %d\n",
+ i, ets->tc_tsa[i]);
+ return -ENOTSUPP;
+ }
+ }
+
+ if (has_ets_tc && total_ets_bw != MLX4_EN_BW_MAX) {
+ en_err(priv, "Bad ETS BW sum: %d. Should be exactly 100%%\n",
+ total_ets_bw);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mlx4_en_config_port_scheduler(struct mlx4_en_priv *priv,
+ struct ieee_ets *ets, u16 *ratelimit)
+{
+ struct mlx4_en_dev *mdev = priv->mdev;
+ int num_strict = 0;
+ int i;
+ __u8 tc_tx_bw[IEEE_8021QAZ_MAX_TCS] = { 0 };
+ __u8 pg[IEEE_8021QAZ_MAX_TCS] = { 0 };
+
+ ets = ets ?: &priv->ets;
+
+ /* higher TC means higher priority => lower pg */
+ for (i = IEEE_8021QAZ_MAX_TCS - 1; i >= 0; i--) {
+ switch (ets->tc_tsa[i]) {
+ case IEEE_8021QAZ_TSA_STRICT:
+ pg[i] = num_strict++;
+ tc_tx_bw[i] = MLX4_EN_BW_MAX;
+ break;
+ case IEEE_8021QAZ_TSA_ETS:
+ pg[i] = MLX4_EN_TC_ETS;
+ tc_tx_bw[i] = ets->tc_tx_bw[i] ?: MLX4_EN_BW_MIN;
+ break;
+ }
+ }
+
+ return mlx4_SET_PORT_SCHEDULER(mdev->dev, priv->port, tc_tx_bw, pg,
+ ratelimit);
+}
+
+static int
+mlx4_en_dcbnl_ieee_setets(struct net_device *dev, struct ieee_ets *ets)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ struct mlx4_en_dev *mdev = priv->mdev;
+ int err;
+
+ err = mlx4_en_ets_validate(priv, ets);
+ if (err)
+ return err;
+
+ err = mlx4_SET_PORT_PRIO2TC(mdev->dev, priv->port, ets->prio_tc);
+ if (err)
+ return err;
+
+ err = mlx4_en_config_port_scheduler(priv, ets, NULL);
+ if (err)
+ return err;
+
+ memcpy(&priv->ets, ets, sizeof(priv->ets));
+
+ return 0;
+}
+
+static int mlx4_en_dcbnl_ieee_getpfc(struct net_device *dev,
+ struct ieee_pfc *pfc)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+
+ pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS;
+ pfc->pfc_en = priv->prof->tx_ppp;
+
+ return 0;
+}
+
+static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev,
+ struct ieee_pfc *pfc)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ struct mlx4_en_dev *mdev = priv->mdev;
+ int err;
+
+ en_dbg(DRV, priv, "cap: 0x%x en: 0x%x mbc: 0x%x delay: %d\n",
+ pfc->pfc_cap,
+ pfc->pfc_en,
+ pfc->mbc,
+ pfc->delay);
+
+ priv->prof->rx_pause = priv->prof->tx_pause = !!pfc->pfc_en;
+ priv->prof->rx_ppp = priv->prof->tx_ppp = pfc->pfc_en;
+
+ err = mlx4_SET_PORT_general(mdev->dev, priv->port,
+ priv->rx_skb_size + ETH_FCS_LEN,
+ priv->prof->tx_pause,
+ priv->prof->tx_ppp,
+ priv->prof->rx_pause,
+ priv->prof->rx_ppp);
+ if (err)
+ en_err(priv, "Failed setting pause params\n");
+
+ return err;
+}
+
+static u8 mlx4_en_dcbnl_getdcbx(struct net_device *dev)
+{
+ return DCB_CAP_DCBX_VER_IEEE;
+}
+
+static u8 mlx4_en_dcbnl_setdcbx(struct net_device *dev, u8 mode)
+{
+ if ((mode & DCB_CAP_DCBX_LLD_MANAGED) ||
+ (mode & DCB_CAP_DCBX_VER_CEE) ||
+ !(mode & DCB_CAP_DCBX_VER_IEEE) ||
+ !(mode & DCB_CAP_DCBX_HOST))
+ return 1;
+
+ return 0;
+}
+
+const struct dcbnl_rtnl_ops mlx4_en_dcbnl_ops = {
+ .ieee_getets = mlx4_en_dcbnl_ieee_getets,
+ .ieee_setets = mlx4_en_dcbnl_ieee_setets,
+ .ieee_getpfc = mlx4_en_dcbnl_ieee_getpfc,
+ .ieee_setpfc = mlx4_en_dcbnl_ieee_setpfc,
+
+ .getdcbx = mlx4_en_dcbnl_getdcbx,
+ .setdcbx = mlx4_en_dcbnl_setdcbx,
+};
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 2322622..107f005 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -967,6 +967,7 @@ void mlx4_en_destroy_netdev(struct net_device *dev)
mutex_unlock(&mdev->state_lock);
mlx4_en_free_resources(priv);
+
free_netdev(dev);
}
@@ -1080,6 +1081,10 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
INIT_WORK(&priv->watchdog_task, mlx4_en_restart);
INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate);
INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats);
+#ifdef CONFIG_MLX4_EN_DCB
+ if (!mlx4_is_slave(priv->mdev->dev))
+ dev->dcbnl_ops = &mlx4_en_dcbnl_ops;
+#endif
/* Query for default mac and max mtu */
priv->max_mtu = mdev->dev->caps.eth_mtu_cap[priv->port];
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 5bd7c2a..97d4f35 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -40,6 +40,9 @@
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
+#ifdef CONFIG_MLX4_EN_DCB
+#include <linux/dcbnl.h>
+#endif
#include <linux/mlx4/device.h>
#include <linux/mlx4/qp.h>
@@ -110,6 +113,7 @@ enum {
#define MLX4_EN_NUM_TX_RINGS 8
#define MLX4_EN_NUM_PPP_RINGS 8
#define MAX_TX_RINGS (MLX4_EN_NUM_TX_RINGS + MLX4_EN_NUM_PPP_RINGS)
+#define MLX4_EN_NUM_UP 8
#define MLX4_EN_DEF_TX_RING_SIZE 512
#define MLX4_EN_DEF_RX_RING_SIZE 1024
@@ -410,6 +414,15 @@ struct mlx4_en_frag_info {
};
+#ifdef CONFIG_MLX4_EN_DCB
+/* Minimal TC BW - setting to 0 will block traffic */
+#define MLX4_EN_BW_MIN 1
+#define MLX4_EN_BW_MAX 100 /* Utilize 100% of the line */
+
+#define MLX4_EN_TC_ETS 7
+
+#endif
+
struct mlx4_en_priv {
struct mlx4_en_dev *mdev;
struct mlx4_en_port_profile *prof;
@@ -483,6 +496,10 @@ struct mlx4_en_priv {
int vids[128];
bool wol;
struct device *ddev;
+
+#ifdef CONFIG_MLX4_EN_DCB
+ struct ieee_ets ets;
+#endif
};
enum mlx4_en_wol {
@@ -557,6 +574,10 @@ int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, struct mlx4_en_priv *priv);
int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset);
int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port);
+#ifdef CONFIG_MLX4_EN_DCB
+extern const struct dcbnl_rtnl_ops mlx4_en_dcbnl_ops;
+#endif
+
#define MLX4_EN_NUM_SELF_TEST 5
void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf);
u64 mlx4_en_mac_to_u64(u8 *addr);
--
1.7.8.2
^ permalink raw reply related
* [PATCH net-next V5 8/8] net/mlx4_en: Set max rate-limit for a TC
From: Amir Vadai @ 2012-04-03 11:10 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai
In-Reply-To: <1333451407-5293-1-git-send-email-amirv@mellanox.com>
This patch is using the DCB netlink to set rate limit per ETS TC
Values are accepted in Kbps and rounded up to the nearest multiply of 100Mbps.
Signed-off-by: Amir Vadai <amirv@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c | 45 ++++++++++++++++++++++++
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 1 +
2 files changed, 46 insertions(+), 0 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
index 6892320..0cc6c96 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
@@ -100,6 +100,7 @@ static int mlx4_en_config_port_scheduler(struct mlx4_en_priv *priv,
__u8 pg[IEEE_8021QAZ_MAX_TCS] = { 0 };
ets = ets ?: &priv->ets;
+ ratelimit = ratelimit ?: priv->maxrate;
/* higher TC means higher priority => lower pg */
for (i = IEEE_8021QAZ_MAX_TCS - 1; i >= 0; i--) {
@@ -198,9 +199,53 @@ static u8 mlx4_en_dcbnl_setdcbx(struct net_device *dev, u8 mode)
return 0;
}
+#define MLX4_RATELIMIT_UNITS_IN_KB 100000 /* rate-limit HW unit in Kbps */
+static int mlx4_en_dcbnl_ieee_getmaxrate(struct net_device *dev,
+ struct ieee_maxrate *maxrate)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ int i;
+
+ if (!priv->maxrate)
+ return -EINVAL;
+
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
+ maxrate->tc_maxrate[i] =
+ priv->maxrate[i] * MLX4_RATELIMIT_UNITS_IN_KB;
+
+ return 0;
+}
+
+static int mlx4_en_dcbnl_ieee_setmaxrate(struct net_device *dev,
+ struct ieee_maxrate *maxrate)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ u16 tmp[IEEE_8021QAZ_MAX_TCS];
+ int i, err;
+
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+ /* Convert from Kbps into HW units, rounding result up.
+ * Setting to 0, means unlimited BW.
+ */
+ tmp[i] =
+ (maxrate->tc_maxrate[i] + MLX4_RATELIMIT_UNITS_IN_KB -
+ 1) / MLX4_RATELIMIT_UNITS_IN_KB;
+ }
+
+ err = mlx4_en_config_port_scheduler(priv, NULL, tmp);
+ if (err)
+ return err;
+
+ memcpy(priv->maxrate, tmp, sizeof(*priv->maxrate));
+
+ return 0;
+}
+
const struct dcbnl_rtnl_ops mlx4_en_dcbnl_ops = {
.ieee_getets = mlx4_en_dcbnl_ieee_getets,
.ieee_setets = mlx4_en_dcbnl_ieee_setets,
+ .ieee_getmaxrate = mlx4_en_dcbnl_ieee_getmaxrate,
+ .ieee_setmaxrate = mlx4_en_dcbnl_ieee_setmaxrate,
.ieee_getpfc = mlx4_en_dcbnl_ieee_getpfc,
.ieee_setpfc = mlx4_en_dcbnl_ieee_setpfc,
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 97d4f35..3879c5e 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -499,6 +499,7 @@ struct mlx4_en_priv {
#ifdef CONFIG_MLX4_EN_DCB
struct ieee_ets ets;
+ u16 maxrate[IEEE_8021QAZ_MAX_TCS];
#endif
};
--
1.7.8.2
^ permalink raw reply related
* [PATCH net-next V5 6/8] IB/rdma_cm: TOS <=> UP mapping for IBoE
From: Amir Vadai @ 2012-04-03 11:10 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai, Sean Hefty
In-Reply-To: <1333451407-5293-1-git-send-email-amirv@mellanox.com>
Both tagged traffic and untagged traffic use tc tool mapping.
Treat RDMA TOS same as IP TOS when mapping to SL
Signed-off-by: Amir Vadai <amirv@mellanox.com>
CC: Sean Hefty <sean.hefty@intel.com>
---
drivers/infiniband/core/cma.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index e3e470f..59fbd70 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -42,6 +42,7 @@
#include <linux/inetdevice.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <net/route.h>
#include <net/tcp.h>
#include <net/ipv6.h>
@@ -1826,7 +1827,10 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv)
route->path_rec->reversible = 1;
route->path_rec->pkey = cpu_to_be16(0xffff);
route->path_rec->mtu_selector = IB_SA_EQ;
- route->path_rec->sl = id_priv->tos >> 5;
+ route->path_rec->sl = netdev_get_prio_tc_map(
+ ndev->priv_flags & IFF_802_1Q_VLAN ?
+ vlan_dev_real_dev(ndev) : ndev,
+ rt_tos2priority(id_priv->tos));
route->path_rec->mtu = iboe_get_mtu(ndev->mtu);
route->path_rec->rate_selector = IB_SA_EQ;
--
1.7.8.2
^ permalink raw reply related
* [PATCH net-next V5 0/8] net/mlx4_en: DCB QoS support
From: Amir Vadai @ 2012-04-03 11:09 UTC (permalink / raw)
To: David S. Miller
Cc: netdev, Roland Dreier, Yevgeny Petrilin, Oren Duer, Amir Vadai,
Amir Vadai, John Fastabend, Eric Dumazet
DCBX version 802.1qaz is supported.
User Priority (UP) is set in QP context instead of in WQE (QP Work Queue
Element), which means that all traffic from a queue will have the same UP.
UP is also set for untagged traffic to be able to classify such traffic too.
Mapping from sk_prio to User Priority is done by sch_mqprio mapping. Although
confusingly sch_mqprio maps sk_prio to something called TC, it is not related
to DCBX's TC, and is interpreted by mlx4_en driver as UP.
User can set maximal BW for an ETS TC. This could be done by a new optional
attribute added to DCB_ATTR_IEEE called DCBNL DCB_ATTR_IEEE_MAXRATE. It accept
an array of 8 64 bits values, 1 for every ETS TC. Units are in Kbps.
CC: John Fastabend <john.r.fastabend@intel.com>
CC: Eric Dumazet <eric.dumazet@gmail.com>
---
Changes from V4:
- Patch 2/8 - set port QoS attributes
- HW commands are using HW ratelimit units (100Mbps)
- Patch 3/8 - DCB QoS support
- ets attributes are stored inline mlx4_en_priv and not alloced separately
- Changed variable name: priv->mlx4_en_ieee_ets into priv->ets
- Patch 8/8 - Set max rate-limit for a TC in mlx4 driver using the
- ratelimit is stored inline mlx4_en_priv and not allocated separately
- ratelimit values are converted from Kbps into HW units (100Mbps) in the
netlink callback instead of in the HW access function.
- ratelimit values are rounded up. 0 is used for unlimited.
- moving ratelimit to be inline mlx4_en_priv also addresses Eric D. comments
Changes from V3:
After having a discussion about the API to set/get rate limit of an ETS TC,
decided to extend DCBNL with an optional attribute for that:
- new patch 7/8 Add an optional max rate attribute to DCBNL
- new patch 8/8 Set max rate-limit for a TC in mlx4 driver using the
infrastructure from patch 7/8
Changes from V2:
- Removed patch 4 who deal with setting ratelimit through sysfs - Still under discussion
- Patch 2/7 - set port QoS attributes
- ratelimit is given in Kbps instead of Mbps units.
Changes from V1:
- Patch 1/7 - Force user priority by QP attribute
- removed unneeded comment
- Patch 2/7 - set port QoS attributes
- ratelimit is given in Mbps instead of 100Mbps units.
- Patch 3/7 - DCB QoS support
- Split mlx4_en_config_port_scheduler from mlx4_en_dcbnl_ieee_setets - enable
setting ratelimit only, without setting up to tc mapping
- Patch 4/7 - Set max rate-limit for a TC
- Rewritten patch according to comment from Eric D.
- setting ratelimit is through sysfs files:
/sys/class/net/<intf>/ratelimit/tc0
/sys/class/net/<intf>/ratelimit/tc1
...
/sys/class/net/<intf>/ratelimit/tc7
- Patch 5/7 - sk_prio <=> UP for untagged traffic
- Using skb_tx_hash for queue selection of untagged traffic
- Added Patch 6/7 - export symbol ip_tos2prio
- Fixed patch according to comment from Eric D.
- Patch 7/7 - TOS <=> UP mapping for IBoE
- No need to clone code from route.c anymore - removed it
Changes from V0:
- Removed patches 6 and 7 who deal with the interaction between the kernel HW
QoS constructs to the queue selection logic are still under discussion with
John and some changes might be needed there.
Amir Vadai (8):
net/mlx4_en: Force user priority by QP attribute
net/mlx4_core: set port QoS attributes
net/mlx4_en: DCB QoS support
net/mlx4_en: sk_prio <=> UP for untagged traffic
net/route: export symbol ip_tos2prio
IB/rdma_cm: TOS <=> UP mapping for IBoE
net/dcb: Add an optional max rate attribute
net/mlx4_en: Set max rate-limit for a TC
drivers/infiniband/core/cma.c | 6 +-
drivers/net/ethernet/mellanox/mlx4/Kconfig | 12 +
drivers/net/ethernet/mellanox/mlx4/Makefile | 1 +
drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c | 254 +++++++++++++++++++++
drivers/net/ethernet/mellanox/mlx4/en_main.c | 2 +-
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 26 ++-
drivers/net/ethernet/mellanox/mlx4/en_port.h | 2 +
drivers/net/ethernet/mellanox/mlx4/en_resources.c | 6 +-
drivers/net/ethernet/mellanox/mlx4/en_rx.c | 4 +-
drivers/net/ethernet/mellanox/mlx4/en_tx.c | 10 +-
drivers/net/ethernet/mellanox/mlx4/mlx4.h | 20 ++
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 28 ++-
drivers/net/ethernet/mellanox/mlx4/port.c | 62 +++++
include/linux/dcbnl.h | 5 +
include/linux/mlx4/cmd.h | 4 +
include/linux/mlx4/device.h | 3 +
include/linux/mlx4/qp.h | 3 +-
include/net/dcbnl.h | 2 +
net/dcb/dcbnl.c | 18 ++
net/ipv4/route.c | 2 +-
20 files changed, 452 insertions(+), 18 deletions(-)
create mode 100644 drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
--
1.7.8.2
^ permalink raw reply
* [PATCH] TCP: update ip_local_port_range documentation
From: fernando @ 2012-04-03 11:12 UTC (permalink / raw)
To: David Miller; +Cc: shemminger@vyatta.com, netdev@vger.kernel.org
In-Reply-To: <20120403200834.witdp0frwwo48occ@webmail.int.intellilink.co.jp>
[-- Attachment #1: Type: text/plain, Size: 1 bytes --]
[-- Attachment #2: sysctl-ip_local_port_range-documentation-v2.patch --]
[-- Type: text/plain, Size: 1187 bytes --]
diff -urNp linux-3.4-rc1-orig/Documentation/networking/ip-sysctl.txt linux-3.4-rc1/Documentation/networking/ip-sysctl.txt
--- linux-3.4-rc1-orig/Documentation/networking/ip-sysctl.txt 2012-03-19 08:15:34.000000000 +0900
+++ linux-3.4-rc1/Documentation/networking/ip-sysctl.txt 2012-04-03 19:08:59.554903584 +0900
@@ -604,15 +604,8 @@ IP Variables:
ip_local_port_range - 2 INTEGERS
Defines the local port range that is used by TCP and UDP to
choose the local port. The first number is the first, the
- second the last local port number. Default value depends on
- amount of memory available on the system:
- > 128Mb 32768-61000
- < 128Mb 1024-4999 or even less.
- This number defines number of active connections, which this
- system can issue simultaneously to systems not supporting
- TCP extensions (timestamps). With tcp_tw_recycle enabled
- (i.e. by default) range 1024-4999 is enough to issue up to
- 2000 connections per second to systems supporting timestamps.
+ second the last local port number. The default values are
+ 32768 and 61000 respectively.
ip_local_reserved_ports - list of comma separated ranges
Specify the ports which are reserved for known third-party
^ permalink raw reply
* stmmac: what happened to IFF_UNICAST_FLT
From: Marc Kleine-Budde @ 2012-04-03 11:14 UTC (permalink / raw)
To: Rayagond Kokatanur; +Cc: Linux Netdev List, Giuseppe Cavallaro
Hello,
I think in patch "bfab27a stmmac: add the experimental PCI support" the
flag IFF_UNICAST_FLT was lost, when the function
stmmac_mac_device_setup() was moved around.
regards, Marc
^ permalink raw reply
* Re: [PATCH v6 0/2] Ethernet drivers for WIZnet chips
From: Eric Dumazet @ 2012-04-03 11:39 UTC (permalink / raw)
To: Mike Sinkovsky; +Cc: netdev, linux-kernel
In-Reply-To: <1333450726-24455-1-git-send-email-msink@permonline.ru>
On Tue, 2012-04-03 at 16:58 +0600, Mike Sinkovsky wrote:
> Based on original driver from chip manufacturer, but nearly full rewite.
> Tested and used in production with Blackfin BF531 embedded processor.
>
> Signed-off-by: Mike Sinkovsky <msink@permonline.ru>
> ---
> v6:
> - remove (S0_TX_FSR < skb->len) check in TX handler, it doesn't work
> anyway. Chip can transmit only one frame with MTU 1500 at a time,
> and tx buffer size is bigger.
So what happens if XXX frames are given to start_xmit() in a flood ?
You removed any flow control, how can this work ?
Device has an infinite queue ?
^ permalink raw reply
* Re: [PATCH 3/3] netfilter: xt_CT: remove a compile warning
From: Tetsuo Handa @ 2012-04-03 11:48 UTC (permalink / raw)
To: pablo; +Cc: netdev
In-Reply-To: <201204031927.AIJ09351.FQFMJOOVFSOtHL@I-love.SAKURA.ne.jp>
One more question.
Tetsuo Handa wrote:
> 216 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
> 217 if (info->timeout) {
> 218 typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
> 219 struct ctnl_timeout *timeout;
> 220 struct nf_conn_timeout *timeout_ext;
> 221
> 222 rcu_read_lock();
> 223 timeout_find_get =
> 224 rcu_dereference(nf_ct_timeout_find_get_hook);
> 225
> 226 if (timeout_find_get) {
I assume timeout_find_get points to e.g. ctnl_timeout_find_get in
net/netfilter/nfnetlink_cttimeout.c . If yes,
> 227 const struct ipt_entry *e = par->entryinfo;
> 228 struct nf_conntrack_l4proto *l4proto;
> 229
> 230 if (e->ip.invflags & IPT_INV_PROTO) {
> 231 ret = -EINVAL;
> 232 pr_info("You cannot use inversion on "
> 233 "L4 protocol\n");
> 234 goto err4;
> 235 }
> 236 timeout = timeout_find_get(info->timeout);
> 237 if (timeout == NULL) {
> 238 ret = -ENOENT;
> 239 pr_info("No such timeout policy \"%s\"\n",
> 240 info->timeout);
> 241 goto err4;
> 242 }
I think "goto err4;" after successful timeout_find_get() wants e.g.
nf_ct_timeout_put_hook call (e.g. ctnl_timeout_put()).
> 243 if (timeout->l3num != par->family) {
> 244 ret = -EINVAL;
> 245 pr_info("Timeout policy `%s' can only be "
> 246 "used by L3 protocol number %d\n",
> 247 info->timeout, timeout->l3num);
> 248 goto err4;
> 249 }
^ permalink raw reply
* [PATCH] Driver: Atm: Remove 'break' after 'return' statement.
From: santosh nayak @ 2012-04-03 11:47 UTC (permalink / raw)
To: chas; +Cc: linux-atm-general, netdev, kernel-janitors, Santosh Nayak
From: Santosh Nayak <santoshprasadnayak@gmail.com>
'break' is unnecessary after 'return' statement.
Remove all such 'break' as clean up.
Signed-off-by: Santosh Nayak <santoshprasadnayak@gmail.com>
---
drivers/atm/horizon.c | 5 -----
1 files changed, 0 insertions(+), 5 deletions(-)
diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c
index b812103..71c801b 100644
--- a/drivers/atm/horizon.c
+++ b/drivers/atm/horizon.c
@@ -2183,7 +2183,6 @@ static int hrz_open (struct atm_vcc *atm_vcc)
default:
PRINTD (DBG_QOS|DBG_VCC, "Bad AAL!");
return -EINVAL;
- break;
}
// TX traffic parameters
@@ -2358,7 +2357,6 @@ static int hrz_open (struct atm_vcc *atm_vcc)
default: {
PRINTD (DBG_QOS, "unsupported TX traffic class");
return -EINVAL;
- break;
}
}
}
@@ -2434,7 +2432,6 @@ static int hrz_open (struct atm_vcc *atm_vcc)
default: {
PRINTD (DBG_QOS, "unsupported RX traffic class");
return -EINVAL;
- break;
}
}
}
@@ -2582,7 +2579,6 @@ static int hrz_getsockopt (struct atm_vcc * atm_vcc, int level, int optname,
// break;
default:
return -ENOPROTOOPT;
- break;
};
break;
}
@@ -2602,7 +2598,6 @@ static int hrz_setsockopt (struct atm_vcc * atm_vcc, int level, int optname,
// break;
default:
return -ENOPROTOOPT;
- break;
};
break;
}
--
1.7.4.4
^ permalink raw reply related
* Re: [PATCH 3/3] netfilter: xt_CT: remove a compile warning
From: Pablo Neira Ayuso @ 2012-04-03 12:09 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: netdev
In-Reply-To: <201204032048.GFH51564.QSOVLMOOJHtFFF@I-love.SAKURA.ne.jp>
[-- Attachment #1: Type: text/plain, Size: 1850 bytes --]
On Tue, Apr 03, 2012 at 08:48:21PM +0900, Tetsuo Handa wrote:
> One more question.
>
> Tetsuo Handa wrote:
> > 216 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
> > 217 if (info->timeout) {
> > 218 typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
> > 219 struct ctnl_timeout *timeout;
> > 220 struct nf_conn_timeout *timeout_ext;
> > 221
> > 222 rcu_read_lock();
> > 223 timeout_find_get =
> > 224 rcu_dereference(nf_ct_timeout_find_get_hook);
> > 225
> > 226 if (timeout_find_get) {
>
> I assume timeout_find_get points to e.g. ctnl_timeout_find_get in
> net/netfilter/nfnetlink_cttimeout.c . If yes,
>
> > 227 const struct ipt_entry *e = par->entryinfo;
> > 228 struct nf_conntrack_l4proto *l4proto;
> > 229
> > 230 if (e->ip.invflags & IPT_INV_PROTO) {
> > 231 ret = -EINVAL;
> > 232 pr_info("You cannot use inversion on "
> > 233 "L4 protocol\n");
> > 234 goto err4;
> > 235 }
> > 236 timeout = timeout_find_get(info->timeout);
> > 237 if (timeout == NULL) {
> > 238 ret = -ENOENT;
> > 239 pr_info("No such timeout policy \"%s\"\n",
> > 240 info->timeout);
> > 241 goto err4;
> > 242 }
>
> I think "goto err4;" after successful timeout_find_get() wants e.g.
> nf_ct_timeout_put_hook call (e.g. ctnl_timeout_put()).
Indeed. The patch attached should fix this problem. Thanks.
[-- Attachment #2: 0001-netfilter-xt_CT-fix-missing-put-timeout-object-in-er.patch --]
[-- Type: text/x-diff, Size: 3112 bytes --]
>From a7ff65f786a38c6a612eb3b65ccdf9ea4c517503 Mon Sep 17 00:00:00 2001
From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue, 3 Apr 2012 14:00:18 +0200
Subject: [PATCH] netfilter: xt_CT: fix missing put timeout object in error path
The error path misses putting the timeout object. This patch adds
new function xt_ct_tg_timeout_put() to do that to avoid code
duplication.
Reported-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/xt_CT.c | 44 +++++++++++++++++++++++++-------------------
1 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index 4babb27..89126fc 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -150,6 +150,24 @@ err1:
return ret;
}
+static void xt_ct_tg_timeout_put(struct nf_conn *ct)
+{
+#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
+ struct nf_conn_timeout *timeout_ext;
+ typeof(nf_ct_timeout_put_hook) timeout_put;
+
+ rcu_read_lock();
+ timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
+
+ if (timeout_put) {
+ timeout_ext = nf_ct_timeout_find(ct);
+ if (timeout_ext)
+ timeout_put(timeout_ext->timeout);
+ }
+ rcu_read_unlock();
+#endif
+}
+
static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
{
struct xt_ct_target_info_v1 *info = par->targinfo;
@@ -245,7 +263,7 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
pr_info("Timeout policy `%s' can only be "
"used by L3 protocol number %d\n",
info->timeout, timeout->l3num);
- goto err4;
+ goto err5;
}
/* Make sure the timeout policy matches any existing
* protocol tracker, otherwise default to generic.
@@ -258,13 +276,13 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
"used by L4 protocol number %d\n",
info->timeout,
timeout->l4proto->l4proto);
- goto err4;
+ goto err5;
}
timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
GFP_ATOMIC);
if (timeout_ext == NULL) {
ret = -ENOMEM;
- goto err4;
+ goto err5;
}
} else {
ret = -ENOENT;
@@ -282,6 +300,8 @@ out:
return 0;
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
+err5:
+ xt_ct_tg_timeout_put(ct);
err4:
rcu_read_unlock();
#endif
@@ -314,28 +334,14 @@ static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
struct xt_ct_target_info_v1 *info = par->targinfo;
struct nf_conn *ct = info->ct;
struct nf_conn_help *help;
-#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
- struct nf_conn_timeout *timeout_ext;
- typeof(nf_ct_timeout_put_hook) timeout_put;
-#endif
+
if (!nf_ct_is_untracked(ct)) {
help = nfct_help(ct);
if (help)
module_put(help->helper->me);
nf_ct_l3proto_module_put(par->family);
-
-#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
- rcu_read_lock();
- timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
-
- if (timeout_put) {
- timeout_ext = nf_ct_timeout_find(ct);
- if (timeout_ext)
- timeout_put(timeout_ext->timeout);
- }
- rcu_read_unlock();
-#endif
+ xt_ct_tg_timeout_put(ct);
}
nf_ct_put(info->ct);
}
--
1.7.2.5
^ permalink raw reply related
* Re: [PATCH v6 0/2] Ethernet drivers for WIZnet chips
From: Mike Sinkovsky @ 2012-04-03 12:15 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev, linux-kernel
In-Reply-To: <1333453170.18626.65.camel@edumazet-glaptop>
03.04.2012 17:39, Eric Dumazet написал:
> On Tue, 2012-04-03 at 16:58 +0600, Mike Sinkovsky wrote:
>> Based on original driver from chip manufacturer, but nearly full rewite.
>> Tested and used in production with Blackfin BF531 embedded processor.
>>
>> Signed-off-by: Mike Sinkovsky<msink@permonline.ru>
>> ---
>> v6:
>> - remove (S0_TX_FSR< skb->len) check in TX handler, it doesn't work
>> anyway. Chip can transmit only one frame with MTU 1500 at a time,
>> and tx buffer size is bigger.
>
> So what happens if XXX frames are given to start_xmit() in a flood ?
>
> You removed any flow control, how can this work ?
>
> Device has an infinite queue ?
As I understand from datasheet, device doesn't have tx queue at all.
It have tx buffer, processor must save transmitted frame to it, and then
save SEND command to command register. When transmission completed,
SENDOK bit in interrupt register will be set to '1', and interrupt
handler will be called.
So, according to datasheet, driver must stop queue in start_xmit()
routine, and wake in interrupt handler of SENDOK bit.
I tried this, and it basically works, but SOMETIMES, very rare, I see
tx_timeout().
And without any flow control - driver works perfectly.
Weird, don't know why.
--
Mike
^ permalink raw reply
* Re: [PATCH 3/3] netfilter: xt_CT: remove a compile warning
From: Tetsuo Handa @ 2012-04-03 12:28 UTC (permalink / raw)
To: pablo; +Cc: netdev
In-Reply-To: <20120403120900.GB6010@1984>
Pablo Neira Ayuso wrote:
> Indeed. The patch attached should fix this problem. Thanks.
It seems to me that the timeout object is associated with the "ct"
by successful nf_ct_timeout_ext_add() call. If yes,
+err5:
+ xt_ct_tg_timeout_put(ct);
will not be able to find the timeout object.
^ permalink raw reply
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