* Re: [flame^Wreview] net: netprio_cgroup: rework update socket logic
From: Neil Horman @ 2012-08-13 11:22 UTC (permalink / raw)
To: John Fastabend; +Cc: Al Viro, netdev, David Miller, linux-kernel
In-Reply-To: <502896C5.7080303@intel.com>
On Sun, Aug 12, 2012 at 10:55:17PM -0700, John Fastabend wrote:
> On 8/12/2012 6:53 PM, Al Viro wrote:
> > Ladies and gentlemen, who the devil had reviewed that little gem?
> >
> >commit 406a3c638ce8b17d9704052c07955490f732c2b8
> >Author: John Fastabend <john.r.fastabend@intel.com>
> >Date: Fri Jul 20 10:39:25 2012 +0000
> >
> >is a bleeding bogosity that doesn't pass even the most cursory
> >inspection. It iterates through descriptor tables of a bunch
> >of processes, doing this:
> > file = fcheck_files(files, fd);
> > if (!file)
> > continue;
> >
> > path = d_path(&file->f_path, tmp, PAGE_SIZE);
> > rv = sscanf(path, "socket:[%lu]", &s);
> > if (rv <= 0)
> > continue;
> >
> > sock = sock_from_file(file, &err);
> > if (!err)
> > sock_update_netprioidx(sock->sk, p);
> >Note the charming use of sscanf() for pattern-matching. 's' (inode
> >number of socket) is completely unused afterwards; what happens here
> >is a very badly written attempt to skip non-sockets. Why, will
> >sock_from_file() blow up on non-sockets? And isn't there some less
> >obnoxious way to check that the file is a sockfs one? Let's see:
> >struct socket *sock_from_file(struct file *file, int *err)
> >{
> > if (file->f_op == &socket_file_ops)
> > return file->private_data; /* set in sock_map_fd */
> >
> > *err = -ENOTSOCK;
> > return NULL;
> >}
> >... and the first line is exactly that - a check that we are on sockfs.
> >_Far_ less expensive one, at that, so it's not even that we are avoiding
> >a costly test. In other words, all masturbation with d_path() is absolutely
> >pointless.
> >
> >Furthermore, it's racy; had been even more so before the delayed fput series
> >went in, but even now it's not safe. fcheck_files() does *NOT* guarantee
> >that file is not getting closed right now. rcu_read_lock() prevents only
> >freeing and potential reuse of struct file we'd got; it might go all the
> >way through final fput() just as we look at it. So file->f_path is not
> >protected by anything. Worse yet, neither is struct socket itself - we
> >might be going through sock_release() at the same time, so sock->sk might
> >very well be NULL, leaving us a oops even after we dump d_path() idiocy.
> >
> >To make it even funnier, there's such thing as SCM_RIGHTS datagrams and
> >descriptor passing. In other words, it's *not* going to catch all sockets
> >that would be caught by the earlier variant.
> >
>
Yes, thank you Al, I should have reviewed this more throughly.
> OK clearly I screwed it up thanks for reviewing Al. How about this.
>
> fdt = files_fdtable(files);
> for (fd = 0; fd < fdt->max_fds; fd++) {
> struct socket *sock;
> int err = 0;
>
> sock = sockfd_lookup(fd, &err);
> if (!sock) {
> lock_sock(sock->sk);
What are you protecting with lock_sock here? The other call sites don't make
use of the socket lock. Arguagbly they could, but I don't think they need to.
As long as the fd doesn't go away we should be able to update the
sk_cgrp_prioidx safely.
Regards
Neil
^ permalink raw reply
* Re: [flame^Wreview] net: netprio_cgroup: rework update socket logic
From: Al Viro @ 2012-08-13 12:18 UTC (permalink / raw)
To: John Fastabend; +Cc: netdev, David Miller, Neil Horman, linux-kernel
In-Reply-To: <50289D7F.3070402@intel.com>
On Sun, Aug 12, 2012 at 11:23:59PM -0700, John Fastabend wrote:
> >OK clearly I screwed it up thanks for reviewing Al. How about this.
> >
> > fdt = files_fdtable(files);
> > for (fd = 0; fd < fdt->max_fds; fd++) {
> > struct socket *sock;
> > int err = 0;
> >
> > sock = sockfd_lookup(fd, &err);
> > if (!sock) {
> typo ^^^^^^
> if (sock) {
>
> to be honest I can't see why I didn't use sockfd_lookup in the first
> place...
Look at sockfd_lookup() arguments and ask yourself - "how could it possibly
guess which descriptor table I want it to look into?" If you want to
kinda-sorta deal with the close() races here, the original loop would
be salvagable by replacing rcu_read_lock() with spin_lock(&files->file_lock);
HOWEVER, it still doesn't address more fundamental problem - somebody
creating a socket and passing it to you in SCM_RIGHTS datagram will
leave you with a socket you can do IO on, still tagged according to who
had created it.
AFAICS, the whole point of that exercise was to allow third-party changing
the priorities of traffic on sockets already created by a process we now
move to a different cgroup. Consider e.g. this:
process A spawns a dozen of children. All children are reading
from the same AF_UNIX socket. Parent listens for requests of some kind
(e.g. it's an httpd, etc.). Once request arrives, it hands it off to
a child, by stuffing some information *and* established connection to
client into SCM_RIGHTS datagram, sends it to shared AF_UNIX socket, closes
the descriptor it got from accept(2) (one it has passed to worker in
SCM_RIGHTS) and moves on. The next child to become free will recvmsg()
on that socket. That will get the thing passed by parent installed into
its descriptor table; resulting descriptor will be found in ancillary
data (see unix(7) and cmsg(3)) and the child goes on to handle request,
using that descriptor to talk to client.
I'm not saying that it's a particulary smart way to implement
worker pools, but it's legitimate and your whole point was to avoid
the need to modify userland code, wasn't it? Now think what'll happen
to that model if you take the whole busily working bunch and move it
to a different cgroup. Nevermind the races, assume that everyone gets
moved very quickly. Both the parent and all children got moved and
their descriptor tables had been walked through by your code. All sockets
present got relabeled... Which does nothing to opened sockets sitting
in the datagrams currently in AF_UNIX socket's queue. They are already
not present in descriptor table of the parent. And they are yet to be
picked by the children...
^ permalink raw reply
* [PATCH net 0/3] llc2 station handler fixes
From: Ben Hutchings @ 2012-08-13 12:48 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: netdev
[-- Attachment #1: Type: text/plain, Size: 673 bytes --]
I was insprired by commit 91d27a8650d5359a7a320daeb35b88cdea15e3a8
('llc: free the right skb') to look into llc_station.c, and didn't like
what I saw.
These are *only* compile tested; hopefully you have some way to test
LLC.
Ben.
Ben Hutchings (3):
llc2: Fix silent failure of llc_station_init()
llc2: Call llc_station_exit() on llc2_init() failure path
llc: Fix races between llc2 handler use and (un)registration
include/net/llc.h | 2 +-
net/llc/af_llc.c | 5 +++--
net/llc/llc_input.c | 21 +++++++++++++++++----
net/llc/llc_station.c | 21 +++------------------
4 files changed, 24 insertions(+), 25 deletions(-)
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply
* [PATCH net 1/3] llc2: Fix silent failure of llc_station_init()
From: Ben Hutchings @ 2012-08-13 12:49 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: netdev
In-Reply-To: <1344862120.824.139.camel@deadeye.wl.decadent.org.uk>
[-- Attachment #1: Type: text/plain, Size: 2284 bytes --]
llc_station_init() creates and processes an event skb with no effect
other than to change the state from DOWN to UP. Allocation failure is
reported, but then ignored by its caller, llc2_init(). Remove this
possibility by simply initialising the state as UP.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
---
include/net/llc.h | 2 +-
net/llc/llc_station.c | 19 ++-----------------
2 files changed, 3 insertions(+), 18 deletions(-)
diff --git a/include/net/llc.h b/include/net/llc.h
index 226c846..f2d0fc5 100644
--- a/include/net/llc.h
+++ b/include/net/llc.h
@@ -133,7 +133,7 @@ extern int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb,
extern void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb);
extern void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_station_init(void);
+extern void llc_station_init(void);
extern void llc_station_exit(void);
#ifdef CONFIG_PROC_FS
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c
index 6828e39..45ddbb9 100644
--- a/net/llc/llc_station.c
+++ b/net/llc/llc_station.c
@@ -687,12 +687,8 @@ static void llc_station_rcv(struct sk_buff *skb)
llc_station_state_process(skb);
}
-int __init llc_station_init(void)
+void __init llc_station_init(void)
{
- int rc = -ENOBUFS;
- struct sk_buff *skb;
- struct llc_station_state_ev *ev;
-
skb_queue_head_init(&llc_main_station.mac_pdu_q);
skb_queue_head_init(&llc_main_station.ev_q.list);
spin_lock_init(&llc_main_station.ev_q.lock);
@@ -700,20 +696,9 @@ int __init llc_station_init(void)
(unsigned long)&llc_main_station);
llc_main_station.ack_timer.expires = jiffies +
sysctl_llc_station_ack_timeout;
- skb = alloc_skb(0, GFP_ATOMIC);
- if (!skb)
- goto out;
- rc = 0;
llc_set_station_handler(llc_station_rcv);
- ev = llc_station_ev(skb);
- memset(ev, 0, sizeof(*ev));
llc_main_station.maximum_retry = 1;
- llc_main_station.state = LLC_STATION_STATE_DOWN;
- ev->type = LLC_STATION_EV_TYPE_SIMPLE;
- ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK;
- rc = llc_station_next_state(skb);
-out:
- return rc;
+ llc_main_station.state = LLC_STATION_STATE_UP;
}
void __exit llc_station_exit(void)
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply related
* [PATCH net 2/3] llc2: Call llc_station_exit() on llc2_init() failure path
From: Ben Hutchings @ 2012-08-13 12:50 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: netdev
In-Reply-To: <1344862120.824.139.camel@deadeye.wl.decadent.org.uk>
[-- Attachment #1: Type: text/plain, Size: 811 bytes --]
Otherwise the station packet handler will remain registered even though
the module is unloaded.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
---
net/llc/af_llc.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index f6fe4d4..8c2919c 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -1206,7 +1206,7 @@ static int __init llc2_init(void)
rc = llc_proc_init();
if (rc != 0) {
printk(llc_proc_err_msg);
- goto out_unregister_llc_proto;
+ goto out_station;
}
rc = llc_sysctl_init();
if (rc) {
@@ -1226,7 +1226,8 @@ out_sysctl:
llc_sysctl_exit();
out_proc:
llc_proc_exit();
-out_unregister_llc_proto:
+out_station:
+ llc_station_exit();
proto_unregister(&llc_proto);
goto out;
}
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply related
* [PATCH net 3/3] llc: Fix races between llc2 handler use and (un)registration
From: Ben Hutchings @ 2012-08-13 12:50 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: netdev
In-Reply-To: <1344862120.824.139.camel@deadeye.wl.decadent.org.uk>
[-- Attachment #1: Type: text/plain, Size: 3199 bytes --]
When registering the handlers, any state they rely on must be
completely initialised first. When unregistering, we must wait until
they are definitely no longer running. llc_rcv() must also avoid
reading the handler pointers again after checking for NULL.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
---
net/llc/llc_input.c | 21 +++++++++++++++++----
net/llc/llc_station.c | 2 +-
2 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c
index e32cab4..dd3e833 100644
--- a/net/llc/llc_input.c
+++ b/net/llc/llc_input.c
@@ -42,6 +42,7 @@ static void (*llc_type_handlers[2])(struct llc_sap *sap,
void llc_add_pack(int type, void (*handler)(struct llc_sap *sap,
struct sk_buff *skb))
{
+ smp_wmb(); /* ensure initialisation is complete before it's called */
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = handler;
}
@@ -50,11 +51,19 @@ void llc_remove_pack(int type)
{
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = NULL;
+ synchronize_net();
}
void llc_set_station_handler(void (*handler)(struct sk_buff *skb))
{
+ /* Ensure initialisation is complete before it's called */
+ if (handler)
+ smp_wmb();
+
llc_station_handler = handler;
+
+ if (!handler)
+ synchronize_net();
}
/**
@@ -150,6 +159,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
int dest;
int (*rcv)(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
+ void (*sta_handler)(struct sk_buff *skb);
+ void (*sap_handler)(struct llc_sap *sap, struct sk_buff *skb);
if (!net_eq(dev_net(dev), &init_net))
goto drop;
@@ -182,7 +193,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
*/
rcv = rcu_dereference(sap->rcv_func);
dest = llc_pdu_type(skb);
- if (unlikely(!dest || !llc_type_handlers[dest - 1])) {
+ sap_handler = dest ? ACCESS_ONCE(llc_type_handlers[dest - 1]) : NULL;
+ if (unlikely(!sap_handler)) {
if (rcv)
rcv(skb, dev, pt, orig_dev);
else
@@ -193,7 +205,7 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
if (cskb)
rcv(cskb, dev, pt, orig_dev);
}
- llc_type_handlers[dest - 1](sap, skb);
+ sap_handler(sap, skb);
}
llc_sap_put(sap);
out:
@@ -202,9 +214,10 @@ drop:
kfree_skb(skb);
goto out;
handle_station:
- if (!llc_station_handler)
+ sta_handler = ACCESS_ONCE(llc_station_handler);
+ if (!sta_handler)
goto drop;
- llc_station_handler(skb);
+ sta_handler(skb);
goto out;
}
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c
index 45ddbb9..81da71a 100644
--- a/net/llc/llc_station.c
+++ b/net/llc/llc_station.c
@@ -696,9 +696,9 @@ void __init llc_station_init(void)
(unsigned long)&llc_main_station);
llc_main_station.ack_timer.expires = jiffies +
sysctl_llc_station_ack_timeout;
- llc_set_station_handler(llc_station_rcv);
llc_main_station.maximum_retry = 1;
llc_main_station.state = LLC_STATION_STATE_UP;
+ llc_set_station_handler(llc_station_rcv);
}
void __exit llc_station_exit(void)
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply related
* Re: [PATCHv2 1/4] modem_shm: Add Modem Access Framework
From: Linus Walleij @ 2012-08-13 13:33 UTC (permalink / raw)
To: Alan Cox, Greg KH
Cc: Arun MURTHY, linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-doc@vger.kernel.org, Sjur BRENDELAND
In-Reply-To: <20120809123855.1ba0fffb@pyramind.ukuu.org.uk>
On Thu, Aug 9, 2012 at 1:38 PM, Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
>> Maybe tty can do this, but want to have all modem related separately.
>> There are many such hardware and many drivers coming up in near future.
>
> tty can't do all this. We have similar things with stuff like CAIF. Modem
> has gone from pretending to be a serial port (tty) to appearing as a
> smart multi-function controller.
I second this, I plead guilty to suggesting to use "modem_shm"
as a layer below because things like CAIF and Phonet are just
growing and growing.
Sure, many of them can emulate AT commands, but that is
not their native tongue, and increasingly any AT command parser
is constrained to userspace which in turn constructs the actually
intended IPC command sequency for doing ATDT555784629568.
These modems are not talking anything resembling a line
discipline, they have their own binary protocol and above are
protocols like CAIF and Phonet talking to the modem, including
IPC to processes running on the other sides, with states.
Sjur is currently working on a future platform where we'll attempt
to bring things closer to existing Linux frameworks using virtio
rings and rpmesg also on the modem side, but the modems Arun
are working on are already deployed with this type of firmware
talking some other IPC lingo.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v3] can: kvaser_usb: Add support for Kvaser CAN/USB devices
From: Olivier Sobrie @ 2012-08-13 13:51 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, linux-usb
Cc: netdev, Olivier Sobrie
In-Reply-To: <1343626352-24760-1-git-send-email-olivier@sobrie.be>
This driver provides support for several Kvaser CAN/USB devices.
Such kind of devices supports up to three CAN network interfaces.
It has been tested with a Kvaser USB Leaf Light (one network interface)
connected to a pch_can interface.
The firmware version of the Kvaser device was 2.5.205.
List of Kvaser devices supported by the driver:
- Kvaser Leaf prototype (P010v2 and v3)
- Kvaser Leaf Light (P010v3)
- Kvaser Leaf Professional HS
- Kvaser Leaf SemiPro HS
- Kvaser Leaf Professional LS
- Kvaser Leaf Professional SWC
- Kvaser Leaf Professional LIN
- Kvaser Leaf SemiPro LS
- Kvaser Leaf SemiPro SWC
- Kvaser Memorator II, Prototype
- Kvaser Memorator II HS/HS
- Kvaser USBcan Professional HS/HS
- Kvaser Leaf Light GI
- Kvaser Leaf Professional HS (OBD-II connector)
- Kvaser Memorator Professional HS/LS
- Kvaser Leaf Light "China"
- Kvaser BlackBird SemiPro
- Kvaser OEM Mercury
- Kvaser OEM Leaf
- Kvaser USBcan R
Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
Changes since v2:
- update of error handling
- add method do_get_berr_counter if hardware support it
- fix race condition in probe function
- ...
Here is the behavior in case of errors:
Case 1: restart-ms 0 + short-circuit
t0: short-circuit + cansend can1 123@1122
(016.731173) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000009) can1 20000088 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000010) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000005) can1 20000088 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000004) can1 20000088 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000007) can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
(000.001062) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000011) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000008) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000978) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000009) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000008) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.001108) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.001082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000046) can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
t1: ip link set can1 type can restart:
(125.211773) can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
(000.020207) can1 20000088 [8] 00 00 40 00 00 00 00 00 ERRORFRAME
Case 2: short-circuit + restart-ms > 0
t0: short-circuit + cansend can1 123@1122
(006.079601) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000091) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000086) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
(000.000352) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000103) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000106) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000105) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000808) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000125) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000110) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000108) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000106) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000110) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000560) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000104) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000104) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000106) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000123) can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.010835) can1 20000188 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
(000.000090) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
...
(000.000084) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
(000.000431) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
...
(000.000105) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000112) can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.010842) can1 20000188 [8] 00 00 D0 00 00 00 00 00 ERRORFRAME
...
(000.001118) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000086) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000084) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000084) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000085) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 2000008C [8] 00 30 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000349) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000087) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000081) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000903) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000110) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000093) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000083) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000755) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000112) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000093) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000084) can1 20000088 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
(000.000082) can1 200000C8 [8] 00 00 90 00 00 00 00 00 ERRORFRAME
t1: short-circuit removed
(000.011273) can1 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
(000.000006) can1 123 [2] 11 22
Case 3: CAN-H and CAN-L disconnected
t0: CAN-H and CAN-L disconnect + cansend can1 123#1122
(524.620978) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000978) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001126) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000007) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000996) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001134) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001775) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000006) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000310) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000005) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001126) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000005) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.001176) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(000.000006) can1 2000008C [8] 00 30 80 19 00 00 00 00 ERRORFRAME
(000.000949) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
...
t1: CAN-H and CAN-L connected:
(000.000005) can1 20000088 [8] 00 00 80 19 00 00 00 00 ERRORFRAME
(004.270502) can1 123 [2] 11 22
(002.242773) can1 20000088 [8] 00 00 40 00 00 00 00 00 ERRORFRAME
Remarks:
- With my device the txerr and rxerr always stay equals to 0
because the hardware doesn't report it... It passes from active state
to passive and then off.
With other hardware, i.e. other than Leaf Light, the txerr and rxerr
are incremented (following Kvaser support)
- With case 2, sometimes after a bus off event, the hardware doesn't
report that it's in active state again... This is why when I get an
acknowledge frame I deduce it's active again...
Kind regards,
Olivier
drivers/net/can/usb/Kconfig | 33 +
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/kvaser_usb.c | 1525 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1559 insertions(+)
create mode 100644 drivers/net/can/usb/kvaser_usb.c
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 0a68768..578955f 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -13,6 +13,39 @@ config CAN_ESD_USB2
This driver supports the CAN-USB/2 interface
from esd electronic system design gmbh (http://www.esd.eu).
+config CAN_KVASER_USB
+ tristate "Kvaser CAN/USB interface"
+ ---help---
+ This driver adds support for Kvaser CAN/USB devices like Kvaser
+ Leaf Light.
+
+ The driver gives support for the following devices:
+ - Kvaser Leaf prototype (P010v2 and v3)
+ - Kvaser Leaf Light (P010v3)
+ - Kvaser Leaf Professional HS
+ - Kvaser Leaf SemiPro HS
+ - Kvaser Leaf Professional LS
+ - Kvaser Leaf Professional SWC
+ - Kvaser Leaf Professional LIN
+ - Kvaser Leaf SemiPro LS
+ - Kvaser Leaf SemiPro SWC
+ - Kvaser Memorator II, Prototype
+ - Kvaser Memorator II HS/HS
+ - Kvaser USBcan Professional HS/HS
+ - Kvaser Leaf Light GI
+ - Kvaser Leaf Professional HS (OBD-II connector)
+ - Kvaser Memorator Professional HS/LS
+ - Kvaser Leaf Light "China"
+ - Kvaser BlackBird SemiPro
+ - Kvaser OEM Mercury
+ - Kvaser OEM Leaf
+ - Kvaser USBcan R
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called kvaser_usb.
+
config CAN_PEAK_USB
tristate "PEAK PCAN-USB/USB Pro interfaces"
---help---
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index da6d1d3..80a2ee4 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
new file mode 100644
index 0000000..938f188
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -0,0 +1,1525 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * Parts of this driver are based on the following:
+ * - Kvaser linux leaf driver (version 4.78)
+ * - CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved.
+ * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh
+ * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be>
+ */
+
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#define MAX_TX_URBS 16
+#define MAX_RX_URBS 4
+#define START_TIMEOUT 1000 /* msecs */
+#define STOP_TIMEOUT 1000 /* msecs */
+#define USB_SEND_TIMEOUT 1000 /* msecs */
+#define USB_RECV_TIMEOUT 1000 /* msecs */
+#define RX_BUFFER_SIZE 3072
+#define CAN_USB_CLOCK 8000000
+#define MAX_NET_DEVICES 3
+
+/* Kvaser USB devices */
+#define KVASER_VENDOR_ID 0x0bfd
+#define USB_LEAF_DEVEL_PRODUCT_ID 10
+#define USB_LEAF_LITE_PRODUCT_ID 11
+#define USB_LEAF_PRO_PRODUCT_ID 12
+#define USB_LEAF_SPRO_PRODUCT_ID 14
+#define USB_LEAF_PRO_LS_PRODUCT_ID 15
+#define USB_LEAF_PRO_SWC_PRODUCT_ID 16
+#define USB_LEAF_PRO_LIN_PRODUCT_ID 17
+#define USB_LEAF_SPRO_LS_PRODUCT_ID 18
+#define USB_LEAF_SPRO_SWC_PRODUCT_ID 19
+#define USB_MEMO2_DEVEL_PRODUCT_ID 22
+#define USB_MEMO2_HSHS_PRODUCT_ID 23
+#define USB_UPRO_HSHS_PRODUCT_ID 24
+#define USB_LEAF_LITE_GI_PRODUCT_ID 25
+#define USB_LEAF_PRO_OBDII_PRODUCT_ID 26
+#define USB_MEMO2_HSLS_PRODUCT_ID 27
+#define USB_LEAF_LITE_CH_PRODUCT_ID 28
+#define USB_BLACKBIRD_SPRO_PRODUCT_ID 29
+#define USB_OEM_MERCURY_PRODUCT_ID 34
+#define USB_OEM_LEAF_PRODUCT_ID 35
+#define USB_CAN_R_PRODUCT_ID 39
+
+/* USB devices features */
+#define KVASER_HAS_SILENT_MODE BIT(0)
+#define KVASER_HAS_TXRX_ERRORS BIT(1)
+
+/* Message header size */
+#define MSG_HEADER_LEN 2
+
+/* Can message flags */
+#define MSG_FLAG_ERROR_FRAME BIT(0)
+#define MSG_FLAG_OVERRUN BIT(1)
+#define MSG_FLAG_NERR BIT(2)
+#define MSG_FLAG_WAKEUP BIT(3)
+#define MSG_FLAG_REMOTE_FRAME BIT(4)
+#define MSG_FLAG_RESERVED BIT(5)
+#define MSG_FLAG_TX_ACK BIT(6)
+#define MSG_FLAG_TX_REQUEST BIT(7)
+
+/* Can states */
+#define M16C_STATE_BUS_RESET BIT(0)
+#define M16C_STATE_BUS_ERROR BIT(4)
+#define M16C_STATE_BUS_PASSIVE BIT(5)
+#define M16C_STATE_BUS_OFF BIT(6)
+
+/* Can msg ids */
+#define CMD_RX_STD_MESSAGE 12
+#define CMD_TX_STD_MESSAGE 13
+#define CMD_RX_EXT_MESSAGE 14
+#define CMD_TX_EXT_MESSAGE 15
+#define CMD_SET_BUS_PARAMS 16
+#define CMD_GET_BUS_PARAMS 17
+#define CMD_GET_BUS_PARAMS_REPLY 18
+#define CMD_GET_CHIP_STATE 19
+#define CMD_CHIP_STATE_EVENT 20
+#define CMD_SET_CTRL_MODE 21
+#define CMD_GET_CTRL_MODE 22
+#define CMD_GET_CTRL_MODE_REPLY 23
+#define CMD_RESET_CHIP 24
+#define CMD_RESET_CARD 25
+#define CMD_START_CHIP 26
+#define CMD_START_CHIP_REPLY 27
+#define CMD_STOP_CHIP 28
+#define CMD_STOP_CHIP_REPLY 29
+#define CMD_GET_CARD_INFO2 32
+#define CMD_GET_CARD_INFO 34
+#define CMD_GET_CARD_INFO_REPLY 35
+#define CMD_GET_SOFTWARE_INFO 38
+#define CMD_GET_SOFTWARE_INFO_REPLY 39
+#define CMD_ERROR_EVENT 45
+#define CMD_FLUSH_QUEUE 48
+#define CMD_RESET_ERROR_COUNTER 49
+#define CMD_TX_ACKNOWLEDGE 50
+#define CMD_CAN_ERROR_EVENT 51
+#define CMD_USB_THROTTLE 77
+
+/* error factors */
+#define M16C_EF_ACKE BIT(0)
+#define M16C_EF_CRCE BIT(1)
+#define M16C_EF_FORME BIT(2)
+#define M16C_EF_STFE BIT(3)
+#define M16C_EF_BITE0 BIT(4)
+#define M16C_EF_BITE1 BIT(5)
+#define M16C_EF_RCVE BIT(6)
+#define M16C_EF_TRE BIT(7)
+
+/* bittiming parameters */
+#define KVASER_USB_TSEG1_MIN 1
+#define KVASER_USB_TSEG1_MAX 16
+#define KVASER_USB_TSEG2_MIN 1
+#define KVASER_USB_TSEG2_MAX 8
+#define KVASER_USB_SJW_MAX 4
+#define KVASER_USB_BRP_MIN 1
+#define KVASER_USB_BRP_MAX 64
+#define KVASER_USB_BRP_INC 1
+
+/* ctrl modes */
+#define KVASER_CTRL_MODE_NORMAL 1
+#define KVASER_CTRL_MODE_SILENT 2
+#define KVASER_CTRL_MODE_SELFRECEPTION 3
+#define KVASER_CTRL_MODE_OFF 4
+
+struct kvaser_msg_simple {
+ u8 tid;
+ u8 channel;
+} __packed;
+
+struct kvaser_msg_cardinfo {
+ u8 tid;
+ u8 nchannels;
+ __le32 serial_number;
+ __le32 padding;
+ __le32 clock_resolution;
+ __le32 mfgdate;
+ u8 ean[8];
+ u8 hw_revision;
+ u8 usb_hs_mode;
+ __le16 padding2;
+} __packed;
+
+struct kvaser_msg_cardinfo2 {
+ u8 tid;
+ u8 channel;
+ u8 pcb_id[24];
+ __le32 oem_unlock_code;
+} __packed;
+
+struct kvaser_msg_softinfo {
+ u8 tid;
+ u8 channel;
+ __le32 sw_options;
+ __le32 fw_version;
+ __le16 max_outstanding_tx;
+ __le16 padding[9];
+} __packed;
+
+struct kvaser_msg_busparams {
+ u8 tid;
+ u8 channel;
+ __le32 bitrate;
+ u8 tseg1;
+ u8 tseg2;
+ u8 sjw;
+ u8 no_samp;
+} __packed;
+
+struct kvaser_msg_tx_can {
+ u8 channel;
+ u8 tid;
+ u8 msg[14];
+ u8 padding;
+ u8 flags;
+} __packed;
+
+struct kvaser_msg_rx_can {
+ u8 channel;
+ u8 flag;
+ __le16 time[3];
+ u8 msg[14];
+} __packed;
+
+struct kvaser_msg_chip_state_event {
+ u8 tid;
+ u8 channel;
+ __le16 time[3];
+ u8 tx_errors_count;
+ u8 rx_errors_count;
+ u8 status;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_tx_acknowledge {
+ u8 channel;
+ u8 tid;
+ __le16 time[3];
+ u8 flags;
+ u8 time_offset;
+} __packed;
+
+struct kvaser_msg_error_event {
+ u8 tid;
+ u8 flags;
+ __le16 time[3];
+ u8 channel;
+ u8 padding;
+ u8 tx_errors_count;
+ u8 rx_errors_count;
+ u8 status;
+ u8 error_factor;
+} __packed;
+
+struct kvaser_msg_ctrl_mode {
+ u8 tid;
+ u8 channel;
+ u8 ctrl_mode;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_flush_queue {
+ u8 tid;
+ u8 channel;
+ u8 flags;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg {
+ u8 len;
+ u8 id;
+ union {
+ struct kvaser_msg_simple simple;
+ struct kvaser_msg_cardinfo cardinfo;
+ struct kvaser_msg_cardinfo2 cardinfo2;
+ struct kvaser_msg_softinfo softinfo;
+ struct kvaser_msg_busparams busparams;
+ struct kvaser_msg_tx_can tx_can;
+ struct kvaser_msg_rx_can rx_can;
+ struct kvaser_msg_chip_state_event chip_state_event;
+ struct kvaser_msg_tx_acknowledge tx_acknowledge;
+ struct kvaser_msg_error_event error_event;
+ struct kvaser_msg_ctrl_mode ctrl_mode;
+ struct kvaser_msg_flush_queue flush_queue;
+ } u;
+} __packed;
+
+struct kvaser_usb_tx_urb_context {
+ struct kvaser_usb_net_priv *priv;
+ u32 echo_index;
+ int dlc;
+};
+
+struct kvaser_usb {
+ struct usb_device *udev;
+ struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
+
+ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
+ struct usb_anchor rx_submitted;
+
+ u32 fw_version;
+ unsigned int nchannels;
+
+ bool rxinitdone;
+ void *rxbuf[MAX_RX_URBS];
+ dma_addr_t rxbuf_dma[MAX_RX_URBS];
+};
+
+struct kvaser_usb_net_priv {
+ struct can_priv can;
+
+ atomic_t active_tx_urbs;
+ struct usb_anchor tx_submitted;
+ struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ struct completion start_comp, stop_comp;
+
+ struct kvaser_usb *dev;
+ struct net_device *netdev;
+ int channel;
+
+ struct can_berr_counter bec;
+};
+
+static struct usb_device_id kvaser_usb_table[] = {
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
+
+static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
+ struct kvaser_msg *msg)
+{
+ int actual_len;
+
+ return usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ msg, msg->len, &actual_len,
+ USB_SEND_TIMEOUT);
+}
+
+static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
+ struct kvaser_msg *msg)
+{
+ struct kvaser_msg *tmp;
+ void *buf;
+ int actual_len;
+ int err;
+ int pos = 0;
+
+ buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE, &actual_len,
+ USB_RECV_TIMEOUT);
+ if (err < 0)
+ goto end;
+
+ while (pos <= actual_len - MSG_HEADER_LEN) {
+ tmp = buf + pos;
+
+ if (!tmp->len)
+ break;
+
+ if (pos + tmp->len > actual_len) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ if (tmp->id == id) {
+ memcpy(msg, tmp, tmp->len);
+ goto end;
+ }
+
+ pos += tmp->len;
+ }
+
+ err = -EINVAL;
+
+end:
+ kfree(buf);
+
+ return err;
+}
+
+static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
+ u8 msg_id, int channel)
+{
+ struct kvaser_msg msg = {
+ .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
+ .id = msg_id,
+ .u.simple.channel = channel,
+ .u.simple.tid = 0xff,
+ };
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
+
+ return 0;
+}
+
+static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->nchannels = msg.u.cardinfo.nchannels;
+
+ return 0;
+}
+
+static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct net_device_stats *stats;
+ struct kvaser_usb_tx_urb_context *context;
+ struct kvaser_usb_net_priv *priv;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ u8 channel = msg->u.tx_acknowledge.channel;
+ u8 tid = msg->u.tx_acknowledge.tid;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ if (!netif_device_present(priv->netdev))
+ return;
+
+ stats = &priv->netdev->stats;
+
+ context = &priv->tx_contexts[tid % MAX_TX_URBS];
+
+ /* Sometimes the state change doesn't come after a bus-off event */
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF)) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (skb) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ } else {
+ netdev_err(priv->netdev,
+ "No memory left for err_skb\n");
+ }
+
+ priv->can.can_stats.restarts++;
+ netif_carrier_on(priv->netdev);
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ stats->tx_packets++;
+ stats->tx_bytes += context->dlc;
+ can_get_echo_skb(priv->netdev, context->echo_index);
+
+ context->echo_index = MAX_TX_URBS;
+ atomic_dec(&priv->active_tx_urbs);
+
+ netif_wake_queue(priv->netdev);
+}
+
+static void kvaser_usb_simple_msg_callback(struct urb *urb)
+{
+ struct net_device *netdev = urb->context;
+
+ kfree(urb->transfer_buffer);
+
+ if (urb->status)
+ netdev_warn(netdev, "urb status received: %d\n",
+ urb->status);
+}
+
+static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
+ u8 msg_id)
+{
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device *netdev = priv->netdev;
+ struct kvaser_msg *msg;
+ struct urb *urb;
+ void *buf;
+ int err;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ return -ENOMEM;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ return -ENOMEM;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->id = msg_id;
+ msg->u.simple.channel = priv->channel;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_simple_msg_callback, priv);
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ netdev_err(netdev, "Error transmitting URB\n");
+ usb_unanchor_urb(urb);
+ kfree(buf);
+ return err;
+ }
+
+ usb_free_urb(urb);
+
+ return 0;
+}
+
+static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
+{
+ int i;
+
+ usb_kill_anchored_urbs(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < MAX_TX_URBS; i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+}
+
+static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ struct kvaser_usb_net_priv *priv;
+ unsigned int new_state;
+ u8 channel, status, txerr, rxerr;
+
+ if (msg->id == CMD_CAN_ERROR_EVENT) {
+ channel = msg->u.error_event.channel;
+ status = msg->u.error_event.status;
+ txerr = msg->u.error_event.tx_errors_count;
+ rxerr = msg->u.error_event.rx_errors_count;
+ } else {
+ channel = msg->u.chip_state_event.channel;
+ status = msg->u.chip_state_event.status;
+ txerr = msg->u.chip_state_event.tx_errors_count;
+ rxerr = msg->u.chip_state_event.rx_errors_count;
+ }
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (status & M16C_STATE_BUS_RESET) {
+ kvaser_usb_unlink_tx_urbs(priv);
+ return;
+ }
+
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ cf->can_id |= CAN_ERR_BUSERROR;
+
+ new_state = priv->can.state;
+
+ netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
+
+ if (status & M16C_STATE_BUS_OFF) {
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ priv->can.can_stats.bus_off++;
+ if (!priv->can.restart_ms)
+ kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
+
+ netif_carrier_off(priv->netdev);
+
+ new_state = CAN_STATE_BUS_OFF;
+ }
+
+ if (status & M16C_STATE_BUS_PASSIVE) {
+ if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
+ cf->can_id |= CAN_ERR_CRTL;
+
+ if ((txerr > 0) || (rxerr > 0))
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_PASSIVE
+ : CAN_ERR_CRTL_RX_PASSIVE;
+ else
+ cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
+ CAN_ERR_CRTL_RX_PASSIVE;
+
+ priv->can.can_stats.error_passive++;
+ }
+
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ }
+
+ if (status == M16C_STATE_BUS_ERROR) {
+ if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
+ ((txerr > 96) || (rxerr > 96))) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_WARNING
+ : CAN_ERR_CRTL_RX_WARNING;
+
+ priv->can.can_stats.error_warning++;
+ new_state = CAN_STATE_ERROR_WARNING;
+ } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+ }
+
+ if (!status) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF) &&
+ (new_state < CAN_STATE_BUS_OFF)) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_carrier_on(priv->netdev);
+
+ priv->can.can_stats.restarts++;
+ }
+
+ if (msg->id == CMD_CAN_ERROR_EVENT) {
+ u8 error_factor = msg->u.error_event.error_factor;
+
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ if (error_factor)
+ cf->can_id |= CAN_ERR_PROT;
+
+ if (error_factor & M16C_EF_ACKE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
+ if (error_factor & M16C_EF_CRCE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL);
+ if (error_factor & M16C_EF_FORME)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ if (error_factor & M16C_EF_STFE)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ if (error_factor & M16C_EF_BITE0)
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ if (error_factor & M16C_EF_BITE1)
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ if (error_factor & M16C_EF_TRE)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+
+ priv->bec.txerr = txerr;
+ priv->bec.rxerr = rxerr;
+
+ priv->can.state = new_state;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ u8 channel = msg->u.rx_can.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ skb = alloc_can_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
+ (msg->u.rx_can.msg[1] & 0x3f);
+ cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
+
+ if (msg->id == CMD_RX_EXT_MESSAGE) {
+ cf->can_id <<= 18;
+ cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
+ ((msg->u.rx_can.msg[3] & 0xff) << 6) |
+ (msg->u.rx_can.msg[4] & 0x3f);
+ cf->can_id |= CAN_EFF_FLAG;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
+ cf->can_id |= CAN_RTR_FLAG;
+ } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
+ MSG_FLAG_NERR)) {
+ cf->can_id |= CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
+ msg->u.rx_can.flag);
+
+ stats->rx_errors++;
+ } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+ cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ cf->can_dlc = CAN_ERR_DLC;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ } else if (!msg->u.rx_can.flag) {
+ memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
+ } else {
+ kfree_skb(skb);
+ return;
+ }
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ if (completion_done(&priv->start_comp) &&
+ netif_queue_stopped(priv->netdev)) {
+ netif_wake_queue(priv->netdev);
+ } else {
+ netif_start_queue(priv->netdev);
+ complete(&priv->start_comp);
+ }
+}
+
+static void kvaser_usb_stop_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ complete(&priv->stop_comp);
+}
+
+static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ switch (msg->id) {
+ case CMD_START_CHIP_REPLY:
+ kvaser_usb_start_chip_reply(dev, msg);
+ break;
+
+ case CMD_STOP_CHIP_REPLY:
+ kvaser_usb_stop_chip_reply(dev, msg);
+ break;
+
+ case CMD_RX_STD_MESSAGE:
+ case CMD_RX_EXT_MESSAGE:
+ kvaser_usb_rx_can_msg(dev, msg);
+ break;
+
+ case CMD_CHIP_STATE_EVENT:
+ case CMD_CAN_ERROR_EVENT:
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_TX_ACKNOWLEDGE:
+ kvaser_usb_tx_acknowledge(dev, msg);
+ break;
+
+ default:
+ dev_warn(dev->udev->dev.parent,
+ "Unhandled message (%d)\n", msg->id);
+ break;
+ }
+}
+
+static void kvaser_usb_read_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb *dev = urb->context;
+ struct kvaser_msg *msg;
+ int pos = 0;
+ int err, i;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default:
+ dev_info(dev->udev->dev.parent, "Rx URB aborted (%d)\n",
+ urb->status);
+ goto resubmit_urb;
+ }
+
+ while (pos <= urb->actual_length - MSG_HEADER_LEN) {
+ msg = urb->transfer_buffer + pos;
+
+ if (!msg->len)
+ break;
+
+ if (pos + msg->len > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ kvaser_usb_handle_message(dev, msg);
+
+ pos += msg->len;
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ urb->transfer_buffer, RX_BUFFER_SIZE,
+ kvaser_usb_read_bulk_callback, dev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err == -ENODEV) {
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ netif_device_detach(dev->nets[i]->netdev);
+ }
+ } else if (err) {
+ dev_err(dev->udev->dev.parent,
+ "Failed resubmitting read bulk urb: %d\n", err);
+ }
+
+ return;
+}
+
+static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev)
+{
+ int i, err = 0;
+
+ if (dev->rxinitdone)
+ return 0;
+
+ for (i = 0; i < MAX_RX_URBS; i++) {
+ struct urb *urb = NULL;
+ u8 *buf = NULL;
+ dma_addr_t buf_dma;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for URBs\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
+ GFP_KERNEL, &buf_dma);
+ if (!buf) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ err = -ENOMEM;
+ break;
+ }
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE,
+ kvaser_usb_read_bulk_callback,
+ dev);
+ urb->transfer_dma = buf_dma;
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ usb_unanchor_urb(urb);
+ usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf,
+ buf_dma);
+ break;
+ }
+
+ dev->rxbuf[i] = buf;
+ dev->rxbuf_dma[i] = buf_dma;
+
+ usb_free_urb(urb);
+ }
+
+ if (i == 0) {
+ dev_warn(dev->udev->dev.parent,
+ "Cannot setup read URBs, error %d\n", err);
+ return err;
+ } else if (i < MAX_RX_URBS) {
+ dev_warn(dev->udev->dev.parent,
+ "RX performances may be slow\n");
+ }
+
+ dev->rxinitdone = true;
+
+ return 0;
+}
+
+static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_SET_CTRL_MODE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_ctrl_mode),
+ .u.ctrl_mode.tid = 0xff,
+ .u.ctrl_mode.channel = priv->channel,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ else
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->start_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->start_comp,
+ msecs_to_jiffies(START_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_open(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ err = kvaser_usb_setup_rx_urbs(dev);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_set_opt_mode(priv);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_start_chip(priv);
+ if (err) {
+ netdev_warn(netdev, "Cannot start device, error %d\n", err);
+ goto error;
+ }
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+
+error:
+ close_candev(netdev);
+ return err;
+}
+
+static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
+{
+ int i;
+
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+
+ for (i = 0; i < MAX_RX_URBS; i++)
+ usb_free_coherent(dev->udev, RX_BUFFER_SIZE,
+ dev->rxbuf[i],
+ dev->rxbuf_dma[i]);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++) {
+ struct kvaser_usb_net_priv *priv = dev->nets[i];
+
+ if (priv)
+ kvaser_usb_unlink_tx_urbs(priv);
+ }
+}
+
+static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->stop_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->stop_comp,
+ msecs_to_jiffies(STOP_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_FLUSH_QUEUE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_flush_queue),
+ .u.flush_queue.channel = priv->channel,
+ .u.flush_queue.flags = 0x00,
+ };
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_close(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ netif_stop_queue(netdev);
+
+ err = kvaser_usb_flush_queue(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
+
+ if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
+ netdev_warn(netdev, "Cannot reset card, error %d\n", err);
+
+ err = kvaser_usb_stop_chip(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot stop device, error %d\n", err);
+
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(priv->netdev);
+
+ return 0;
+}
+
+static void kvaser_usb_write_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb_tx_urb_context *context = urb->context;
+ struct kvaser_usb_net_priv *priv;
+ struct net_device *netdev;
+
+ if (WARN_ON(!context))
+ return;
+
+ priv = context->priv;
+ netdev = priv->netdev;
+
+ kfree(urb->transfer_buffer);
+
+ if (!netif_device_present(netdev))
+ return;
+
+ if (urb->status)
+ netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
+}
+
+static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct kvaser_usb_tx_urb_context *context = NULL;
+ struct urb *urb;
+ void *buf;
+ struct kvaser_msg *msg;
+ int i, err;
+ int ret = NETDEV_TX_OK;
+
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ goto nobufmem;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
+ msg->u.tx_can.flags = 0;
+ msg->u.tx_can.channel = priv->channel;
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ msg->id = CMD_TX_EXT_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
+ msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
+ msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
+ msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
+ msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
+ } else {
+ msg->id = CMD_TX_STD_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
+ msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
+ }
+
+ msg->u.tx_can.msg[5] = cf->can_dlc;
+ memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
+ if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &priv->tx_contexts[i];
+ break;
+ }
+ }
+
+ if (!context) {
+ netdev_warn(netdev, "cannot find free context\n");
+ ret = NETDEV_TX_BUSY;
+ goto releasebuf;
+ }
+
+ context->priv = priv;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ msg->u.tx_can.tid = context->echo_index;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_write_bulk_callback, context);
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ can_put_echo_skb(skb, netdev, context->echo_index);
+
+ atomic_inc(&priv->active_tx_urbs);
+
+ if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
+ netif_stop_queue(netdev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ atomic_dec(&priv->active_tx_urbs);
+ usb_unanchor_urb(urb);
+
+ stats->tx_dropped++;
+
+ if (err == -ENODEV)
+ netif_device_detach(netdev);
+ else
+ netdev_warn(netdev, "Failed tx_urb %d\n", err);
+
+ goto releasebuf;
+ }
+
+ netdev->trans_start = jiffies;
+
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ kfree(buf);
+nobufmem:
+ usb_free_urb(urb);
+ return ret;
+}
+
+static const struct net_device_ops kvaser_usb_netdev_ops = {
+ .ndo_open = kvaser_usb_open,
+ .ndo_stop = kvaser_usb_close,
+ .ndo_start_xmit = kvaser_usb_start_xmit,
+};
+
+static struct can_bittiming_const kvaser_usb_bittiming_const = {
+ .name = "kvaser_usb",
+ .tseg1_min = KVASER_USB_TSEG1_MIN,
+ .tseg1_max = KVASER_USB_TSEG1_MAX,
+ .tseg2_min = KVASER_USB_TSEG2_MIN,
+ .tseg2_max = KVASER_USB_TSEG2_MAX,
+ .sjw_max = KVASER_USB_SJW_MAX,
+ .brp_min = KVASER_USB_BRP_MIN,
+ .brp_max = KVASER_USB_BRP_MAX,
+ .brp_inc = KVASER_USB_BRP_INC,
+};
+
+static int kvaser_usb_set_bittiming(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct kvaser_usb *dev = priv->dev;
+ struct kvaser_msg msg = {
+ .id = CMD_SET_BUS_PARAMS,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_busparams),
+ .u.busparams.channel = priv->channel,
+ .u.busparams.tid = 0xff,
+ .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
+ .u.busparams.sjw = bt->sjw,
+ .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
+ .u.busparams.tseg2 = bt->phase_seg2,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ msg.u.busparams.no_samp = 3;
+ else
+ msg.u.busparams.no_samp = 1;
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_set_mode(struct net_device *netdev,
+ enum can_mode mode)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ int err;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
+ struct can_berr_counter *bec)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+
+ *bec = priv->bec;
+
+ return 0;
+}
+
+static int kvaser_usb_init_one(struct usb_interface *intf,
+ const struct usb_device_id *id, int channel)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ struct kvaser_usb_net_priv *priv;
+ int i, err;
+
+ netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "Cannot alloc candev\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(netdev);
+
+ init_completion(&priv->start_comp);
+ init_completion(&priv->stop_comp);
+
+ init_usb_anchor(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ priv->dev = dev;
+ priv->netdev = netdev;
+ priv->channel = channel;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->can.clock.freq = CAN_USB_CLOCK;
+ priv->can.bittiming_const = &kvaser_usb_bittiming_const;
+ priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
+ priv->can.do_set_mode = kvaser_usb_set_mode;
+ if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
+ priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ if (id->driver_info & KVASER_HAS_SILENT_MODE)
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+ netdev->flags |= IFF_ECHO;
+
+ netdev->netdev_ops = &kvaser_usb_netdev_ops;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ dev->nets[channel] = priv;
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to register can device\n");
+ free_candev(netdev);
+ return err;
+ }
+
+ netdev_dbg(netdev, "device registered\n");
+
+ return 0;
+}
+
+static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
+ struct usb_endpoint_descriptor **in,
+ struct usb_endpoint_descriptor **out)
+{
+ const struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ iface_desc = &intf->altsetting[0];
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint))
+ *in = endpoint;
+
+ if (usb_endpoint_is_bulk_out(endpoint))
+ *out = endpoint;
+ }
+}
+
+static int kvaser_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct kvaser_usb *dev;
+ int err = -ENOMEM;
+ int i;
+
+ dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+ if (!dev->bulk_in || !dev->bulk_out) {
+ dev_err(&intf->dev, "Cannot get usb endpoint(s)");
+ return err;
+ }
+
+ dev->udev = interface_to_usbdev(intf);
+
+ init_usb_anchor(&dev->rx_submitted);
+
+ usb_set_intfdata(intf, dev);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++)
+ kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
+
+ err = kvaser_usb_get_software_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get software infos, error %d\n", err);
+ return err;
+ }
+
+ err = kvaser_usb_get_card_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get card infos, error %d\n", err);
+ return err;
+ }
+
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ for (i = 0; i < dev->nchannels; i++)
+ kvaser_usb_init_one(intf, id, i);
+
+ return 0;
+}
+
+static void kvaser_usb_disconnect(struct usb_interface *intf)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ int i;
+
+ usb_set_intfdata(intf, NULL);
+
+ if (!dev)
+ return;
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ unregister_netdev(dev->nets[i]->netdev);
+ }
+
+ kvaser_usb_unlink_all_urbs(dev);
+
+ for (i = 0; i < dev->nchannels; i++)
+ free_candev(dev->nets[i]->netdev);
+}
+
+static struct usb_driver kvaser_usb_driver = {
+ .name = "kvaser_usb",
+ .probe = kvaser_usb_probe,
+ .disconnect = kvaser_usb_disconnect,
+ .id_table = kvaser_usb_table
+};
+
+module_usb_driver(kvaser_usb_driver);
+
+MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
+MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related
* RE:DEAR WINNER,OPEN THE ATTACHED FILE FOR YOUR SHELL AWARD WINNING INFORMATION:
From: SHELL AWARD 2012 @ 2012-08-13 14:23 UTC (permalink / raw)
[-- Attachment #1: Type: text/plain, Size: 0 bytes --]
[-- Attachment #2: spdc docment.docx --]
[-- Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document, Size: 39166 bytes --]
^ permalink raw reply
* [PATCH] net: Fix incorrect comment in netif_tx_stop_queue()
From: Ilya Shchepetkov @ 2012-08-13 14:43 UTC (permalink / raw)
To: David S. Miller; +Cc: Ilya Shchepetkov, netdev, linux-kernel, ldv-project
netif_stop_queue() can be called before register_netdev() because now
TX queues are allocated inside alloc_netdev_mqs().
(since ed9af2e839c06c18f721da2c768fbb444c4a10e5 commit)
Found by Linux Driver Verification project (linuxtesting.org).
Signed-off-by: Ilya Shchepetkov <shchepetkov@ispras.ru>
---
include/linux/netdevice.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index a9db4f3..a94fbbd 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1860,7 +1860,7 @@ static inline void netif_tx_wake_all_queues(struct net_device *dev)
static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue)
{
if (WARN_ON(!dev_queue)) {
- pr_info("netif_stop_queue() cannot be called before register_netdev()\n");
+ pr_info("dev_queue is null\n");
return;
}
set_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state);
--
1.7.7
^ permalink raw reply related
* [PATCH] ixgbevf - Remove unused parameter in ixgbevf_receive_skb
From: Narendra_K @ 2012-08-13 14:44 UTC (permalink / raw)
To: netdev; +Cc: jeffrey.t.kirsher, peter.p.waskiewicz.jr
From: Narendra K <narendra_k@dell.com>
Remove 'rx_ring' parameter as it is not used in ixgbevf_receive_skb
Signed-off-by: Narendra K <narendra_k@dell.com>
---
The patch applies to 'net' tree.
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 60ef645..cc5cdeb 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -272,12 +272,10 @@ cont_loop:
* @q_vector: structure containing interrupt and ring information
* @skb: packet to send up
* @status: hardware indication of status of receive
- * @rx_ring: rx descriptor ring (for a specific queue) to setup
* @rx_desc: rx descriptor
**/
static void ixgbevf_receive_skb(struct ixgbevf_q_vector *q_vector,
struct sk_buff *skb, u8 status,
- struct ixgbevf_ring *ring,
union ixgbe_adv_rx_desc *rx_desc)
{
struct ixgbevf_adapter *adapter = q_vector->adapter;
@@ -461,7 +459,7 @@ static bool ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
}
skb->protocol = eth_type_trans(skb, rx_ring->netdev);
- ixgbevf_receive_skb(q_vector, skb, staterr, rx_ring, rx_desc);
+ ixgbevf_receive_skb(q_vector, skb, staterr, rx_desc);
next_desc:
rx_desc->wb.upper.status_error = 0;
--
1.7.10.1
--
With regards,
Narendra K
^ permalink raw reply related
* Re: [PATCH] Makefile: add missing blankspace
From: Stephen Hemminger @ 2012-08-13 14:58 UTC (permalink / raw)
To: Li Wei; +Cc: netdev
In-Reply-To: <1343897109-22855-1-git-send-email-lw@cn.fujitsu.com>
On Thu, 2 Aug 2012 16:45:09 +0800
Li Wei <lw@cn.fujitsu.com> wrote:
> The missing blankspace caused us can't get tc.8 and tc-codel.8
> installed.
>
> Signed-off-by: Li Wei <lw@cn.fujitsu.com>
> ---
> man/man8/Makefile | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/man/man8/Makefile b/man/man8/Makefile
> index 1b671a4..c344094 100644
> --- a/man/man8/Makefile
> +++ b/man/man8/Makefile
> @@ -3,7 +3,7 @@ TARGETS = ip-address.8 ip-link.8 ip-route.8
> MAN8PAGES = $(TARGETS) ip.8 arpd.8 lnstat.8 routel.8 rtacct.8 rtmon.8 ss.8 \
> tc-bfifo.8 tc-cbq-details.8 tc-cbq.8 tc-drr.8 tc-htb.8 \
> tc-pfifo.8 tc-pfifo_fast.8 tc-prio.8 tc-red.8 tc-sfq.8 \
> - tc-tbf.8 tc.8tc-codel.8 tc-fq_codel.8 tc-sfb.8 tc-netem.8 tc-choke.8 \
> + tc-tbf.8 tc.8 tc-codel.8 tc-fq_codel.8 tc-sfb.8 tc-netem.8 tc-choke.8 \
> bridge.8 rtstat.8 ctstat.8 nstat.8 routef.8 \
> ip-tunnel.8 ip-rule.8 ip-ntable.8 \
> ip-monitor.8 tc-stab.8 tc-hfsc.8 ip-xfrm.8 ip-netns.8 \
Applied
^ permalink raw reply
* Re: [PATCH iproute2] Install the bridge command under the same name we build it.
From: Stephen Hemminger @ 2012-08-13 15:04 UTC (permalink / raw)
To: Li Wei; +Cc: Nick Alcock, netdev
In-Reply-To: <501F2311.8070708@cn.fujitsu.com>
On Mon, 06 Aug 2012 09:51:13 +0800
Li Wei <lw@cn.fujitsu.com> wrote:
> On 08/04/2012 10:36 PM, Nick Alcock wrote:
> >
> > Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
> > ---
> > bridge/Makefile | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/bridge/Makefile b/bridge/Makefile
> > index 0c333bf..9a6743e 100644
> > --- a/bridge/Makefile
> > +++ b/bridge/Makefile
> > @@ -7,8 +7,8 @@ all: bridge
> > bridge: $(BROBJ) $(LIBNETLINK)
> >
> > install: all
> > - install -m 0755 br $(DESTDIR)$(SBINDIR)
> > + install -m 0755 bridge $(DESTDIR)$(SBINDIR)
> >
> > clean:
> > - rm -f $(BROBJ) br
> > + rm -f $(BROBJ) bridge
> >
>
> Hi Nick,
>
> To post a patch for iproute2, you should send it to
> Stephen Hemminger <shemminger@vyatta.com> who is the maintainer of iproute2.
>
>
> Stephen, I also noticed this problem and thought should ask you for the final
> name of the brige extension. Personally I prefer br, it is short and
> distingushable enough.
>
> Thanks.
br was already taken by X10 package on debian.
^ permalink raw reply
* Re: iproute2-3.5.0: `ip a s` no longer shows addresses
From: Stephen Hemminger @ 2012-08-13 15:11 UTC (permalink / raw)
To: Mike Frysinger; +Cc: netdev
In-Reply-To: <201208071503.01261.vapier@gentoo.org>
[-- Attachment #1: Type: text/plain, Size: 1677 bytes --]
On Tue, 7 Aug 2012 15:03:00 -0400
Mike Frysinger <vapier@gentoo.org> wrote:
> since 3.4.0 works, but 3.5.0, a quick bisect shows the bad commit:
> http://git.kernel.org/?p=linux/kernel/git/shemminger/iproute2.git;a=commit;h=8d07e5f7d995171ee8834eae268e300560de5d4f
>
> the simple test case:
> make clean && make -j -s && ./ip/ip a s lo
>
> before that change, i would get:
> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
> link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
> inet 127.0.0.1/8 scope host lo
> inet6 ::1/128 scope host
> valid_lft forever preferred_lft forever
>
> but after, i now get:
> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
> link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
>
> seems like the bug was introduced in the middle of that patch:
>
> - if (filter.family != AF_PACKET) {
> + if (filter.family && filter.family != AF_PACKET) {
> + if (filter.oneline)
> + no_link = 1;
> +
> if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
> perror("Cannot send dump request");
> exit(1);
>
> if i revert the change to the if statement there, `ip a s` works for me again.
> (see below)
> -mike
>
> diff --git a/ip/ipaddress.c b/ip/ipaddress.c
> index 37deda5..69a63b3 100644
> --- a/ip/ipaddress.c
> +++ b/ip/ipaddress.c
> @@ -1018,7 +1018,7 @@ static int ipaddr_list_or_flush(int argc, char **argv, int flush)
> exit(1);
> }
>
> - if (filter.family && filter.family != AF_PACKET) {
> + if (filter.family != AF_PACKET) {
> if (filter.oneline)
> no_link = 1;
>
Ok. Applied
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* [ANNOUNCE] iproute2 3.5.1
From: Stephen Hemminger @ 2012-08-13 15:25 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev, linux-kernel
In-Reply-To: <20120801160550.344a66b1@nehalam.linuxnetplumber.net>
I'm re-releasing the iproute2 tools for 3.5.0 kernel.
There were a couple of bugs (one serious) in the 3.5.0 version.
Source:
http://www.kernel.org/pub/linux/utils/net/iproute2/iproute2-3.5.1.tar.gz
The iproute2 3.5.1 git tree can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git iproute-3.5.1
-----------
Mike Frysinger (1):
Fix regression with 'ip address show'
Stephen Hemminger (1):
v3.5.1
Xose Vazquez Perez (1):
Fix Makefile's
^ permalink raw reply
* Re: [PATCH] configure: Add search path for 64bit library.
From: Stephen Hemminger @ 2012-08-13 15:26 UTC (permalink / raw)
To: Ben Hutchings; +Cc: Li Wei, netdev
In-Reply-To: <1344363358.2688.8.camel@bwh-desktop.uk.solarflarecom.com>
On Tue, 7 Aug 2012 19:15:58 +0100
Ben Hutchings <bhutchings@solarflare.com> wrote:
> The subject line doesn't say what this is for, but it looks like
> iproute2...
>
> On Tue, 2012-08-07 at 12:22 +0800, Li Wei wrote:
> > Signed-off-by: Li Wei <lw@cn.fujitsu.com>
> > ---
> > configure | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/configure b/configure
> > index 0f4444f..997759c 100755
> > --- a/configure
> > +++ b/configure
> > @@ -149,7 +149,7 @@ check_ipt()
> > check_ipt_lib_dir()
> > {
> > IPT_LIB_DIR=""
> > - for dir in /lib /usr/lib /usr/local/lib
> > + for dir in /lib /usr/lib /usr/local/lib /lib64 /usr/lib64 /usr/local/lib64
> > do
> > for file in $dir/{xtables,iptables}/lib*t_*so ; do
> > if [ -f $file ]; then
>
> On a bi-arch system, surely the lib64 directories should be preferred to
> the lib directories? And this still leaves multi-arch to be handled.
>
> I think this should be done with pkg-config:
>
> pkg-config --variable=xtlibdir xtables
>
> possibly with that directory list as a fallback if it's useful to
> support iptables library versions that didn't include xtables.pc.
>
> Ben.
>
Does every distro have pkg-config or does more logic need to be done here?
^ permalink raw reply
* [patch net-next 00/16] net: introduce upper device lists and remove dev->master
From: Jiri Pirko @ 2012-08-13 15:26 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, faisal.latif, roland, sean.hefty, hal.rosenstock,
fubar, andy, divy, jitendra.kalsaria, sony.chacko, linux-driver,
kaber, ursula.braun, blaschka, linux390, shemminger, bhutchings,
therbert, xiyou.wangcong, joe, gregory.v.rose, john.r.fastabend,
linux-rdma, linux-kernel, linux-s390, bridge, fbl
Hi all.
Recent discussion around
"[net-next] bonding: don't allow the master to become its slave"
forced me to think about upper<->lower device connections.
This patchset adds a possibility to record upper device linkage.
All upper<->lower devices are converted to use this mechanism right after.
That leads to dev->master removal because this info becomes redundant since
"unique links" have the same value.
After all changes, there is no longer possible to do:
"bond->someotherdevice->samebond"
Also I think that drivers like cxgb3, qlcnic, qeth would benefit by this
in future by being able to get more appropriate info about l3 addresses.
Jiri Pirko (16):
net: introduce upper device lists
macvlan: add link to upper device
vlan: add link to upper device
rtnetlink: remove usage of dev->master
team: remove usage of netdev_set_master()
bridge: remove usage of netdev_set_master()
netpoll: remove usage of dev->master
cxgb3: remove usage of dev->master
qlcnic: guard __vlan_find_dev_deep() by rcu_read_lock
qeth: ensure that __vlan_find_dev_deep() is called with rcu_read_lock
vlan: remove usage of dev->master in __vlan_find_dev_deep()
nes: remove usage of dev->master
bonding: remove usage of dev->master
net: remove no longer used netdev_set_bond_master() and
netdev_set_master()
net: remove usage of dev->master
net: kill dev->master
drivers/infiniband/hw/nes/nes.c | 8 +-
drivers/infiniband/hw/nes/nes_cm.c | 2 +-
drivers/net/bonding/bond_3ad.c | 30 +--
drivers/net/bonding/bond_alb.c | 6 +-
drivers/net/bonding/bond_main.c | 94 ++++----
drivers/net/bonding/bonding.h | 14 +-
drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 11 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +
drivers/net/macvlan.c | 9 +-
drivers/net/team/team.c | 13 +-
drivers/s390/net/qeth_l3_main.c | 21 +-
include/linux/netdevice.h | 22 +-
net/8021q/vlan.c | 10 +-
net/8021q/vlan_core.c | 18 +-
net/bridge/br_if.c | 6 +-
net/core/dev.c | 241 +++++++++++++++++---
net/core/netpoll.c | 8 +-
net/core/rtnetlink.c | 45 ++--
18 files changed, 392 insertions(+), 168 deletions(-)
--
1.7.10.4
^ permalink raw reply
* [patch net-next 01/16] net: introduce upper device lists
From: Jiri Pirko @ 2012-08-13 15:27 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA
Cc: davem-fT/PcQaiUtIeIZ0/mPfg9Q, edumazet-hpIqsD4AKlfQT0dZR+AlfA,
faisal.latif-ral2JQCrhuEAvxtiuMwx3w,
roland-DgEjT+Ai2ygdnm+yROfE0A, sean.hefty-ral2JQCrhuEAvxtiuMwx3w,
hal.rosenstock-Re5JQEeQqe8AvxtiuMwx3w,
fubar-r/Jw6+rmf7HQT0dZR+AlfA, andy-QlMahl40kYEqcZcGjlUOXw,
divy-ut6Up61K2wZBDgjK7y7TUQ,
jitendra.kalsaria-h88ZbnxC6KDQT0dZR+AlfA,
sony.chacko-h88ZbnxC6KDQT0dZR+AlfA,
linux-driver-h88ZbnxC6KDQT0dZR+AlfA, kaber-dcUjhNyLwpNeoWH0uzbU5w,
ursula.braun-tA70FqPdS9bQT0dZR+AlfA,
blaschka-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
linux390-tA70FqPdS9bQT0dZR+AlfA,
shemminger-ZtmgI6mnKB3QT0dZR+AlfA,
bhutchings-s/n/eUQHGBpZroRs9YW3xA,
therbert-hpIqsD4AKlfQT0dZR+AlfA,
xiyou.wangcong-Re5JQEeQqe8AvxtiuMwx3w, joe-6d6DIl74uiNBDgjK7y7TUQ,
gregory.v.rose-ral2JQCrhuEAvxtiuMwx3w,
john.r.fastabend-ral2JQCrhuEAvxtiuMwx3w,
linux-rdma-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-s390-u79uwXL29TY76Z2rM5mHXA,
bridge-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
fbl-H+wXaHxf7aLQT0dZR+AlfA
In-Reply-To: <1344871635-1052-1-git-send-email-jiri-rHqAuBHg3fBzbRFIqnYvSA@public.gmane.org>
This lists are supposed to serve for storing pointers to all upper devices.
Eventually it will replace dev->master pointer which is used for
bonding, bridge, team but it cannot be used for vlan, macvlan where
there might be multiple "masters" present.
New upper device list resolves this limitation. Also, the information
stored in lists is used for preventing looping setups like
"bond->somethingelse->samebond"
Signed-off-by: Jiri Pirko <jiri-rHqAuBHg3fBzbRFIqnYvSA@public.gmane.org>
---
include/linux/netdevice.h | 14 +++
net/core/dev.c | 232 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 244 insertions(+), 2 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index a9db4f3..e7a07f8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1173,6 +1173,8 @@ struct net_device {
* which this device is member of.
*/
+ struct list_head upper_dev_list; /* List of upper devices */
+
/* Interface address info used in eth_type_trans() */
unsigned char *dev_addr; /* hw address, (before bcast
because most packets are
@@ -2611,6 +2613,18 @@ extern int netdev_max_backlog;
extern int netdev_tstamp_prequeue;
extern int weight_p;
extern int bpf_jit_enable;
+
+extern bool netdev_has_upper_dev(struct net_device *dev,
+ struct net_device *upper_dev);
+extern bool netdev_has_any_upper_dev(struct net_device *dev);
+extern struct net_device *netdev_unique_upper_dev_get(struct net_device *dev);
+extern struct net_device *netdev_unique_upper_dev_get_rcu(struct net_device *dev);
+extern int netdev_upper_dev_link(struct net_device *dev,
+ struct net_device *upper_dev);
+extern int netdev_unique_upper_dev_link(struct net_device *dev,
+ struct net_device *upper_dev);
+extern void netdev_upper_dev_unlink(struct net_device *dev,
+ struct net_device *upper_dev);
extern int netdev_set_master(struct net_device *dev, struct net_device *master);
extern int netdev_set_bond_master(struct net_device *dev,
struct net_device *master);
diff --git a/net/core/dev.c b/net/core/dev.c
index 1f06df8..68db1ac 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4425,6 +4425,229 @@ static int __init dev_proc_init(void)
#endif /* CONFIG_PROC_FS */
+struct netdev_upper {
+ struct net_device *dev;
+ bool unique;
+ struct list_head list;
+ struct rcu_head rcu;
+};
+
+static bool __netdev_has_upper_dev(struct net_device *dev,
+ struct net_device *upper_dev,
+ bool deep)
+{
+ struct netdev_upper *upper;
+
+ list_for_each_entry(upper, &dev->upper_dev_list, list) {
+ if (upper->dev == upper_dev)
+ return true;
+ if (deep && __netdev_has_upper_dev(upper->dev, upper_dev, deep))
+ return true;
+ }
+ return false;
+}
+
+static struct netdev_upper *__netdev_find_upper(struct net_device *dev,
+ struct net_device *upper_dev)
+{
+ struct netdev_upper *upper;
+
+ list_for_each_entry(upper, &dev->upper_dev_list, list) {
+ if (upper->dev == upper_dev)
+ return upper;
+ }
+ return NULL;
+}
+
+/**
+ * netdev_has_upper_dev - Check if device is linked to an upper device
+ * @dev: device
+ * @upper_dev: upper device to check
+ *
+ * Find out if a device is linked to specified upper device and return true
+ * in case it is. The caller must hold the RTNL semaphore.
+ */
+bool netdev_has_upper_dev(struct net_device *dev,
+ struct net_device *upper_dev)
+{
+ ASSERT_RTNL();
+
+ return __netdev_has_upper_dev(dev, upper_dev, false);
+}
+EXPORT_SYMBOL(netdev_has_upper_dev);
+
+/**
+ * netdev_has_any_upper_dev - Check if device is linked to some device
+ * @dev: device
+ *
+ * Find out if a device is linked to an upper device and return true in case
+ * it is. The caller must hold the RTNL semaphore.
+ */
+bool netdev_has_any_upper_dev(struct net_device *dev)
+{
+ ASSERT_RTNL();
+
+ return !list_empty(&dev->upper_dev_list);
+}
+EXPORT_SYMBOL(netdev_has_any_upper_dev);
+
+/**
+ * netdev_unique_upper_dev_get - Get unique upper device
+ * @dev: device
+ *
+ * Find a unique upper device and return pointer to it or NULL in case
+ * it's not there. The caller must hold the RTNL semaphore.
+ */
+struct net_device *netdev_unique_upper_dev_get(struct net_device *dev)
+{
+ struct netdev_upper *upper;
+
+ ASSERT_RTNL();
+
+ if (list_empty(&dev->upper_dev_list))
+ return NULL;
+
+ upper = list_first_entry(&dev->upper_dev_list,
+ struct netdev_upper, list);
+ if (likely(upper->unique))
+ return upper->dev;
+ return NULL;
+}
+EXPORT_SYMBOL(netdev_unique_upper_dev_get);
+
+/**
+ * netdev_unique_upper_dev_get_rcu - Get unique upper device
+ * @dev: device
+ *
+ * Find a unique upper device and return pointer to it or NULL in case
+ * it's not there. The caller must hold the RCU read lock.
+ */
+struct net_device *netdev_unique_upper_dev_get_rcu(struct net_device *dev)
+{
+ struct netdev_upper *upper;
+
+ upper = list_first_or_null_rcu(&dev->upper_dev_list,
+ struct netdev_upper, list);
+ if (likely(upper->unique))
+ return upper->dev;
+ return NULL;
+}
+EXPORT_SYMBOL(netdev_unique_upper_dev_get_rcu);
+
+static int __netdev_upper_dev_link(struct net_device *dev,
+ struct net_device *upper_dev, bool unique)
+{
+ struct netdev_upper *upper;
+
+ ASSERT_RTNL();
+
+ if (dev == upper_dev)
+ return -EBUSY;
+ /*
+ * To prevent loops, check if dev is not upper device to upper_dev.
+ */
+ if (__netdev_has_upper_dev(upper_dev, dev, true))
+ return -EBUSY;
+
+ if (__netdev_find_upper(dev, upper_dev))
+ return -EEXIST;
+
+ if (unique && netdev_unique_upper_dev_get(dev))
+ return -EBUSY;
+
+ upper = kmalloc(sizeof(*upper), GFP_KERNEL);
+ if (!upper)
+ return -ENOMEM;
+
+ upper->dev = upper_dev;
+ upper->unique = unique;
+
+ /*
+ * Ensure that unique upper link is always the first item in the list.
+ */
+ if (unique)
+ list_add_rcu(&upper->list, &dev->upper_dev_list);
+ else
+ list_add_tail_rcu(&upper->list, &dev->upper_dev_list);
+ dev_hold(upper_dev);
+
+ return 0;
+}
+/**
+ * netdev_upper_dev_link - Add a link to the upper device
+ * @dev: device
+ * @upper_dev: new upper device
+ *
+ * Adds a link to device which is upper to this one. The caller must hold
+ * the RTNL semaphore. On a failure a negative errno code is returned.
+ * On success the reference counts are adjusted and the function
+ * returns zero.
+ */
+int netdev_upper_dev_link(struct net_device *dev,
+ struct net_device *upper_dev)
+{
+ return __netdev_upper_dev_link(dev, upper_dev, false);
+}
+EXPORT_SYMBOL(netdev_upper_dev_link);
+
+/**
+ * netdev_unique_upper_dev_link - Add a unique link to the upper device
+ * @dev: device
+ * @upper_dev: new upper device
+ *
+ * Adds a link to device which is upper to this one. In this case, only
+ * one unique upper device can be linked, although other non-unique devices
+ * might be linked as well. The caller must hold the RTNL semaphore.
+ * On a failure a negative errno code is returned. On success the reference
+ * counts are adjusted and the function returns zero.
+ */
+int netdev_unique_upper_dev_link(struct net_device *dev,
+ struct net_device *upper_dev)
+{
+ return __netdev_upper_dev_link(dev, upper_dev, true);
+}
+EXPORT_SYMBOL(netdev_unique_upper_dev_link);
+
+/**
+ * netdev_upper_free_rcu - Frees a upper device list item via the RCU pointer
+ * @entry: the entry's RCU field
+ *
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that the memory allocated to the netdev upper device list item
+ * can be released safely.
+ */
+static void netdev_upper_free_rcu(struct rcu_head *entry)
+{
+ struct netdev_upper *upper;
+
+ upper = container_of(entry, struct netdev_upper, rcu);
+ kfree(upper);
+}
+
+/**
+ * netdev_upper_dev_unlink - Removes a link to upper device
+ * @dev: device
+ * @upper_dev: new upper device
+ *
+ * Removes a link to device which is upper to this one. The caller must hold
+ * the RTNL semaphore.
+ */
+void netdev_upper_dev_unlink(struct net_device *dev,
+ struct net_device *upper_dev)
+{
+ struct netdev_upper *upper;
+
+ ASSERT_RTNL();
+
+ upper = __netdev_find_upper(dev, upper_dev);
+ if (!upper)
+ return;
+ list_del_rcu(&upper->list);
+ dev_put(upper_dev);
+ call_rcu(&upper->rcu, netdev_upper_free_rcu);
+}
+EXPORT_SYMBOL(netdev_upper_dev_unlink);
+
/**
* netdev_set_master - set up master pointer
* @slave: slave device
@@ -4438,19 +4661,23 @@ static int __init dev_proc_init(void)
int netdev_set_master(struct net_device *slave, struct net_device *master)
{
struct net_device *old = slave->master;
+ int err;
ASSERT_RTNL();
if (master) {
if (old)
return -EBUSY;
- dev_hold(master);
+ err = netdev_unique_upper_dev_link(slave, master);
+ if (err)
+ return err;
}
slave->master = master;
if (old)
- dev_put(old);
+ netdev_upper_dev_unlink(slave, master);
+
return 0;
}
EXPORT_SYMBOL(netdev_set_master);
@@ -5999,6 +6226,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
INIT_LIST_HEAD(&dev->napi_list);
INIT_LIST_HEAD(&dev->unreg_list);
INIT_LIST_HEAD(&dev->link_watch_list);
+ INIT_LIST_HEAD(&dev->upper_dev_list);
dev->priv_flags = IFF_XMIT_DST_RELEASE;
setup(dev);
--
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [patch net-next 02/16] macvlan: add link to upper device
From: Jiri Pirko @ 2012-08-13 15:27 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, faisal.latif, roland, sean.hefty, hal.rosenstock,
fubar, andy, divy, jitendra.kalsaria, sony.chacko, linux-driver,
kaber, ursula.braun, blaschka, linux390, shemminger, bhutchings,
therbert, xiyou.wangcong, joe, gregory.v.rose, john.r.fastabend,
linux-rdma, linux-kernel, linux-s390, bridge, fbl
In-Reply-To: <1344871635-1052-1-git-send-email-jiri@resnulli.us>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
drivers/net/macvlan.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 66a9bfe..f5bd8a2 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -765,16 +765,22 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN);
}
+ err = netdev_upper_dev_link(lowerdev, dev);
+ if (err)
+ goto destroy_port;
+
port->count += 1;
err = register_netdevice(dev);
if (err < 0)
- goto destroy_port;
+ goto upper_dev_unlink;
list_add_tail(&vlan->list, &port->vlans);
netif_stacked_transfer_operstate(lowerdev, dev);
return 0;
+upper_dev_unlink:
+ netdev_upper_dev_unlink(lowerdev, dev);
destroy_port:
port->count -= 1;
if (!port->count)
@@ -798,6 +804,7 @@ void macvlan_dellink(struct net_device *dev, struct list_head *head)
list_del(&vlan->list);
unregister_netdevice_queue(dev, head);
+ netdev_upper_dev_unlink(vlan->lowerdev, dev);
}
EXPORT_SYMBOL_GPL(macvlan_dellink);
--
1.7.10.4
^ permalink raw reply related
* [patch net-next 03/16] vlan: add link to upper device
From: Jiri Pirko @ 2012-08-13 15:27 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, faisal.latif, roland, sean.hefty, hal.rosenstock,
fubar, andy, divy, jitendra.kalsaria, sony.chacko, linux-driver,
kaber, ursula.braun, blaschka, linux390, shemminger, bhutchings,
therbert, xiyou.wangcong, joe, gregory.v.rose, john.r.fastabend,
linux-rdma, linux-kernel, linux-s390, bridge, fbl
In-Reply-To: <1344871635-1052-1-git-send-email-jiri@resnulli.us>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
net/8021q/vlan.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 9096bcb..739665e 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -105,6 +105,8 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
*/
unregister_netdevice_queue(dev, head);
+ netdev_upper_dev_unlink(real_dev, dev);
+
if (grp->nr_vlan_devs == 0)
vlan_gvrp_uninit_applicant(real_dev);
@@ -162,9 +164,13 @@ int register_vlan_dev(struct net_device *dev)
if (err < 0)
goto out_uninit_applicant;
+ err = netdev_upper_dev_link(real_dev, dev);
+ if (err)
+ goto out_uninit_applicant;
+
err = register_netdevice(dev);
if (err < 0)
- goto out_uninit_applicant;
+ goto out_upper_dev_unlink;
/* Account for reference in struct vlan_dev_priv */
dev_hold(real_dev);
@@ -180,6 +186,8 @@ int register_vlan_dev(struct net_device *dev)
return 0;
+upper_dev_unlink:
+ netdev_upper_dev_unlink(real_dev, dev);
out_uninit_applicant:
if (grp->nr_vlan_devs == 0)
vlan_gvrp_uninit_applicant(real_dev);
--
1.7.10.4
^ permalink raw reply related
* [patch net-next 04/16] rtnetlink: remove usage of dev->master
From: Jiri Pirko @ 2012-08-13 15:27 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, faisal.latif, roland, sean.hefty, hal.rosenstock,
fubar, andy, divy, jitendra.kalsaria, sony.chacko, linux-driver,
kaber, ursula.braun, blaschka, linux390, shemminger, bhutchings,
therbert, xiyou.wangcong, joe, gregory.v.rose, john.r.fastabend,
linux-rdma, linux-kernel, linux-s390, bridge, fbl
In-Reply-To: <1344871635-1052-1-git-send-email-jiri@resnulli.us>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
net/core/rtnetlink.c | 44 ++++++++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 20 deletions(-)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 34d975b..c341eb7 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -879,6 +879,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
const struct rtnl_link_stats64 *stats;
struct nlattr *attr, *af_spec;
struct rtnl_af_ops *af_ops;
+ struct net_device *upper_dev = netdev_unique_upper_dev_get(dev);
ASSERT_RTNL();
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
@@ -907,8 +908,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
#endif
(dev->ifindex != dev->iflink &&
nla_put_u32(skb, IFLA_LINK, dev->iflink)) ||
- (dev->master &&
- nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) ||
+ (upper_dev &&
+ nla_put_u32(skb, IFLA_MASTER, upper_dev->ifindex)) ||
(dev->qdisc &&
nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) ||
(dev->ifalias &&
@@ -1270,16 +1271,16 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)
static int do_set_master(struct net_device *dev, int ifindex)
{
- struct net_device *master_dev;
+ struct net_device *upper_dev = netdev_unique_upper_dev_get(dev);
const struct net_device_ops *ops;
int err;
- if (dev->master) {
- if (dev->master->ifindex == ifindex)
+ if (upper_dev) {
+ if (upper_dev->ifindex == ifindex)
return 0;
- ops = dev->master->netdev_ops;
+ ops = upper_dev->netdev_ops;
if (ops->ndo_del_slave) {
- err = ops->ndo_del_slave(dev->master, dev);
+ err = ops->ndo_del_slave(upper_dev, dev);
if (err)
return err;
} else {
@@ -1288,12 +1289,12 @@ static int do_set_master(struct net_device *dev, int ifindex)
}
if (ifindex) {
- master_dev = __dev_get_by_index(dev_net(dev), ifindex);
- if (!master_dev)
+ upper_dev = __dev_get_by_index(dev_net(dev), ifindex);
+ if (!upper_dev)
return -EINVAL;
- ops = master_dev->netdev_ops;
+ ops = upper_dev->netdev_ops;
if (ops->ndo_add_slave) {
- err = ops->ndo_add_slave(master_dev, dev);
+ err = ops->ndo_add_slave(upper_dev, dev);
if (err)
return err;
} else {
@@ -2050,7 +2051,6 @@ errout:
static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
- struct net_device *master = NULL;
struct ndmsg *ndm;
struct nlattr *tb[NDA_MAX+1];
struct net_device *dev;
@@ -2089,9 +2089,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
/* Support fdb on master device the net/bridge default case */
if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
(dev->priv_flags & IFF_BRIDGE_PORT)) {
- master = dev->master;
- err = master->netdev_ops->ndo_fdb_add(ndm, dev, addr,
- nlh->nlmsg_flags);
+ struct net_device *br_dev = netdev_unique_upper_dev_get(dev);
+ const struct net_device_ops *ops = br_dev->netdev_ops;
+
+ err = ops->ndo_fdb_add(ndm, dev, addr, nlh->nlmsg_flags);
if (err)
goto out;
else
@@ -2148,10 +2149,11 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
/* Support fdb on master device the net/bridge default case */
if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
(dev->priv_flags & IFF_BRIDGE_PORT)) {
- struct net_device *master = dev->master;
+ struct net_device *br_dev = netdev_unique_upper_dev_get(dev);
+ const struct net_device_ops *ops = br_dev->netdev_ops;
- if (master->netdev_ops->ndo_fdb_del)
- err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr);
+ if (ops->ndo_fdb_del)
+ err = ops->ndo_fdb_del(ndm, dev, addr);
if (err)
goto out;
@@ -2234,9 +2236,11 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
rcu_read_lock();
for_each_netdev_rcu(net, dev) {
if (dev->priv_flags & IFF_BRIDGE_PORT) {
- struct net_device *master = dev->master;
- const struct net_device_ops *ops = master->netdev_ops;
+ struct net_device *br_dev;
+ const struct net_device_ops *ops;
+ br_dev = netdev_unique_upper_dev_get(dev);
+ ops = br_dev->netdev_ops;
if (ops->ndo_fdb_dump)
idx = ops->ndo_fdb_dump(skb, cb, dev, idx);
}
--
1.7.10.4
^ permalink raw reply related
* [patch net-next 05/16] team: remove usage of netdev_set_master()
From: Jiri Pirko @ 2012-08-13 15:27 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, faisal.latif, roland, sean.hefty, hal.rosenstock,
fubar, andy, divy, jitendra.kalsaria, sony.chacko, linux-driver,
kaber, ursula.braun, blaschka, linux390, shemminger, bhutchings,
therbert, xiyou.wangcong, joe, gregory.v.rose, john.r.fastabend,
linux-rdma, linux-kernel, linux-s390, bridge, fbl
In-Reply-To: <1344871635-1052-1-git-send-email-jiri@resnulli.us>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
drivers/net/team/team.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index ba10c46..0533cbf 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1040,10 +1040,11 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
}
}
- err = netdev_set_master(port_dev, dev);
+ err = netdev_unique_upper_dev_link(port_dev, dev);
if (err) {
- netdev_err(dev, "Device %s failed to set master\n", portname);
- goto err_set_master;
+ netdev_err(dev, "Device %s failed to set upper link\n",
+ portname);
+ goto err_set_upper_link;
}
err = netdev_rx_handler_register(port_dev, team_handle_frame,
@@ -1076,9 +1077,9 @@ err_option_port_add:
netdev_rx_handler_unregister(port_dev);
err_handler_register:
- netdev_set_master(port_dev, NULL);
+ netdev_upper_dev_unlink(port_dev, dev);
-err_set_master:
+err_set_upper_link:
team_port_disable_netpoll(port);
err_enable_netpoll:
@@ -1121,7 +1122,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
team_port_disable(team, port);
list_del_rcu(&port->list);
netdev_rx_handler_unregister(port_dev);
- netdev_set_master(port_dev, NULL);
+ netdev_upper_dev_unlink(port_dev, dev);
team_port_disable_netpoll(port);
vlan_vids_del_by_dev(port_dev, dev);
dev_close(port_dev);
--
1.7.10.4
^ permalink raw reply related
* [patch net-next 06/16] bridge: remove usage of netdev_set_master()
From: Jiri Pirko @ 2012-08-13 15:27 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, faisal.latif, roland, sean.hefty, hal.rosenstock,
fubar, andy, divy, jitendra.kalsaria, sony.chacko, linux-driver,
kaber, ursula.braun, blaschka, linux390, shemminger, bhutchings,
therbert, xiyou.wangcong, joe, gregory.v.rose, john.r.fastabend,
linux-rdma, linux-kernel, linux-s390, bridge, fbl
In-Reply-To: <1344871635-1052-1-git-send-email-jiri@resnulli.us>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
net/bridge/br_if.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index e1144e1..5933900 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -148,7 +148,7 @@ static void del_nbp(struct net_bridge_port *p)
netdev_rx_handler_unregister(dev);
synchronize_net();
- netdev_set_master(dev, NULL);
+ netdev_upper_dev_unlink(dev, br->dev);
br_multicast_del_port(p);
@@ -364,7 +364,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (br_netpoll_info(br) && ((err = br_netpoll_enable(p))))
goto err3;
- err = netdev_set_master(dev, br->dev);
+ err = netdev_unique_upper_dev_link(dev, br->dev);
if (err)
goto err3;
@@ -403,7 +403,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return 0;
err4:
- netdev_set_master(dev, NULL);
+ netdev_upper_dev_unlink(dev, br->dev);
err3:
sysfs_remove_link(br->ifobj, p->dev->name);
err2:
--
1.7.10.4
^ permalink raw reply related
* [patch net-next 07/16] netpoll: remove usage of dev->master
From: Jiri Pirko @ 2012-08-13 15:27 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, faisal.latif, roland, sean.hefty, hal.rosenstock,
fubar, andy, divy, jitendra.kalsaria, sony.chacko, linux-driver,
kaber, ursula.braun, blaschka, linux390, shemminger, bhutchings,
therbert, xiyou.wangcong, joe, gregory.v.rose, john.r.fastabend,
linux-rdma, linux-kernel, linux-s390, bridge, fbl
In-Reply-To: <1344871635-1052-1-git-send-email-jiri@resnulli.us>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
net/core/netpoll.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index b4c90e4..5c8e560 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -207,12 +207,16 @@ static void netpoll_poll_dev(struct net_device *dev)
if (dev->flags & IFF_SLAVE) {
if (dev->npinfo) {
- struct net_device *bond_dev = dev->master;
+ struct net_device *bond_dev;
struct sk_buff *skb;
+
+ rcu_read_lock();
+ bond_dev = netdev_unique_upper_dev_get_rcu(dev);
while ((skb = skb_dequeue(&dev->npinfo->arp_tx))) {
skb->dev = bond_dev;
skb_queue_tail(&bond_dev->npinfo->arp_tx, skb);
}
+ rcu_read_unlock();
}
}
@@ -795,7 +799,7 @@ int netpoll_setup(struct netpoll *np)
return -ENODEV;
}
- if (ndev->master) {
+ if (netdev_unique_upper_dev_get(ndev)) {
np_err(np, "%s is a slave device, aborting\n", np->dev_name);
err = -EBUSY;
goto put;
--
1.7.10.4
^ permalink raw reply related
* [patch net-next 09/16] qlcnic: guard __vlan_find_dev_deep() by rcu_read_lock
From: Jiri Pirko @ 2012-08-13 15:27 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, faisal.latif, roland, sean.hefty, hal.rosenstock,
fubar, andy, divy, jitendra.kalsaria, sony.chacko, linux-driver,
kaber, ursula.braun, blaschka, linux390, shemminger, bhutchings,
therbert, xiyou.wangcong, joe, gregory.v.rose, john.r.fastabend,
linux-rdma, linux-kernel, linux-s390, bridge, fbl
In-Reply-To: <1344871635-1052-1-git-send-email-jiri@resnulli.us>
rcu_read_lock was missing here
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 212c121..9eadf17 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -4425,12 +4425,14 @@ qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event)
qlcnic_config_indev_addr(adapter, netdev, event);
+ rcu_read_lock();
for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) {
dev = __vlan_find_dev_deep(netdev, vid);
if (!dev)
continue;
qlcnic_config_indev_addr(adapter, dev, event);
}
+ rcu_read_unlock();
}
static int qlcnic_netdev_event(struct notifier_block *this,
--
1.7.10.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox