* [PATCH v2 0/2] CS89x0: add QQ2440 networking support
From: Domenico Andreoli @ 2011-03-14 13:46 UTC (permalink / raw)
To: David S. Miller, Andrew Morton; +Cc: linux-arm-kernel, netdev
Hi,
this patchset cleans some bit left from the CS89x0_NONISA_IRQ
transition and then enables the support of CS89x0 on QQ2440.
Thanks,
Domenico
-----[ Domenico Andreoli, aka cavok
--[ http://www.dandreoli.com/gpgkey.asc
---[ 3A0F 2F80 F79C 678A 8936 4FEE 0677 9033 A20E BC50
^ permalink raw reply
* [PATCH v2 1/2] CS89x0: Finish transition to CS89x0_NONISA_IRQ
From: Domenico Andreoli @ 2011-03-14 13:46 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: linux-arm-kernel, netdev, Domenico Andreoli
In-Reply-To: <20110314133302.137497490@gmail.com>
[-- Attachment #1: cs89x0-finish-config-nonisa-irq-conversion --]
[-- Type: text/plain, Size: 1526 bytes --]
From: Domenico Andreoli <cavokz@gmail.com>
CS89x0_NONISA_IRQ is selected by all those non-ISA boards which use
CS89x0. This patch only cleans the last bits left after its introduction.
Signed-off-by: Domenico Andreoli <cavokz@gmail.com>
Index: arm-2.6.git/drivers/net/cs89x0.c
===================================================================
--- arm-2.6.git.orig/drivers/net/cs89x0.c 2011-03-14 11:29:35.000000000 +0000
+++ arm-2.6.git/drivers/net/cs89x0.c 2011-03-14 11:35:11.000000000 +0000
@@ -943,10 +943,10 @@
static void __init reset_chip(struct net_device *dev)
{
#if !defined(CONFIG_MACH_MX31ADS)
-#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01)
+#if !defined(CS89x0_NONISA_IRQ)
struct net_local *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
-#endif
+#endif /* CS89x0_NONISA_IRQ */
int reset_start_time;
writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
@@ -954,7 +954,7 @@
/* wait 30 ms */
msleep(30);
-#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01)
+#if !defined(CS89x0_NONISA_IRQ)
if (lp->chip_type != CS8900) {
/* Hardware problem requires PNP registers to be reconfigured after a reset */
writeword(ioaddr, ADD_PORT, PP_CS8920_ISAINT);
@@ -965,7 +965,7 @@
outb((dev->mem_start >> 16) & 0xff, ioaddr + DATA_PORT);
outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT + 1);
}
-#endif /* IXDP2x01 */
+#endif /* CS89x0_NONISA_IRQ */
/* Wait until the chip is reset */
reset_start_time = jiffies;
^ permalink raw reply
* [PATCH v2 2/2] CS89x0: Add networking support for QQ2440
From: Domenico Andreoli @ 2011-03-14 13:47 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: linux-arm-kernel, netdev, Domenico Andreoli
In-Reply-To: <20110314133302.137497490@gmail.com>
[-- Attachment #1: cs89x0-add-qq2440-support --]
[-- Type: text/plain, Size: 3018 bytes --]
From: Domenico Andreoli <cavokz@gmail.com>
QQ2440 is only another non-ISA board using CS89x0. This patch adds the
minimum bits required to make QQ2440 work with CS89x0.
Signed-off-by: Domenico Andreoli <cavokz@gmail.com>
---
I'm working on another series that brings CS89x0 to the third millenium,
it properly uses the modern ISA and platform drivers model. That series
is a little more invasive and will require more testing than this one.
arch/arm/mach-s3c2440/include/mach/qq2440.h | 22 ++++++++++++
arch/arm/mach-s3c2440/mach-qq2440.c | 16 ++++++++
drivers/net/Kconfig | 4 +-
drivers/net/cs89x0.c | 33 ++++++++++++------
4 files changed, 61 insertions(+), 14 deletions(-)
Index: arm-2.6.git/drivers/net/cs89x0.c
===================================================================
--- arm-2.6.git.orig/drivers/net/cs89x0.c 2011-02-07 17:04:06.000000000 +0000
+++ arm-2.6.git/drivers/net/cs89x0.c 2011-02-07 17:07:36.000000000 +0000
@@ -95,6 +95,9 @@
Dmitry Pervushin : dpervushin@ru.mvista.com
: PNX010X platform support
+ Domenico Andreoli : cavokz@gmail.com
+ : QQ2440 platform support
+
*/
/* Always include 'config.h' first in case the user wants to turn on
@@ -176,6 +179,10 @@
#elif defined(CONFIG_ARCH_IXDP2X01)
static unsigned int netcard_portlist[] __used __initdata = {IXDP2X01_CS8900_VIRT_BASE, 0};
static unsigned int cs8900_irq_map[] = {IRQ_IXDP2X01_CS8900, 0, 0, 0};
+#elif defined(CONFIG_MACH_QQ2440)
+#include <mach/qq2440.h>
+static unsigned int netcard_portlist[] __used __initdata = { QQ2440_CS8900_VIRT_BASE + 0x300, 0 };
+static unsigned int cs8900_irq_map[] = { QQ2440_CS8900_IRQ, 0, 0, 0 };
#elif defined(CONFIG_MACH_MX31ADS)
#include <mach/board-mx31ads.h>
static unsigned int netcard_portlist[] __used __initdata = {
@@ -521,6 +528,10 @@
#endif
lp->force = g_cs89x0_media__force;
#endif
+
+#if defined(CONFIG_MACH_QQ2440)
+ lp->force |= FORCE_RJ45 | FORCE_FULL;
+#endif
}
/* Grab the region so we can find another board if autoIRQ fails. */
Index: arm-2.6.git/drivers/net/Kconfig
===================================================================
--- arm-2.6.git.orig/drivers/net/Kconfig 2011-02-07 17:04:06.000000000 +0000
+++ arm-2.6.git/drivers/net/Kconfig 2011-02-07 17:04:42.000000000 +0000
@@ -1498,7 +1498,7 @@
config CS89x0
tristate "CS89x0 support"
depends on NET_ETHERNET && (ISA || EISA || MACH_IXDP2351 \
- || ARCH_IXDP2X01 || MACH_MX31ADS)
+ || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440)
---help---
Support for CS89x0 chipset based Ethernet cards. If you have a
network (Ethernet) card of this type, say Y and read the
@@ -1512,7 +1512,7 @@
config CS89x0_NONISA_IRQ
def_bool y
depends on CS89x0 != n
- depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS
+ depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440
config TC35815
tristate "TOSHIBA TC35815 Ethernet support"
^ permalink raw reply
* Re: kernel 2.6.37 : oops in cleanup_once
From: Eric Dumazet @ 2011-03-14 13:14 UTC (permalink / raw)
To: Yann Dupont; +Cc: linux-kernel, netdev
In-Reply-To: <4D7DF1A3.6020501@univ-nantes.fr>
Le lundi 14 mars 2011 à 11:44 +0100, Yann Dupont a écrit :
> Le 02/02/2011 16:08, Eric Dumazet a écrit :
>
>
> > I suspect a mem corruption from another layer (not inetpeer)
> >
> > Unfortunately many kmem caches share the "64 bytes" cache.
> >
> > Could you please add "slub_nomerge" on your boot command ?
> >
> ...
>
> >
> >> -Is there a very severe impact on performance ?
> >>
> > not at all
> >
> Maybe there is an impact after all : since then, we don't have problems
> anymore !
>
> linkwood:~# uptime
> 11:42:03 up 39 days, 17:08, 3 users, load average: 0.01, 0.03, 0.05
>
> So... could slub_nomerge hide or simply avoid the problem ?
> Or are we just lucky this time ?
>
>
I would say you are lucky ;)
Not all memory corruptions are noticed. Sometimes it touch unused parts
of memory, or some parts with no critical content.
^ permalink raw reply
* [RFC v3 0/6] CAN: add SAE J1939 protocol
From: Kurt Van Dijck @ 2011-03-14 13:20 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
This series adds SAE J1939 support to the current net-next-2.6.
For the rtnetlink operations to work, a 2.6.38-rc1 is required.
1/6: can: extend sockaddr_can to include j1939 members
2/6: can: add rtnetlink support
3/6: can: make struct proto const
4/6: j1939: initial import of SAE J1939
5/6: j1939: add documentation & MAINTAINERS
6/6: iproute2: use CAN & J1939
Still a lot of things probably need explanation...
Kind regards,
Kurt Van Dijck
EIA Electronics
^ permalink raw reply
* [patch 0/1] s390: qeth patch for net-next
From: frank.blaschka @ 2011-03-14 13:20 UTC (permalink / raw)
To: davem; +Cc: netdev, linux-s390
Hi Dave,
here is another qeth patch for net-next.
shortlog:
Frank Blaschka (1)
qeth: change some configurations defaults
Thanks,
Frank
^ permalink raw reply
* [patch 1/1] [PATCH] qeth: change some configurations defaults
From: frank.blaschka @ 2011-03-14 13:20 UTC (permalink / raw)
To: davem; +Cc: netdev, linux-s390
In-Reply-To: <20110314132053.132019386@de.ibm.com>
[-- Attachment #1: 603-qeth-defaults.diff --]
[-- Type: text/plain, Size: 2524 bytes --]
From: Frank Blaschka <frank.blaschka@de.ibm.com>
This patch turns on RX checksum and GRO by default. To improve
receiving performance and reduce congestion in case of network
bursts we also increase the default number of inbound buffers.
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
---
drivers/s390/net/qeth_core.h | 3 ++-
drivers/s390/net/qeth_core_main.c | 5 ++++-
drivers/s390/net/qeth_l3_main.c | 5 +++++
3 files changed, 11 insertions(+), 2 deletions(-)
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -225,7 +225,8 @@ static inline int qeth_is_ipa_enabled(st
/*****************************************************************************/
#define QETH_MAX_QUEUES 4
#define QETH_IN_BUF_SIZE_DEFAULT 65536
-#define QETH_IN_BUF_COUNT_DEFAULT 16
+#define QETH_IN_BUF_COUNT_DEFAULT 64
+#define QETH_IN_BUF_COUNT_HSDEFAULT 128
#define QETH_IN_BUF_COUNT_MIN 8
#define QETH_IN_BUF_COUNT_MAX 128
#define QETH_MAX_BUFFER_ELEMENTS(card) ((card)->qdio.in_buf_size >> 12)
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1026,7 +1026,10 @@ static void qeth_init_qdio_info(struct q
atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
/* inbound */
card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
- card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT;
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_HSDEFAULT;
+ else
+ card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT;
card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count;
INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list);
INIT_LIST_HEAD(&card->qdio.init_pool.entry_list);
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -3392,6 +3392,8 @@ static int qeth_l3_setup_netdev(struct q
if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
card->dev->dev_id = card->info.unique_id &
0xffff;
+ if (!card->info.guestlan)
+ card->dev->features |= NETIF_F_GRO;
}
} else if (card->info.type == QETH_CARD_TYPE_IQD) {
card->dev = alloc_netdev(0, "hsi%d", ether_setup);
@@ -3430,6 +3432,9 @@ static int qeth_l3_probe_device(struct c
card->discipline.output_handler = (qdio_handler_t *)
qeth_qdio_output_handler;
card->discipline.recover = qeth_l3_recover;
+ if ((card->info.type == QETH_CARD_TYPE_OSD) ||
+ (card->info.type == QETH_CARD_TYPE_OSX))
+ card->options.checksum_type = HW_CHECKSUMMING;
return 0;
}
^ permalink raw reply
* [PATCH] pktgen: fix transmission headers with frags=0
From: Daniel Turull @ 2011-03-14 13:22 UTC (permalink / raw)
To: netdev; +Cc: Robert Olsson, Jens Laas, Voravit Tanyingyong, Daniel Turull
The headers of pktgen were incorrectly added in a pktgen packet
without frags (frags=0). There was an offset in the pktgen headers.
The cause was in reusing the pgh variable as a return variable in skb_put
when adding the payload to the skb.
A rename of the variable is done.
Signed-off-by: Daniel Turull <daniel.turull@gmail.com>
---
The PKTGEN magic (be9b e955) now starts in the correct offset.
Before the patch, it was starting at the end of the packet (be9b)
Capture from tcpdump:
before patch:
14:57:37.854812 IP 10.0.0.2.discard > 10.254.254.84.discard: UDP, length 18
0x0000: 001b 2157 ed84 001b 215d 01d0 0800 4500
0x0010: 002e 0004 0000 2011 8767 0a00 0002 0afe
0x0020: fe54 0009 0009 001a 0000 0000 0000 b072
0x0030: 9102 00ea ffff 0010 0000 be9b
after patch:
14:44:32.896048 IP 10.0.0.2.discard > 10.217.234.56.discard: UDP, length 18
0x0000: 001b 2157 ed84 001b 215d 01d0 0800 4500
0x0010: 002e 0000 0000 2011 9bac 0a00 0002 0ad9
0x0020: ea38 0009 0009 001a 0000 be9b e955 0000
0x0030: 0001 4d7e 1b09 0005 5f23 af00
---
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index f0aec6c..5baa9d9 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2615,13 +2615,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
{
struct timeval timestamp;
struct pktgen_hdr *pgh;
+ void *data;
pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
datalen -= sizeof(*pgh);
if (pkt_dev->nfrags <= 0) {
- pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
- memset(pgh + 1, 0, datalen);
+ data = skb_put(skb, datalen);
+ memset(data + 1, 0, datalen);
} else {
int frags = pkt_dev->nfrags;
int i, len;
^ permalink raw reply related
* [RFC v3 1/6] can: extend sockaddr_can to include j1939 members
From: Kurt Van Dijck @ 2011-03-14 13:24 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>
This patch prepares struct sockaddr_can for SAE J1939.
The size of this structure increases. To stay binary compatible,
the required_size macro is introduced for existing CAN protocols.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/include/linux/can.h b/include/linux/can.h
index d183333..9c2523c 100644
--- a/include/linux/can.h
+++ b/include/linux/can.h
@@ -67,7 +67,8 @@ struct can_frame {
#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
#define CAN_MCNET 5 /* Bosch MCNet */
#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
-#define CAN_NPROTO 7
+#define CAN_J1939 7 /* SAE J1939 */
+#define CAN_NPROTO 8
#define SOL_CAN_BASE 100
@@ -84,6 +85,23 @@ struct sockaddr_can {
/* transport protocol class address information (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
+ /* J1939 address information */
+ struct {
+ /* 8 byte name when using dynamic addressing */
+ __u64 name;
+ /*
+ * pgn:
+ * 8bit: PS in PDU2 case, else 0
+ * 8bit: PF
+ * 1bit: DP
+ * 1bit: reserved
+ */
+ __u32 pgn;
+
+ /* 1byte address */
+ __u8 addr;
+ } j1939;
+
/* reserved for future CAN protocols address information */
} can_addr;
};
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 6c507be..653c33e 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -42,6 +42,15 @@ struct can_proto {
struct proto *prot;
};
+/*
+ * required_size
+ * macro to find the minimum size of a struct
+ * that includes a requested member
+ */
+#define required_size(member, struct_type) \
+ (offsetof(typeof(struct_type), member) + \
+ sizeof(((typeof(struct_type) *)(0))->member))
+
/* function prototypes for the CAN networklayer core (af_can.c) */
extern int can_proto_register(struct can_proto *cp);
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 092dc88..b286e45 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1256,7 +1256,7 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sockaddr_can *addr =
(struct sockaddr_can *)msg->msg_name;
- if (msg->msg_namelen < sizeof(*addr))
+ if (msg->msg_namelen < required_size(can_ifindex, *addr))
return -EINVAL;
if (addr->can_family != AF_CAN)
@@ -1493,7 +1493,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
- if (len < sizeof(*addr))
+ if (len < required_size(can_ifindex, *addr))
return -EINVAL;
if (bo->bound)
diff --git a/net/can/raw.c b/net/can/raw.c
index 883e9d7..a5f1f41 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -350,7 +350,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
int err = 0;
int notify_enetdown = 0;
- if (len < sizeof(*addr))
+ if (len < required_size(can_ifindex, *addr))
return -EINVAL;
lock_sock(sk);
@@ -649,7 +649,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sockaddr_can *addr =
(struct sockaddr_can *)msg->msg_name;
- if (msg->msg_namelen < sizeof(*addr))
+ if (msg->msg_namelen < required_size(can_ifindex, *addr))
return -EINVAL;
if (addr->can_family != AF_CAN)
^ permalink raw reply related
* [RFC v3 2/6] can: add rtnetlink support
From: Kurt Van Dijck @ 2011-03-14 13:26 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>
This patch adds rtnetlink support for AF_CAN. This support is really
a multiplexer towards the different CAN protocols.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 653c33e..430c446 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -18,6 +18,7 @@
#include <linux/can.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
+#include <net/rtnetlink.h>
#define CAN_VERSION "20090105"
@@ -40,6 +41,28 @@ struct can_proto {
int protocol;
struct proto_ops *ops;
struct proto *prot;
+
+ const struct rtnl_af_ops *rtnl_link_ops;
+ /*
+ * hooks for rtnl hooks
+ * for the *dump* functions, cb->args[0] is reserved
+ * for use by af_can.c, so keep your fingers off.
+ */
+ rtnl_doit_func rtnl_new_addr;
+ rtnl_doit_func rtnl_del_addr;
+ rtnl_dumpit_func rtnl_dump_addr;
+};
+
+/*
+ * this is quite a dirty hack:
+ * reuse the second byte of a rtnetlink msg
+ * to indicate the precise protocol.
+ * The major problem is that is may conflict
+ * with the prefixlen in struct ifaddrmsg.
+ */
+struct rtgencanmsg {
+ unsigned char rtgen_family;
+ unsigned char can_protocol;
};
/*
@@ -53,8 +76,8 @@ struct can_proto {
/* function prototypes for the CAN networklayer core (af_can.c) */
-extern int can_proto_register(struct can_proto *cp);
-extern void can_proto_unregister(struct can_proto *cp);
+extern int can_proto_register(const struct can_proto *cp);
+extern void can_proto_unregister(const struct can_proto *cp);
extern int can_rx_register(struct net_device *dev, canid_t can_id,
canid_t mask,
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 702be5a..db59c6e 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -3,6 +3,7 @@
* (used by different CAN protocol modules)
*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * Copyright (C) 2011 Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -69,7 +70,8 @@ static __initdata const char banner[] = KERN_INFO
MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>, "
- "Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>");
+ "Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>, "
+ "Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>");
MODULE_ALIAS_NETPROTO(PF_CAN);
@@ -84,7 +86,7 @@ static DEFINE_SPINLOCK(can_rcvlists_lock);
static struct kmem_cache *rcv_cache __read_mostly;
/* table of registered CAN protocols */
-static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static const struct can_proto *proto_tab[CAN_NPROTO];
static DEFINE_SPINLOCK(proto_tab_lock);
struct timer_list can_stattimer; /* timer for statistics update */
@@ -92,6 +94,48 @@ struct s_stats can_stats; /* packet statistics */
struct s_pstats can_pstats; /* receive list statistics */
/*
+ * can protocol lookup
+ */
+#define CGP_F_LOAD 0x01 /* try to load protocol when absent */
+static inline const struct can_proto *can_get_proto(int protocol, int flags)
+{
+ const struct can_proto *cp;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return ERR_PTR(-EINVAL);
+
+#ifdef CONFIG_MODULES
+ /* try to load protocol module kernel is modular */
+ if (!proto_tab[protocol] && (flags & CGP_F_LOAD)) {
+ int ret;
+
+ ret = request_module("can-proto-%d", protocol);
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret)
+ pr_err_ratelimited("can: request_module "
+ "(can-proto-%d) failed.\n", protocol);
+ }
+#endif
+
+ spin_lock(&proto_tab_lock);
+ cp = proto_tab[protocol];
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = 0;
+ spin_unlock(&proto_tab_lock);
+ if (!cp)
+ cp = ERR_PTR(-ENOPROTOOPT);
+ return cp;
+}
+
+static inline void can_put_proto(const struct can_proto *cp)
+{
+ module_put(cp->prot->owner);
+}
+/*
* af_can socket functions
*/
@@ -118,46 +162,22 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
- struct can_proto *cp;
+ const struct can_proto *cp;
int err = 0;
sock->state = SS_UNCONNECTED;
- if (protocol < 0 || protocol >= CAN_NPROTO)
- return -EINVAL;
-
if (!net_eq(net, &init_net))
return -EAFNOSUPPORT;
-#ifdef CONFIG_MODULES
- /* try to load protocol module kernel is modular */
- if (!proto_tab[protocol]) {
- err = request_module("can-proto-%d", protocol);
-
- /*
- * In case of error we only print a message but don't
- * return the error code immediately. Below we will
- * return -EPROTONOSUPPORT
- */
- if (err && printk_ratelimit())
- printk(KERN_ERR "can: request_module "
- "(can-proto-%d) failed.\n", protocol);
- }
-#endif
-
- spin_lock(&proto_tab_lock);
- cp = proto_tab[protocol];
- if (cp && !try_module_get(cp->prot->owner))
- cp = NULL;
- spin_unlock(&proto_tab_lock);
-
+ cp = can_get_proto(protocol, CGP_F_LOAD);
/* check for available protocol and correct usage */
- if (!cp)
- return -EPROTONOSUPPORT;
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
if (cp->type != sock->type) {
- err = -EPROTONOSUPPORT;
+ err = -ESOCKTNOSUPPORT;
goto errout;
}
@@ -182,7 +202,7 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
}
errout:
- module_put(cp->prot->owner);
+ can_put_proto(cp);
return err;
}
@@ -678,7 +698,7 @@ drop:
* -EBUSY protocol already in use
* -ENOBUF if proto_register() fails
*/
-int can_proto_register(struct can_proto *cp)
+int can_proto_register(const struct can_proto *cp)
{
int proto = cp->protocol;
int err = 0;
@@ -718,7 +738,7 @@ EXPORT_SYMBOL(can_proto_register);
* can_proto_unregister - unregister CAN transport protocol
* @cp: pointer to CAN protocol structure
*/
-void can_proto_unregister(struct can_proto *cp)
+void can_proto_unregister(const struct can_proto *cp)
{
int proto = cp->protocol;
@@ -809,6 +829,206 @@ static struct notifier_block can_netdev_notifier __read_mostly = {
.notifier_call = can_notifier,
};
+/*
+ * RTNETLINK
+ */
+static int can_rtnl_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ int ret, protocol;
+ const struct can_proto *cp;
+ rtnl_doit_func fn;
+
+ protocol = ((struct rtgencanmsg *)NLMSG_DATA(nlh))->can_protocol;
+ /* since rtnl_lock is held, dont try to load protocol */
+ cp = can_get_proto(protocol, 0);
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+
+ switch (nlh->nlmsg_type) {
+ case RTM_NEWADDR:
+ fn = cp->rtnl_new_addr;
+ break;
+ case RTM_DELADDR:
+ fn = cp->rtnl_del_addr;
+ break;
+ default:
+ fn = 0;
+ break;
+ }
+ if (fn)
+ ret = fn(skb, nlh, arg);
+ else
+ ret = -EPROTONOSUPPORT;
+ can_put_proto(cp);
+ return ret;
+}
+
+static int can_rtnl_dumpit(struct sk_buff *skb, struct netlink_callback *cb,
+ int offset)
+{
+ int ret, j;
+ const struct can_proto *cp;
+ rtnl_dumpit_func fn;
+
+ ret = 0;
+ for (j = cb->args[0]; j < CAN_NPROTO; ++j) {
+ /* save state */
+ cb->args[0] = j;
+ cp = can_get_proto(j, 0);
+ if (IS_ERR(cp))
+ /* we are looping, any error is our own fault */
+ continue;
+ fn = *((rtnl_dumpit_func *)(&((const uint8_t *)cp)[offset]));
+ if (fn)
+ ret = fn(skb, cb);
+ can_put_proto(cp);
+ if (ret < 0)
+ /* suspend this skb */
+ return ret;
+ }
+ return ret;
+}
+
+static int can_rtnl_dump_addr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ return can_rtnl_dumpit(skb, cb,
+ offsetof(struct can_proto, rtnl_dump_addr));
+}
+
+/*
+ * LINK AF properties
+ */
+static size_t can_get_link_af_size(const struct net_device *dev)
+{
+ int ret, j, total;
+ const struct can_proto *cp;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return 0;
+
+ total = 0;
+ for (j = 0; j < CAN_NPROTO; ++j) {
+ cp = can_get_proto(j, 0);
+ if (IS_ERR(cp))
+ /* no worry */
+ continue;
+ ret = 0;
+ if (cp->rtnl_link_ops && cp->rtnl_link_ops->get_link_af_size)
+ ret = cp->rtnl_link_ops->get_link_af_size(dev) +
+ nla_total_size(sizeof(struct nlattr));
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ total += ret;
+ }
+ return nla_total_size(total);
+}
+
+static int can_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+{
+ int ret, j, n;
+ struct nlattr *nla;
+ const struct can_proto *cp;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -ENODATA;
+
+ n = 0;
+ for (j = 0; j < CAN_NPROTO; ++j) {
+ cp = can_get_proto(j, 0);
+ if (IS_ERR(cp))
+ /* no worry */
+ continue;
+ if (cp->rtnl_link_ops && cp->rtnl_link_ops->fill_link_af) {
+ nla = nla_nest_start(skb, j);
+ if (!nla)
+ goto nla_put_failure;
+
+ ret = cp->rtnl_link_ops->fill_link_af(skb, dev);
+ /*
+ * Caller may return ENODATA to indicate that there
+ * was no data to be dumped. This is not an error, it
+ * means we should trim the attribute header and
+ * continue.
+ */
+ if (ret == -ENODATA)
+ nla_nest_cancel(skb, nla);
+ else if (ret < 0)
+ goto nla_put_failure;
+ nla_nest_end(skb, nla);
+ ++n;
+ }
+ can_put_proto(cp);
+ }
+ return n ? 0 : -ENODATA;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nla);
+ can_put_proto(cp);
+ return -EMSGSIZE;
+}
+
+static int can_validate_link_af(const struct net_device *dev,
+ const struct nlattr *nla)
+{
+ int ret, rem;
+ const struct can_proto *cp;
+ struct nlattr *prot;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -EPROTONOSUPPORT;
+
+ nla_for_each_nested(prot, nla, rem) {
+ cp = can_get_proto(nla_type(prot), 0);
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+ if (!cp->rtnl_link_ops)
+ ret = -EPROTONOSUPPORT;
+ else if (!cp->rtnl_link_ops->validate_link_af)
+ ret = 0;
+ else
+ ret = cp->rtnl_link_ops->validate_link_af(dev, prot);
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int can_set_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+ int ret, rem;
+ const struct can_proto *cp;
+ struct nlattr *prot;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -EPROTONOSUPPORT;
+
+ nla_for_each_nested(prot, nla, rem) {
+ cp = can_get_proto(nla_type(prot), 0);
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+ if (!cp->rtnl_link_ops || !cp->rtnl_link_ops->set_link_af)
+ ret = -EPROTONOSUPPORT;
+ else
+ ret = cp->rtnl_link_ops->set_link_af(dev, prot);
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static struct rtnl_af_ops can_rtnl_af_ops = {
+ .family = AF_CAN,
+ .fill_link_af = can_fill_link_af,
+ .get_link_af_size = can_get_link_af_size,
+ .validate_link_af = can_validate_link_af,
+ .set_link_af = can_set_link_af,
+};
+
+/* exported init */
+
static __init int can_init(void)
{
printk(banner);
@@ -834,6 +1054,11 @@ static __init int can_init(void)
register_netdevice_notifier(&can_netdev_notifier);
dev_add_pack(&can_packet);
+ rtnl_af_register(&can_rtnl_af_ops);
+ rtnl_register(PF_CAN, RTM_NEWADDR, can_rtnl_doit, NULL);
+ rtnl_register(PF_CAN, RTM_DELADDR, can_rtnl_doit, NULL);
+ rtnl_register(PF_CAN, RTM_GETADDR, NULL, can_rtnl_dump_addr);
+
return 0;
}
@@ -844,6 +1069,11 @@ static __exit void can_exit(void)
if (stats_timer)
del_timer(&can_stattimer);
+ rtnl_unregister(PF_CAN, RTM_NEWADDR);
+ rtnl_unregister(PF_CAN, RTM_DELADDR);
+ rtnl_unregister(PF_CAN, RTM_GETADDR);
+ rtnl_af_unregister(&can_rtnl_af_ops);
+
can_remove_proc();
/* protocol unregister */
diff --git a/net/can/bcm.c b/net/can/bcm.c
index b286e45..ac1961d 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1596,7 +1596,7 @@ static struct proto bcm_proto __read_mostly = {
.init = bcm_init,
};
-static struct can_proto bcm_can_proto __read_mostly = {
+static const struct can_proto bcm_can_proto = {
.type = SOCK_DGRAM,
.protocol = CAN_BCM,
.ops = &bcm_ops,
diff --git a/net/can/raw.c b/net/can/raw.c
index a5f1f41..9ad3dfc 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -769,7 +769,7 @@ static struct proto raw_proto __read_mostly = {
.init = raw_init,
};
-static struct can_proto raw_can_proto __read_mostly = {
+static const struct can_proto raw_can_proto = {
.type = SOCK_RAW,
.protocol = CAN_RAW,
.ops = &raw_ops,
^ permalink raw reply related
* Re: [GIT/PATCH v4] xen network backend driver
From: Ian Campbell @ 2011-03-14 13:43 UTC (permalink / raw)
To: Michał Mirosław
Cc: netdev@vger.kernel.org, xen-devel, Ben Hutchings,
Jeremy Fitzhardinge, Herbert Xu, Konrad Rzeszutek Wilk,
Francois Romieu, Michał Mirosław
In-Reply-To: <AANLkTinHyFLRkeb60x4Q6uUw6p43XBjk5rpqCMo49WKF@mail.gmail.com>
On Thu, 2011-03-10 at 17:33 +0000, Michał Mirosław wrote:
> 2011/3/10 Ian Campbell <Ian.Campbell@citrix.com>:
> > The following patch is the fourth iteration of the Xen network backend
> > driver for upstream Linux.
> >
> > This driver ("netback") is the host side counterpart to the frontend
> > driver in drivers/net/xen-netfront.c. The PV protocol is also
> > implemented by frontend drivers in other OSes too, such as the BSDs and
> > even Windows.
> [...]
> > --- /dev/null
> > +++ b/drivers/net/xen-netback/common.h
> > @@ -0,0 +1,162 @@
> [...]
> > +struct xenvif {
> [...]
> > + /* Flags that must not be set in dev->features */
> > + int features_disabled;
> > +
> > + /* Frontend feature information. */
> > + u8 can_sg:1;
> > + u8 gso:1;
> > + u8 gso_prefix:1;
> > + u8 csum:1;
> [...]
> > --- /dev/null
> > +++ b/drivers/net/xen-netback/interface.c
> > @@ -0,0 +1,424 @@
> [...]
> > +static int xenvif_change_mtu(struct net_device *dev, int mtu)
> > +{
> > + struct xenvif *vif = netdev_priv(dev);
> > + int max = vif->can_sg ? 65535 - VLAN_ETH_HLEN : ETH_DATA_LEN;
> > +
> > + if (mtu > max)
> > + return -EINVAL;
> > + dev->mtu = mtu;
> > + return 0;
> > +}
> > +
> > +static void xenvif_set_features(struct xenvif *vif)
> > +{
> > + struct net_device *dev = vif->dev;
> > + int features = dev->features;
> > +
> > + if (vif->can_sg)
> > + features |= NETIF_F_SG;
> > + if (vif->gso || vif->gso_prefix)
> > + features |= NETIF_F_TSO;
> > + if (vif->csum)
> > + features |= NETIF_F_IP_CSUM;
> > +
> > + features &= ~(vif->features_disabled);
> > +
> > + if (!(features & NETIF_F_SG) && dev->mtu > ETH_DATA_LEN)
> > + dev->mtu = ETH_DATA_LEN;
> > +
> > + dev->features = features;
> > +}
> > +
> > +static int xenvif_set_tx_csum(struct net_device *dev, u32 data)
> > +{
> > + struct xenvif *vif = netdev_priv(dev);
> > + if (data) {
> > + if (!vif->csum)
> > + return -EOPNOTSUPP;
> > + vif->features_disabled &= ~NETIF_F_IP_CSUM;
> > + } else {
> > + vif->features_disabled |= NETIF_F_IP_CSUM;
> > + }
> > +
> > + xenvif_set_features(vif);
> > + return 0;
> > +}
> > +
> > +static int xenvif_set_sg(struct net_device *dev, u32 data)
> > +{
> > + struct xenvif *vif = netdev_priv(dev);
> > + if (data) {
> > + if (!vif->can_sg)
> > + return -EOPNOTSUPP;
> > + vif->features_disabled &= ~NETIF_F_SG;
> > + } else {
> > + vif->features_disabled |= NETIF_F_SG;
> > + }
> > +
> > + xenvif_set_features(vif);
> > + return 0;
> > +}
> > +
> > +static int xenvif_set_tso(struct net_device *dev, u32 data)
> > +{
> > + struct xenvif *vif = netdev_priv(dev);
> > + if (data) {
> > + if (!vif->gso && !vif->gso_prefix)
> > + return -EOPNOTSUPP;
> > + vif->features_disabled &= ~NETIF_F_TSO;
> > + } else {
> > + vif->features_disabled |= NETIF_F_TSO;
> > + }
> > +
> > + xenvif_set_features(vif);
> > + return 0;
> > +}
> [...]
>
> You could make this simpler by using netdev->hw_features and
> ndo_fix_features/ndo_set_features calls recently introduced in
> net-next.
Thanks, this does look like it could simplify things. Doesn't look like
its use is widespread (even net-next) yet though?
I'll add it to my TODO list for the future.
> I'll reply with a proof-of-concept patch for xen-netfront.
>
> Best Regards,
> Michał Mirosław
^ permalink raw reply
* [RFC v3 3/6] can: make struct proto const
From: Kurt Van Dijck @ 2011-03-14 13:47 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>
can_ioctl is the only reason for struct proto to be non-const.
script/check-patch.pl suggests struct proto be const.
This patch performs the necessary change.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 430c446..0767cc6 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -39,7 +39,7 @@
struct can_proto {
int type;
int protocol;
- struct proto_ops *ops;
+ const struct proto_ops *ops;
struct proto *prot;
const struct rtnl_af_ops *rtnl_link_ops;
@@ -78,6 +78,8 @@ struct rtgencanmsg {
extern int can_proto_register(const struct can_proto *cp);
extern void can_proto_unregister(const struct can_proto *cp);
+extern int can_sock_ioctl(struct socket *sock, unsigned int cmd,
+ unsigned long arg);
extern int can_rx_register(struct net_device *dev, canid_t can_id,
canid_t mask,
diff --git a/net/can/af_can.c b/net/can/af_can.c
index db59c6e..c1f8c05 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -139,7 +139,7 @@ static inline void can_put_proto(const struct can_proto *cp)
* af_can socket functions
*/
-static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+int can_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
@@ -152,6 +152,7 @@ static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return -ENOIOCTLCMD;
}
}
+EXPORT_SYMBOL(can_sock_ioctl);
static void can_sock_destruct(struct sock *sk)
{
@@ -720,10 +721,6 @@ int can_proto_register(const struct can_proto *cp)
err = -EBUSY;
} else {
proto_tab[proto] = cp;
-
- /* use generic ioctl function if not defined by module */
- if (!cp->ops->ioctl)
- cp->ops->ioctl = can_ioctl;
}
spin_unlock(&proto_tab_lock);
diff --git a/net/can/bcm.c b/net/can/bcm.c
index ac1961d..fd89542 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1569,7 +1569,7 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,
return size;
}
-static struct proto_ops bcm_ops __read_mostly = {
+static const struct proto_ops bcm_ops = {
.family = PF_CAN,
.release = bcm_release,
.bind = sock_no_bind,
@@ -1578,7 +1578,7 @@ static struct proto_ops bcm_ops __read_mostly = {
.accept = sock_no_accept,
.getname = sock_no_getname,
.poll = datagram_poll,
- .ioctl = NULL, /* use can_ioctl() from af_can.c */
+ .ioctl = can_sock_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
diff --git a/net/can/raw.c b/net/can/raw.c
index 9ad3dfc..8581596 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -742,7 +742,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
return size;
}
-static struct proto_ops raw_ops __read_mostly = {
+static const struct proto_ops raw_ops = {
.family = PF_CAN,
.release = raw_release,
.bind = raw_bind,
@@ -751,7 +751,7 @@ static struct proto_ops raw_ops __read_mostly = {
.accept = sock_no_accept,
.getname = raw_getname,
.poll = datagram_poll,
- .ioctl = NULL, /* use can_ioctl() from af_can.c */
+ .ioctl = can_sock_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = raw_setsockopt,
^ permalink raw reply related
* Re: [PATCH] pktgen: fix transmission headers with frags=0
From: Eric Dumazet @ 2011-03-14 13:56 UTC (permalink / raw)
To: Daniel Turull; +Cc: netdev, Robert Olsson, Jens Laas, Voravit Tanyingyong
In-Reply-To: <4D7E167E.2090807@gmail.com>
Le lundi 14 mars 2011 à 14:22 +0100, Daniel Turull a écrit :
> The headers of pktgen were incorrectly added in a pktgen packet
> without frags (frags=0). There was an offset in the pktgen headers.
>
> The cause was in reusing the pgh variable as a return variable in skb_put
> when adding the payload to the skb.
>
> A rename of the variable is done.
>
> Signed-off-by: Daniel Turull <daniel.turull@gmail.com>
> ---
> The PKTGEN magic (be9b e955) now starts in the correct offset.
> Before the patch, it was starting at the end of the packet (be9b)
>
not exactly, but offseted +16 bytes
(sizeof(struct pktgen_hdr))
> Capture from tcpdump:
>
> before patch:
> 14:57:37.854812 IP 10.0.0.2.discard > 10.254.254.84.discard: UDP, length 18
> 0x0000: 001b 2157 ed84 001b 215d 01d0 0800 4500
> 0x0010: 002e 0004 0000 2011 8767 0a00 0002 0afe
> 0x0020: fe54 0009 0009 001a 0000 0000 0000 b072
> 0x0030: 9102 00ea ffff 0010 0000 be9b
>
> after patch:
> 14:44:32.896048 IP 10.0.0.2.discard > 10.217.234.56.discard: UDP, length 18
> 0x0000: 001b 2157 ed84 001b 215d 01d0 0800 4500
> 0x0010: 002e 0000 0000 2011 9bac 0a00 0002 0ad9
> 0x0020: ea38 0009 0009 001a 0000 be9b e955 0000
> 0x0030: 0001 4d7e 1b09 0005 5f23 af00
>
> ---
> diff --git a/net/core/pktgen.c b/net/core/pktgen.c
> index f0aec6c..5baa9d9 100644
> --- a/net/core/pktgen.c
> +++ b/net/core/pktgen.c
> @@ -2615,13 +2615,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
> {
> struct timeval timestamp;
> struct pktgen_hdr *pgh;
> + void *data;
>
> pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
> datalen -= sizeof(*pgh);
>
> if (pkt_dev->nfrags <= 0) {
> - pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
> - memset(pgh + 1, 0, datalen);
> + data = skb_put(skb, datalen);
> + memset(data + 1, 0, datalen);
> } else {
> int frags = pkt_dev->nfrags;
> int i, len;
> --
Good catch !
Hmm this patch is not correct, why memset(data + 1, ...) ?
Also, this patch is needed for net-next-2.6 only
(bug introduced by commit 26ad787962ef84677a48c560
(pktgen: speedup fragmented skbs)
I would avoid the "void *data;" declaration and just use following :
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index f0aec6c..f727c83 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2620,7 +2620,7 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
datalen -= sizeof(*pgh);
if (pkt_dev->nfrags <= 0) {
- pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
+ skb_put(skb, datalen);
memset(pgh + 1, 0, datalen);
} else {
int frags = pkt_dev->nfrags;
or even :
memset(skb_put(skb, datalen), 0, datalen);
^ permalink raw reply related
* [RFC v3 4/6] j1939: initial import of SAE J1939
From: Kurt Van Dijck @ 2011-03-14 13:56 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>
This patch contains the main SAE J1939 stack.
scripts/checkpatch.pl reports 3 errors like:
ERROR: return is not a function, parentheses are not required
#3819: FILE: net/can/j1939/transport.c:246:
+ return (dat[2] << 8) + (dat[1] << 0);
I don't see how to fix the checkpatch.pl error _and_ improving
the code.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/include/linux/can/Kbuild b/include/linux/can/Kbuild
index 8cb05aa..0364eef 100644
--- a/include/linux/can/Kbuild
+++ b/include/linux/can/Kbuild
@@ -2,3 +2,4 @@ header-y += raw.h
header-y += bcm.h
header-y += error.h
header-y += netlink.h
+header-y += j1939.h
diff --git a/include/linux/can/j1939.h b/include/linux/can/j1939.h
new file mode 100644
index 0000000..87d28e9
--- /dev/null
+++ b/include/linux/can/j1939.h
@@ -0,0 +1,100 @@
+/*
+ * j1939.h
+ *
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _J1939_H_
+#define _J1939_H_
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/can.h>
+
+#define J1939_IDLE_ADDR 0xfe
+#define J1939_NO_ADDR 0xff
+#define J1939_NO_NAME 0
+#define J1939_NO_PGN 0x7ffff
+/*
+ * J1939 Parameter Group Number
+ *
+ * bit 0-7 : PDU Specific (PS)
+ * bit 8-15 : PDU Format (PF)
+ * bit 16 : Data Page (DP)
+ * bit 17 : Reserved (R)
+ * bit 19-31 : set to zero
+ */
+typedef __u32 pgn_t;
+
+/*
+ * J1939 Priority
+ *
+ * bit 0-2 : Priority (P)
+ * bit 3-7 : set to zero
+ */
+typedef __u8 priority_t;
+
+/*
+ * J1939 NAME
+ *
+ * bit 0-20 : Identity Number
+ * bit 21-31 : Manufacturer Code
+ * bit 32-34 : ECU Instance
+ * bit 35-39 : Function Instance
+ * bit 40-47 : Function
+ * bit 48 : Reserved
+ * bit 49-55 : Vehicle System
+ * bit 56-59 : Vehicle System Instance
+ * bit 60-62 : Industry Group
+ * bit 63 : Arbitrary Address Capable
+ */
+typedef __u64 name_t;
+
+/*
+ * J1939 socket options
+ */
+#define SOL_CAN_J1939 (SOL_CAN_BASE + CAN_J1939)
+enum {
+ SO_J1939_FILTER = 1, /* set filters */
+ SO_J1939_PROMISC = 2, /* set/clr promiscuous mode */
+ SO_J1939_RECV_OWN = 3,
+ SO_J1939_SEND_PRIO = 4,
+ SO_J1939_DEST_MASK = 5, /* mask names in connect() & sendto() */
+};
+
+enum {
+ SCM_J1939_DEST_ADDR = 1,
+ SCM_J1939_DEST_NAME = 2,
+ SCM_J1939_PRIO = 3,
+};
+
+struct j1939_filter {
+ name_t name;
+ name_t name_mask;
+ __u8 addr;
+ __u8 addr_mask;
+ pgn_t pgn;
+ pgn_t pgn_mask;
+};
+
+/*
+ * RTNETLINK
+ */
+enum {
+ IFLA_J1939_UNSPEC,
+ IFLA_J1939_ENABLE,
+ IFLA_J1939_MAX,
+};
+
+enum {
+ IFA_J1939_UNSPEC,
+ IFA_J1939_ADDR,
+ IFA_J1939_NAME,
+ IFA_J1939_MAX,
+};
+
+#endif /* _J1939_H_ */
diff --git a/net/can/Kconfig b/net/can/Kconfig
index 89395b2..7feb58c 100644
--- a/net/can/Kconfig
+++ b/net/can/Kconfig
@@ -40,5 +40,6 @@ config CAN_BCM
CAN messages are used on the bus (e.g. in automotive environments).
To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
+source "net/can/j1939/Kconfig"
source "drivers/net/can/Kconfig"
diff --git a/net/can/Makefile b/net/can/Makefile
index 2d3894b..953d851 100644
--- a/net/can/Makefile
+++ b/net/can/Makefile
@@ -10,3 +10,5 @@ can-raw-y := raw.o
obj-$(CONFIG_CAN_BCM) += can-bcm.o
can-bcm-y := bcm.o
+
+obj-$(CONFIG_CAN_J1939) += j1939/
diff --git a/net/can/j1939/Kconfig b/net/can/j1939/Kconfig
new file mode 100644
index 0000000..74d2a86
--- /dev/null
+++ b/net/can/j1939/Kconfig
@@ -0,0 +1,22 @@
+#
+# SAE J1939 network layer core configuration
+#
+
+config CAN_J1939
+ tristate "SAE J1939"
+ depends on CAN
+ ---help---
+ SAE J1939
+ Say Y to have in-kernel support for j1939 socket type. This
+ allows communication according to SAE j1939.
+ The relevant parts in kernel are
+ SAE j1939-21 (datalink & transport protocol)
+ & SAE j1939-81 (network management).
+
+config CAN_J1939_DEBUG
+ bool "debug SAE J1939"
+ depends on CAN_J1939
+ default n
+ ---help---
+ Say Y to add extra debug code (via printk) in the j1939 stack
+
diff --git a/net/can/j1939/Makefile b/net/can/j1939/Makefile
new file mode 100644
index 0000000..7ca2fc9
--- /dev/null
+++ b/net/can/j1939/Makefile
@@ -0,0 +1,14 @@
+
+obj-$(CONFIG_CAN_J1939) += can-j1939.o
+
+can-j1939-objs := main.o \
+ proc.o bus.o \
+ rtnl.o \
+ socket.o \
+ address-claim.o transport.o \
+ promisc.o filter.o
+
+ifeq ($(CONFIG_CAN_J1939_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/net/can/j1939/address-claim.c b/net/can/j1939/address-claim.c
new file mode 100644
index 0000000..4793b9f
--- /dev/null
+++ b/net/can/j1939/address-claim.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * J1939 Address Claiming.
+ * Address Claiming in the kernel
+ * - keeps track of the AC states of ECU's,
+ * - resolves NAME<=>SA taking into account the AC states of ECU's.
+ *
+ * All Address Claim msgs (including host-originated msg) are processed
+ * at the receive path (a sent msg is always received again via CAN echo).
+ * As such, the processing of AC msgs is done in the order on which msgs
+ * are sent on the bus.
+ *
+ * This module doesn't send msgs itself (e.g. replies on Address Claims),
+ * this is the responsibility of a user space application or daemon.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/byteorder/generic.h>
+
+#include "j1939-priv.h"
+
+static void ac_sm_step(struct j1939_ecu *ecu, int event, int new_sa);
+
+#define J1939_STACK_MODULE "AC "
+#define j1939_ac_err(...) j1939_err(J1939_STACK_MODULE __VA_ARGS__)
+#define j1939_ac_warning(...) j1939_warning(J1939_STACK_MODULE __VA_ARGS__)
+#define j1939_ac_debug(...) j1939_debug(J1939_STACK_MODULE __VA_ARGS__)
+
+#define PGN_REQUEST 0x00EA00
+#define PGN_ADDRESS_CLAIMED 0x00EE00
+
+#define NULL_ADDRESS 0xFE
+#define BROADCAST 0xFF
+
+#define CANDATA2NAME(data) le64_to_cpu(*(name_t *)(data))
+
+/* ECU address claiming states. */
+enum {
+ STATE_AC_SA_NONE = 0, /* no address claimed */
+ STATE_AC_SA_PENDING, /* address claim pending (waiting for 250ms) */
+ STATE_AC_SA_CLAIMED, /* successfully claimed sa */
+ STATE_AC_MAX /* do not remove */
+};
+
+/* State to name mapping. */
+static const char *st2name[STATE_AC_MAX] = {
+ "sa none",
+ "sa pending",
+ "sa claimed",
+};
+
+/* ECU address claiming events. */
+enum {
+ EVENT_AC_TX, /* tx of ac msg:
+ * msg will be processed at rx side (via CAN intf echo)
+ * but might already block outgoing traffic
+ */
+ EVENT_AC_SA_CC, /* cannot claim sa, 2 reasons:
+ * rx of cannot claim msg or,
+ * rx of address claim msg of conflicting ecu with higher prio
+ */
+ EVENT_AC_SA_CL, /* sa can be claimed, reason:
+ * rx of address claim msg
+ */
+ EVENT_AC_TIMEOUT, /* (250ms) timer expired */
+ EVENT_AC_MAX /* do not remove */
+};
+
+/* Event to name mapping. */
+static const char *ev2name[EVENT_AC_MAX] = {
+ "ac tx",
+ "sa cc",
+ "sa cl",
+ "timeout"
+};
+
+/* Impossible state change: print error. */
+static void ac_sm_bug(struct j1939_ecu *ecu, int new_sa)
+{
+ j1939_ac_err("BUG: invalid state change\n");
+}
+
+/*
+ * Process tx of an ac.
+ * The new_sa will be processed when the msg is received via CAN intf echo.
+ * Don't change source address as it can still be used for at rx side.
+ * Avoid further state changes (by cancelling the timer)
+ * until the ac msg (or another one) is received again.
+ */
+static void ac_sm_tx(struct j1939_ecu *ecu, int new_sa)
+{
+ if (new_sa != ecu->sa) {
+ hrtimer_try_to_cancel(&ecu->ac_timer);
+ ecu->block_tx = 1;
+ }
+}
+
+/*
+ * Process cannot claim sa
+ * as a result of cannot claim msg or lower prio name.
+ */
+static void ac_sm_sa_cc(struct j1939_ecu *ecu, int new_sa)
+{
+ hrtimer_try_to_cancel(&ecu->ac_timer);
+ ecu->ac_state = STATE_AC_SA_NONE;
+ j1939_ecu_set_address(ecu, -1);
+}
+
+/*
+ * Process address claim.
+ */
+static void ac_sm_sa_cl(struct j1939_ecu *ecu, int new_sa)
+{
+ if (ecu->sa == new_sa)
+ return;
+
+ hrtimer_try_to_cancel(&ecu->ac_timer);
+ ecu->ac_state = STATE_AC_SA_PENDING;
+ j1939_ecu_set_address(ecu, new_sa);
+ hrtimer_start(&ecu->ac_timer, ktime_set(0, 250000000),
+ HRTIMER_MODE_REL);
+}
+
+/*
+ * Process timer expiry.
+ */
+static void ac_sm_timeout(struct j1939_ecu *ecu, int new_sa)
+{
+ ecu->ac_state = STATE_AC_SA_CLAIMED;
+ ecu->block_tx = 0;
+}
+
+/*
+ * Address Claiming state machine.
+ * Redundant (unnecessary) state transitions get NULL.
+ * Invalid (impossible) state transitions dump an error (ac_sm_bug).
+ */
+static void (*const ac_sm[STATE_AC_MAX][EVENT_AC_MAX])
+ (struct j1939_ecu *ecu, int new_sa) = {
+ /* TX, SA_CC, SA_CL, TIMEOUT */
+ [STATE_AC_SA_NONE] = {
+ NULL, NULL, ac_sm_sa_cl, ac_sm_bug, },
+ [STATE_AC_SA_PENDING] = {
+ ac_sm_tx, ac_sm_sa_cc, ac_sm_sa_cl, ac_sm_timeout, },
+ [STATE_AC_SA_CLAIMED] = {
+ ac_sm_tx, ac_sm_sa_cc, ac_sm_sa_cl, ac_sm_bug, },
+};
+
+/*
+ * Progress state machine one step.
+ */
+static void ac_sm_step(struct j1939_ecu *ecu, int event, int new_sa)
+{
+ char *str;
+ char out[100];
+
+ str = out;
+ memset(out, '\0', 100);
+
+ BUG_ON(!ecu);
+ BUG_ON(event >= EVENT_AC_MAX || event < 0);
+
+ write_lock_bh(&ecu->ac_lock);
+
+ str += sprintf(str, "%i:%016llx (%02x,%s)",
+ ecu->ifindex, ecu->name,
+ (ecu->sa > 0) ? ecu->sa : 0x1ff,
+ st2name[ecu->ac_state]);
+ str += sprintf(str, " =[%s,0x%02x]=> ", ev2name[event], new_sa);
+
+ if (ac_sm[ecu->ac_state][event])
+ ac_sm[ecu->ac_state][event](ecu, new_sa);
+
+ str += sprintf(str, "(%02x,%s)",
+ (ecu->sa > 0) ? ecu->sa : 0x1ff,
+ st2name[ecu->ac_state]);
+
+ write_unlock_bh(&ecu->ac_lock);
+
+ j1939_ac_debug("%s\n", out);
+}
+
+static inline int ac_msg_is_address_claimed(pgn_t pgn, int sa)
+{
+ return (pgn == PGN_ADDRESS_CLAIMED) && (sa != NULL_ADDRESS);
+}
+
+static inline int ac_msg_is_cannot_claim(pgn_t pgn, int sa)
+{
+ return (pgn == PGN_ADDRESS_CLAIMED) && (sa == NULL_ADDRESS);
+}
+
+static inline int ac_msg_is_request_for_address_claimed(pgn_t pgn,
+ const unsigned char *d, unsigned int len)
+{
+ pgn_t req_pgn;
+
+ if (len < 3)
+ return 0;
+ /* Little Endian safe */
+ req_pgn = (d[0] << 0) | (d[1] << 8) | (d[2] << 16);
+ return (pgn == PGN_REQUEST) && (PGN_ADDRESS_CLAIMED == req_pgn);
+}
+
+/*
+ * Resolve a NAME to a SA, taking into account ac state.
+ * Returns invalid sa (<0) in case of failure.
+ */
+static int ac_name2sa(name_t name, int ifindex, int check_block_tx)
+{
+ int sa;
+ struct j1939_ecu *ecu = j1939_ecu_find_by_name(name, ifindex);
+
+ if (!ecu)
+ return -1;
+
+ read_lock_bh(&ecu->ac_lock);
+ if (ecu->ac_state == STATE_AC_SA_CLAIMED) {
+ if (check_block_tx && ecu->block_tx)
+ sa = -1;
+ else
+ sa = ecu->sa;
+ } else {
+ sa = -1;
+ }
+ read_unlock_bh(&ecu->ac_lock);
+
+ put_j1939_ecu(ecu);
+ return sa;
+}
+
+/*
+ * Resolve SA to NAME, taking into account ac state.
+ * Returns invalid name (=0) in case of failure.
+ */
+static name_t ac_sa2name(int sa, int ifindex)
+{
+ name_t name;
+ struct j1939_ecu *ecu = j1939_ecu_find_by_addr(sa, ifindex);
+
+ if (!ecu)
+ return 0;
+
+ read_lock_bh(&ecu->ac_lock);
+ if (ecu->ac_state == STATE_AC_SA_CLAIMED)
+ name = ecu->name;
+ else
+ name = 0;
+ read_unlock_bh(&ecu->ac_lock);
+
+ put_j1939_ecu(ecu);
+ return name;
+}
+
+static int ac_process_outgoing_address_claim(struct j1939_sk_buff_cb *sk_addr,
+ unsigned char *data, unsigned int len)
+{
+ int err = 0;
+ struct j1939_ecu *ecu;
+
+ if (len != 8) {
+ j1939_ac_warning("tx: invalid data length\n");
+ return -EPROTO;
+ }
+
+ if (sk_addr->src.name != CANDATA2NAME(data)) {
+ j1939_ac_warning("tx: src name (0x%016llx) != "
+ "data (0x%016llx)\n",
+ (long long)sk_addr->src.name,
+ (long long)CANDATA2NAME(data));
+ return -EPROTO;
+ }
+
+ if (sk_addr->src.addr == BROADCAST) {
+ j1939_ac_warning("tx: address claim with broadcast sa\n");
+ return -EPROTO;
+ }
+
+ /* ac must always be a broadcast */
+ if (sk_addr->dst.addr != BROADCAST) {
+ j1939_ac_warning("tx: address claim with da, not broadcast\n");
+ return -EPROTO;
+ }
+
+ /* if no matching ecu (name, ifindex) is found
+ * then ecu will be created at the receive path of the AC msg. */
+ ecu = j1939_ecu_find_by_name(sk_addr->src.name, sk_addr->ifindex);
+
+ if (ecu) {
+ ac_sm_step(ecu, EVENT_AC_TX, sk_addr->src.addr);
+ put_j1939_ecu(ecu);
+ }
+ return err;
+}
+
+int j1939_send_address_claim(struct sk_buff *skb)
+{
+ int ret;
+ int sa;
+ struct j1939_sk_buff_cb *sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+
+ /*
+ * network mgmt: address claiming msgs
+ */
+ if (sk_addr->pgn == PGN_ADDRESS_CLAIMED) {
+ ret = ac_process_outgoing_address_claim(sk_addr, skb->data,
+ skb->len);
+ if (ret < 0)
+ j1939_ac_warning("tx drop: invalid address claim\n");
+ /* return both when failure & when successfull */
+ return ret;
+ }
+
+ /*
+ * assign source address
+ */
+ if (sk_addr->src.name) {
+ sa = ac_name2sa(sk_addr->src.name, sk_addr->ifindex,
+ 1/* check block tx*/);
+ if (sa >= 0)
+ sk_addr->src.addr = sa;
+ else if (ac_msg_is_request_for_address_claimed(sk_addr->pgn,
+ skb->data, skb->len))
+ sk_addr->src.addr = NULL_ADDRESS;
+ else {
+ j1939_ac_warning("tx drop: invalid sa for name "
+ "0x%016llx\n", sk_addr->src.name);
+ return -EADDRNOTAVAIL;
+ }
+ }
+
+ /*
+ * assign destination address
+ */
+ if (sk_addr->dst.name) {
+ sa = ac_name2sa(sk_addr->dst.name, sk_addr->ifindex, 0);
+ if (sa >= 0) {
+ sk_addr->dst.addr = sa;
+ } else {
+ j1939_ac_warning("tx drop: invalid da for name "
+ "0x%016llx\n", sk_addr->dst.name);
+ return -EADDRNOTAVAIL;
+ }
+ }
+ return 0;
+}
+
+static struct j1939_ecu *ac_ecu_get(name_t name, int ifindex, uint8_t new_sa)
+{
+ struct j1939_ecu *ecu;
+ int ret;
+
+ ecu = j1939_ecu_find_by_name(name, ifindex);
+ if (ecu)
+ return ecu;
+
+ ecu = j1939_ecu_alloc(ifindex, name);
+ if (!ecu) {
+ j1939_ac_err("rx: failed to alloc ecu\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ ecu->flags |= ECUFLAG_REMOTE;
+ ecu->sa = new_sa;
+ ret = j1939_ecu_register(ecu);
+ if (ret < 0) {
+ j1939_ac_err("rx: failed to register ecu\n");
+ goto fail_free;
+ }
+
+ if (!get_j1939_ecu(ecu)) {
+ ret = -EINVAL;
+ j1939_ac_err("rx: failed to incr ref of ecu\n");
+ goto fail_unreg;
+ }
+
+ return ecu;
+
+fail_unreg:
+ j1939_ecu_unregister(ecu);
+fail_free:
+ j1939_ecu_free(ecu);
+fail:
+ return ERR_PTR(ret);
+}
+
+/*
+ * Update the ac state of an ecu and resolves conflicts.
+ */
+static void ac_ecu_update(struct j1939_ecu *ecu, int new_sa)
+{
+ int event = EVENT_AC_MAX;
+
+ if (new_sa == NULL_ADDRESS) {
+ /* cannot claim */
+ event = EVENT_AC_SA_CC;
+ } else {
+ /* claim for sa => resolve conflicts */
+ int ev_conflict = EVENT_AC_MAX;
+ struct j1939_ecu *ecu_conflict;
+
+ ecu_conflict = j1939_ecu_find_by_addr(new_sa, ecu->ifindex);
+ if (ecu_conflict == ecu) {
+ put_j1939_ecu(ecu_conflict);
+ ecu_conflict = NULL;
+ }
+
+ if (!ecu_conflict) {
+ event = EVENT_AC_SA_CL;
+ } else if (!ecu_conflict->name) {
+ /*
+ * a local, static ecu was using this address
+ * This is stupid.
+ * There's no point in contesting the address,
+ * assume noone had claimed it
+ * (which is in fact, true).
+ * Both Pieter & Kurt are not really sure
+ * EVENT_AC_SA_CL is the right thing to do here.
+ */
+ j1939_ac_err("bus conflict on 0x%02x "
+ "with static ECU\n", new_sa);
+ event = EVENT_AC_SA_CL;
+ } else if (ecu->name < ecu_conflict->name) {
+ ev_conflict = EVENT_AC_SA_CC;
+ event = EVENT_AC_SA_CL;
+ } else if (ecu_conflict->name < ecu->name) {
+ ev_conflict = EVENT_AC_SA_CL;
+ event = EVENT_AC_SA_CC;
+ } else {
+ if (ecu_conflict)
+ put_j1939_ecu(ecu_conflict);
+ j1939_ac_err("2 ecu's with same name on bus?\n");
+ return;
+ }
+ if (ecu_conflict && ecu_conflict->name)
+ ac_sm_step(ecu_conflict, ev_conflict, new_sa);
+ if (ecu_conflict)
+ put_j1939_ecu(ecu_conflict);
+ }
+ ac_sm_step(ecu, event, new_sa);
+}
+
+static int ac_process_incoming_address_claim(struct j1939_sk_buff_cb *sk_addr,
+ unsigned char *data, unsigned int len)
+{
+ struct j1939_ecu *ecu;
+ name_t name;
+
+ if (len < 8) {
+ j1939_ac_warning("rx: address claim with dlc < 8\n");
+ return -EPROTO;
+ }
+
+ name = CANDATA2NAME(data);
+ if (!name) {
+ j1939_ac_warning("rx: address claim without name\n");
+ return -EPROTO;
+ }
+
+ if (!j1939_address_is_valid(sk_addr->src.addr)) {
+ j1939_ac_warning("rx: address claim with broadcast sa\n");
+ return -EPROTO;
+ }
+
+ sk_addr->src.name = name;
+ ecu = ac_ecu_get(name, sk_addr->ifindex, sk_addr->src.addr);
+ if (IS_ERR(ecu))
+ return PTR_ERR(ecu);
+
+ sk_addr->src.flags = ecu->flags;
+ ecu->rxtime = ktime_get();
+ ac_ecu_update(ecu, sk_addr->src.addr);
+
+ put_j1939_ecu(ecu);
+ return 0;
+}
+
+int j1939_recv_address_claim(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+
+ /*
+ * network mgmt
+ */
+ if (sk_addr->pgn == PGN_ADDRESS_CLAIMED) {
+ int ret;
+ ret = ac_process_incoming_address_claim(sk_addr,
+ skb->data, skb->len);
+ if (ret)
+ return ret;
+ } else if (j1939_address_is_unicast(sk_addr->src.addr)) {
+ /*
+ * assign source name
+ */
+ sk_addr->src.name =
+ ac_sa2name(sk_addr->src.addr, sk_addr->ifindex);
+ }
+
+ /*
+ * assign dest name
+ */
+ if (j1939_address_is_unicast(sk_addr->dst.addr))
+ sk_addr->dst.name =
+ ac_sa2name(sk_addr->dst.addr, sk_addr->ifindex);
+
+ return 0;
+}
+
+enum hrtimer_restart j1939_ac_timer_handler(struct hrtimer *hrtimer)
+{
+ struct j1939_ecu *ecu =
+ container_of(hrtimer, struct j1939_ecu, ac_timer);
+
+ ac_sm_step(ecu, EVENT_AC_TIMEOUT, ecu->sa);
+ return HRTIMER_NORESTART;
+}
+
diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c
new file mode 100644
index 0000000..78973c2
--- /dev/null
+++ b/net/can/j1939/bus.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * j1939-bus.c - bus for j1939 remote devices
+ * Since rtnetlink, no real bus is used.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+
+#include "j1939-priv.h"
+
+#define jseg_dbg(jseg, fmt, ...) \
+ pr_debug("j1939-%i: " fmt, (jseg)->ifindex, ##__VA_ARGS__)
+
+#define ecu_dbg(ecu, fmt, ...) \
+ pr_debug("j1939-%i,%016llx,%02x: " fmt, (ecu)->ifindex, (ecu)->name, \
+ ((ecu)->sa >= 0) ? (ecu)->sa : J1939_NO_ADDR, ##__VA_ARGS__)
+#define ecu_alert(ecu, fmt, ...) \
+ pr_alert("j1939-%i,%016llx,%02x: " fmt, (ecu)->ifindex, (ecu)->name, \
+ ((ecu)->sa >= 0) ? (ecu)->sa : J1939_NO_ADDR, ##__VA_ARGS__)
+
+static inline
+struct j1939_segment *j1939_ecu_to_segment(struct j1939_ecu *ecu)
+{
+ return ecu->parent;
+}
+
+static struct {
+ struct list_head list;
+ spinlock_t lock;
+} segments;
+
+struct j1939_segment *j1939_segment_find(int ifindex)
+{
+ struct j1939_segment *jseg;
+
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ if (jseg->ifindex == ifindex) {
+ get_j1939_segment(jseg);
+ goto found;
+ }
+ }
+ jseg = NULL;
+found:
+ spin_unlock_bh(&segments.lock);
+ return jseg;
+}
+
+/*
+ * segment device interface
+ */
+static inline void jseg_release(struct j1939_segment *jseg)
+{
+ kfree(jseg);
+}
+static void cb_put_j1939_segment(struct kref *kref)
+{
+ jseg_release(container_of(kref, struct j1939_segment, kref));
+}
+void put_j1939_segment(struct j1939_segment *segment)
+{
+ kref_put(&segment->kref, cb_put_j1939_segment);
+}
+
+int j1939_segment_register(struct net_device *netdev)
+{
+ int ret;
+ struct j1939_segment *jseg;
+
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (jseg) {
+ put_j1939_segment(jseg);
+ ret = -EALREADY;
+ goto fail_exist;
+ }
+ jseg = kzalloc(sizeof(*jseg), GFP_KERNEL);
+ if (!jseg) {
+ ret = -ENOMEM;
+ goto fail_malloc;
+ }
+ rwlock_init(&jseg->lock);
+ INIT_LIST_HEAD(&jseg->ecus);
+ INIT_LIST_HEAD(&jseg->flist);
+ jseg->ifindex = netdev->ifindex;
+
+ kref_init(&jseg->kref);
+
+ spin_lock_bh(&segments.lock);
+ list_add_tail(&jseg->flist, &segments.list);
+ spin_unlock_bh(&segments.lock);
+
+ jseg_dbg(jseg, "register\n");
+ return 0;
+
+fail_malloc:
+fail_exist:
+ return ret;
+}
+
+void j1939_segment_unregister(struct j1939_segment *jseg)
+{
+ struct j1939_ecu *ecu;
+
+ if (!jseg)
+ return;
+
+ spin_lock_bh(&segments.lock);
+ list_del_init(&jseg->flist);
+ spin_unlock_bh(&segments.lock);
+
+ write_lock_bh(&jseg->lock);
+ while (!list_empty(&jseg->ecus)) {
+ ecu = list_first_entry(&jseg->ecus, struct j1939_ecu, list);
+ write_unlock_bh(&jseg->lock);
+ j1939_ecu_unregister(ecu);
+ write_lock_bh(&jseg->lock);
+ }
+ write_unlock_bh(&jseg->lock);
+ jseg_dbg(jseg, "unregister\n");
+ put_j1939_segment(jseg);
+}
+
+/*
+ * ECU device interface
+ */
+void j1939_ecu_free(struct j1939_ecu *ecu)
+{
+ hrtimer_try_to_cancel(&ecu->ac_timer);
+ kfree(ecu);
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_free);
+
+static void cb_put_j1939_ecu(struct kref *kref)
+{
+ j1939_ecu_free(container_of(kref, struct j1939_ecu, kref));
+}
+void put_j1939_ecu(struct j1939_ecu *ecu)
+{
+ kref_put(&ecu->kref, cb_put_j1939_ecu);
+}
+
+struct j1939_ecu *j1939_ecu_alloc(int ifindex, name_t name)
+{
+ struct j1939_ecu *ecu;
+
+ if (!name) {
+ pr_alert("%s(%i, 0), no name\n", __func__, ifindex);
+ return NULL;
+ }
+ ecu = kzalloc(sizeof(*ecu), GFP_KERNEL);
+ if (!ecu)
+ return NULL;
+ kref_init(&ecu->kref);
+ ecu->sa = J1939_IDLE_ADDR;
+ ecu->name = name;
+ ecu->ifindex = ifindex;
+
+ /* address claiming init */
+ hrtimer_init(&ecu->ac_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ecu->ac_timer.function = j1939_ac_timer_handler;
+ rwlock_init(&ecu->ac_lock);
+
+ INIT_LIST_HEAD(&ecu->list);
+ return ecu;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_alloc);
+
+int j1939_ecu_register(struct j1939_ecu *ecu)
+{
+ int ret;
+ struct j1939_segment *parent;
+ struct list_head *lst;
+ struct j1939_ecu *dut;
+
+ if (ecu->ifindex < 0) {
+ ecu_alert(ecu, "register with no ifindex\n");
+ return -EINVAL;
+ }
+ parent = j1939_segment_find(ecu->ifindex);
+ if (!parent) {
+ ecu_alert(ecu, "parent not found\n");
+ return -EINVAL;
+ }
+ ecu->parent = parent;
+
+ /* first add to internal list */
+ write_lock_bh(&parent->lock);
+ if (ecu->name) {
+ /* test for duplicate name */
+ list_for_each(lst, &ecu->list) {
+ dut = list_entry(lst, struct j1939_ecu, list);
+ if (dut->name == ecu->name) {
+ ecu_alert(ecu, "duplicate name\n");
+ ret = -EEXIST;
+ goto fail_duplicate;
+ }
+ }
+ }
+ list_add_tail(&ecu->list, &parent->ecus);
+ if (j1939_address_is_unicast(ecu->sa))
+ parent->ents[ecu->sa].ecu = ecu;
+ write_unlock_bh(&parent->lock);
+
+ put_j1939_segment(parent);
+ ecu_dbg(ecu, "register\n");
+ return 0;
+
+fail_duplicate:
+ write_unlock_bh(&parent->lock);
+
+ put_j1939_segment(parent);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_register);
+
+void j1939_ecu_unregister(struct j1939_ecu *ecu)
+{
+ struct j1939_segment *parent;
+
+ if (!ecu)
+ return;
+
+ ecu_dbg(ecu, "unregister\n");
+ parent = j1939_ecu_to_segment(ecu);
+ get_j1939_segment(parent);
+ j1939_ecu_set_address(ecu, -1);
+ write_lock_bh(&parent->lock);
+ list_del_init(&ecu->list);
+ write_unlock_bh(&parent->lock);
+ put_j1939_ecu(ecu);
+ put_j1939_segment(parent);
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_unregister);
+
+struct j1939_ecu *j1939_ecu_find_by_addr(int sa, int ifindex)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *parent;
+
+ if (!j1939_address_is_unicast(sa))
+ return NULL;
+ parent = j1939_segment_find(ifindex);
+ if (!parent)
+ return NULL;
+ read_lock_bh(&parent->lock);
+ ecu = parent->ents[sa].ecu;
+ if (ecu)
+ get_j1939_ecu(ecu);
+ read_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ return ecu;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_find_by_addr);
+
+struct j1939_ecu *j1939_ecu_find_segment_default_tx(int ifindex,
+ name_t *name, uint8_t *addr)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *parent;
+ struct addr_ent *paddr;
+ int j;
+
+ if (ifindex <= 0)
+ return ERR_PTR(-EINVAL);
+ parent = j1939_segment_find(ifindex);
+ if (!parent)
+ return ERR_PTR(-ENETUNREACH);
+ read_lock_bh(&parent->lock);
+ list_for_each_entry(ecu, &parent->ecus, list) {
+ if (ecu->flags & ECUFLAG_LOCAL) {
+ get_j1939_ecu(ecu);
+ if (name)
+ *name = ecu->name;
+ if (addr)
+ *addr = ecu->sa;
+ goto found;
+ }
+ }
+ ecu = NULL;
+ for (j = 0, paddr = parent->ents; j < J1939_IDLE_ADDR; ++j, ++paddr) {
+ if (paddr->ecu)
+ continue;
+ if (paddr->flags & ECUFLAG_LOCAL) {
+ if (name)
+ *name = 0;
+ if (addr)
+ *addr = j;
+ goto found;
+ }
+ }
+ ecu = ERR_PTR(-EHOSTDOWN);
+found:
+ read_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ return ecu;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_find_segment_default_tx);
+
+/* find ecu in segment (having segment's kref & readlock) */
+static struct j1939_ecu *_j1939_segment_find_name_masked(
+ name_t name, name_t mask, struct j1939_segment *parent)
+{
+ struct j1939_ecu *ecu;
+
+ if (!name)
+ return NULL;
+ if (!parent)
+ return NULL;
+ name &= mask;
+ list_for_each_entry(ecu, &parent->ecus, list) {
+ if ((ecu->name & mask) == name)
+ return get_j1939_ecu(ecu);
+ }
+ return NULL;
+}
+/* find ecu in segment (having segment's kref) */
+static struct j1939_ecu *j1939_segment_find_name_masked(
+ name_t name, name_t mask, struct j1939_segment *parent)
+{
+ struct j1939_ecu *ecu;
+
+ if (!name || !parent)
+ return NULL;
+ read_lock_bh(&parent->lock);
+ ecu = _j1939_segment_find_name_masked(name, mask, parent);
+ read_unlock_bh(&parent->lock);
+ return ecu;
+}
+struct j1939_ecu *j1939_ecu_find_name_masked(name_t name, name_t mask,
+ int ifindex)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *jseg;
+
+ if (!name)
+ return NULL;
+ if (ifindex) {
+ jseg = j1939_segment_find(ifindex);
+ if (!jseg)
+ return NULL;
+ ecu = j1939_segment_find_name_masked(name, mask, jseg);
+ put_j1939_segment(jseg);
+ return ecu;
+ }
+ /* iterate segments */
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ ecu = j1939_segment_find_name_masked(name, mask, jseg);
+ put_j1939_segment(jseg);
+ if (ecu)
+ goto found;
+ }
+ ecu = NULL;
+found:
+ spin_unlock_bh(&segments.lock);
+ return ecu;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_find_name_masked);
+
+static void _clear_ecu_flags(struct j1939_ecu *ecu, struct addr_ent *ent)
+{
+ int clearflags = ECUFLAG_LOCAL | ECUFLAG_REMOTE;
+
+ /*
+ * when the SA is a static one too,
+ * prevent it from getting cleared.
+ */
+ clearflags = ECUFLAG_REMOTE |
+ ((ent->flags & ECUFLAG_STATIC) ? 0 : ECUFLAG_LOCAL);
+ ent->flags &= ~(ecu->flags & clearflags);
+}
+
+int j1939_ecu_set_address(struct j1939_ecu *ecu, int sa)
+{
+ struct j1939_segment *parent = j1939_ecu_to_segment(ecu);
+ struct addr_ent *ent;
+
+ write_lock_bh(&parent->lock);
+ if (j1939_address_is_unicast(ecu->sa)) {
+ ent = &parent->ents[ecu->sa];
+ if (ent->ecu == ecu) {
+ ent->ecu = NULL;
+ _clear_ecu_flags(ecu, ent);
+ }
+ }
+ if (j1939_address_is_unicast(sa)) {
+ ent = &parent->ents[ecu->sa];
+ if (ent->ecu) {
+ ent->ecu->sa = J1939_IDLE_ADDR;
+ _clear_ecu_flags(ecu, ent);
+ }
+ ent->ecu = ecu;
+ ent->flags |= ecu->flags;
+ }
+ ecu->sa = sa;
+ write_unlock_bh(&parent->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_set_address);
+
+/* PROC */
+static int j1939_proc_addr(struct seq_file *sqf, void *v)
+{
+ struct j1939_segment *jseg;
+ struct net_device *netdev;
+ struct addr_ent *paddr;
+ int j;
+ ktime_t now;
+ struct timeval tv;
+
+ now = ktime_get();
+ seq_printf(sqf, "iface\tSA\tflags\trxtime\n");
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ if (!netdev) {
+ pr_alert("j1939 proc: ifindex %i not found\n",
+ jseg->ifindex);
+ put_j1939_segment(jseg);
+ continue;
+ }
+ read_lock_bh(&jseg->lock);
+ for (j = 0, paddr = jseg->ents; j < J1939_IDLE_ADDR;
+ ++j, ++paddr) {
+ tv = ktime_to_timeval(ktime_sub(now, paddr->rxtime));
+ if (!paddr->flags && !paddr->ecu)
+ continue;
+ seq_printf(sqf, "%s\t%02x\t%c%c%c%c\t-%lu.%06lu\n",
+ netdev->name, j,
+ (paddr->flags & ECUFLAG_STATIC) ? 'S' : '-',
+ (paddr->flags & ECUFLAG_LOCAL) ? 'L' : '-',
+ (paddr->flags & ECUFLAG_REMOTE) ? 'R' : '-',
+ paddr->ecu ? 'E' : '.',
+ tv.tv_sec, tv.tv_usec);
+ }
+ read_unlock_bh(&jseg->lock);
+ dev_put(netdev);
+ put_j1939_segment(jseg);
+ }
+ spin_unlock_bh(&segments.lock);
+ return 0;
+}
+
+static int j1939_proc_ecu(struct seq_file *sqf, void *v)
+{
+ struct j1939_segment *jseg;
+ struct j1939_ecu *ecu;
+ struct net_device *netdev;
+ ktime_t now;
+ struct timeval tv;
+ char sa[4];
+
+ now = ktime_get();
+ seq_printf(sqf, "iface\taddr\tname\tflags\trxtime\n");
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ if (!netdev) {
+ pr_alert("j1939 proc: ifindex %i not found\n",
+ jseg->ifindex);
+ put_j1939_segment(jseg);
+ continue;
+ }
+ read_lock_bh(&jseg->lock);
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ tv = ktime_to_timeval(ktime_sub(now, ecu->rxtime));
+ if (ecu->sa < 0)
+ strcpy(sa, "-");
+ else
+ snprintf(sa, sizeof(sa), "%02x", ecu->sa);
+ seq_printf(sqf, "%s\t%s\t%016llx\t%c\t-%lu.%06lu\n",
+ netdev->name, sa,
+ (unsigned long long)ecu->name,
+ (ecu->flags & ECUFLAG_LOCAL) ? 'L' : 'R',
+ tv.tv_sec, tv.tv_usec);
+ }
+ read_unlock_bh(&jseg->lock);
+ dev_put(netdev);
+ put_j1939_segment(jseg);
+ }
+ spin_unlock_bh(&segments.lock);
+ return 0;
+}
+
+/* exported init */
+int __init j1939bus_module_init(void)
+{
+ INIT_LIST_HEAD(&segments.list);
+ spin_lock_init(&segments.lock);
+ j1939_proc_add("addr", j1939_proc_addr, NULL);
+ j1939_proc_add("ecu", j1939_proc_ecu, NULL);
+ return 0;
+}
+
+void j1939bus_module_exit(void)
+{
+ struct j1939_segment *jseg;
+ struct net_device *netdev;
+
+ spin_lock_bh(&segments.lock);
+ while (!list_empty(&segments.list)) {
+ jseg = list_first_entry(&segments.list,
+ struct j1939_segment, flist);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ spin_unlock_bh(&segments.lock);
+ j1939_segment_detach(netdev);
+ dev_put(netdev);
+ spin_lock_bh(&segments.lock);
+ }
+ spin_unlock_bh(&segments.lock);
+
+ j1939_proc_remove("ecu");
+ j1939_proc_remove("addr");
+}
+
+
diff --git a/net/can/j1939/filter.c b/net/can/j1939/filter.c
new file mode 100644
index 0000000..47fc52c
--- /dev/null
+++ b/net/can/j1939/filter.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+
+#include "j1939-priv.h"
+
+static LIST_HEAD(filters);
+DEFINE_RWLOCK(j1939_receiver_rwlock); /* protects the filter list */
+
+struct filter {
+ struct list_head list;
+ void *vp;
+ void (*fn)(struct sk_buff *, void *);
+};
+
+int j1939_recv_distribute(struct sk_buff *skb)
+{
+ struct filter *filter;
+
+ read_lock(&j1939_receiver_rwlock);
+ list_for_each_entry(filter, &filters, list)
+ filter->fn(skb, filter->vp);
+ read_unlock(&j1939_receiver_rwlock);
+
+ return 0;
+}
+
+int j1939_recv_add(void *vp, void (*fn)(struct sk_buff *, void *))
+{
+ struct filter *f;
+
+ f = kzalloc(sizeof(*f), GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+
+ f->vp = vp;
+ f->fn = fn;
+
+ j1939_recv_suspend();
+ list_add(&f->list, &filters);
+ j1939_recv_resume();
+ return 0;
+}
+
+int j1939_recv_remove(void *vp, void (*fn)(struct sk_buff *, void *))
+{
+ struct filter *filter;
+ int found = 0;
+
+ j1939_recv_suspend();
+ list_for_each_entry(filter, &filters, list) {
+ if ((filter->vp == vp) && (filter->fn == fn)) {
+ list_del_init(&filter->list);
+ kfree(filter);
+ found = 1;
+ break;
+ }
+ }
+ j1939_recv_resume();
+ return found ? 0 : -ENOENT;
+}
+
diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h
new file mode 100644
index 0000000..64008e9
--- /dev/null
+++ b/net/can/j1939/j1939-priv.h
@@ -0,0 +1,287 @@
+/*
+ * j1939-priv.h
+ *
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _J1939_PRIV_H_
+#define _J1939_PRIV_H_
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/can/j1939.h>
+#include <linux/atomic.h>
+
+/* TODO: return ENETRESET on busoff. */
+
+#define ECUFLAG_LOCAL 0x01
+#define ECUFLAG_REMOTE 0x02
+#define ECUFLAG_STATIC 0x04
+/*
+ * j1939 devices
+ */
+struct j1939_ecu {
+ struct list_head list;
+ name_t name;
+ int flags;
+ ktime_t rxtime;
+ int ifindex;
+ int sa;
+ int ac_state;
+ struct hrtimer ac_timer;
+ uint8_t block_tx; /*
+ * Outgoing traffic can be blocked by an outgoing Address Claim msg,
+ * The Address Claim msg itself is processed
+ * when it's received again via CAN intf echo.
+ * State changes (ac_state) are only done at rx path.
+ */
+ rwlock_t ac_lock; /*
+ * protects members which are updated by address claiming logic:
+ * sa, ac_state, ac_timer, block_tx
+ */
+ struct kref kref;
+ struct j1939_segment *parent;
+};
+#define to_j1939_ecu(x) container_of((x), struct j1939_ecu, dev)
+
+struct j1939_segment {
+ struct list_head ecus; /*
+ * local list entry in parent
+ * These allow irq (& softirq) context lookups on j1939 devices
+ * This approach (seperate lists) is done as the other 2 alternatives
+ * are not easier or even wrong
+ * 1) using the pure kobject methods involves mutexes, which are not
+ * allowed in irq context.
+ * 2) duplicating data structures would require a lot of synchronization
+ * code
+ * usage:
+ */
+ rwlock_t lock; /*
+ * segments need a lock to protect the above list
+ */
+ struct list_head flist; /*
+ * list entry for use by interrupt lookup routines
+ */
+ int ifindex;
+ struct addr_ent {
+ struct j1939_ecu *ecu;
+ int flags;
+ ktime_t rxtime;
+ } ents[256];
+ /*
+ * list of 256 ecu ptrs, that cache the claimed addresses.
+ * also protected by the above lock
+ * don't use directly, use j1939_ecu_set_address() instead
+ */
+ struct kref kref;
+};
+#define to_j1939_segment(x) container_of((x), struct j1939_segment, dev)
+
+extern void put_j1939_ecu(struct j1939_ecu *ecu);
+extern void put_j1939_segment(struct j1939_segment *segment);
+static inline struct j1939_ecu *get_j1939_ecu(struct j1939_ecu *dut)
+{
+ kref_get(&dut->kref);
+ return dut;
+}
+static inline struct j1939_segment *get_j1939_segment(struct j1939_segment *dut)
+{
+ kref_get(&dut->kref);
+ return dut;
+}
+
+/*
+ * conversion function between (struct sock | struct sk_buff)->sk_priority
+ * from linux and j1939 priority field
+ */
+static inline int j1939_prio(int sk_priority)
+{
+ if (sk_priority < 0)
+ return 6; /* default */
+ else if (sk_priority > 7)
+ return 0;
+ else
+ return 7 - sk_priority;
+}
+static inline int j1939_to_sk_priority(int j1939_prio)
+{
+ return 7 - j1939_prio;
+}
+
+static inline int j1939_address_is_valid(int sa)
+{
+ return (sa >= 0) && (sa < 0xff);
+}
+
+static inline int j1939_address_is_unicast(int sa)
+{
+ return (sa >= 0) && (sa < 0xfe);
+}
+
+static inline int pgn_is_pdu1(pgn_t pgn)
+{
+ return (pgn & 0xff00) < 0xf000;
+}
+
+#define NO_PGN 0x40000
+static inline int pgn_is_valid(pgn_t pgn)
+{
+ return (pgn < NO_PGN);
+}
+
+extern struct j1939_ecu *j1939_ecu_find_by_addr(int sa, int ifindex);
+extern struct j1939_ecu *j1939_ecu_find_name_masked(
+ name_t name, name_t mask, int ifindex);
+static inline struct j1939_ecu *j1939_ecu_find_by_name(name_t name, int ifindex)
+{
+ return j1939_ecu_find_name_masked(name, 0xffffffffffffffffULL, ifindex);
+}
+/* find_by_name, with kref & read_lock taken */
+extern int j1939_ecu_set_address(struct j1939_ecu *, int sa);
+extern struct j1939_ecu *j1939_ecu_find_segment_default_tx(
+ int ifindex, name_t *pname, uint8_t *paddr);
+
+extern void j1939_put_promisc_receiver(int ifindex);
+extern void j1939_get_promisc_receiver(int ifindex);
+
+extern int j1939_proc_add(const char *file,
+ int (*seq_show)(struct seq_file *sqf, void *v),
+ write_proc_t write);
+extern void j1939_proc_remove(const char *file);
+
+extern const char j1939_procname[];
+/* j1939 printk */
+#define j1939_printk(level, ...) printk(level "J1939 " __VA_ARGS__)
+
+#define j1939_err(...) j1939_printk(KERN_ERR , __VA_ARGS__)
+#define j1939_warning(...) j1939_printk(KERN_WARNING , __VA_ARGS__)
+#define j1939_notice(...) j1939_printk(KERN_NOTICE , __VA_ARGS__)
+#define j1939_info(...) j1939_printk(KERN_INFO , __VA_ARGS__)
+#ifdef DEBUG
+#define j1939_debug(...) j1939_printk(KERN_DEBUG , __VA_ARGS__)
+#else
+#define j1939_debug(...)
+#endif
+
+struct sk_buff;
+
+/* control buffer of the sk_buff */
+struct j1939_sk_buff_cb {
+ int ifindex;
+ priority_t priority;
+ struct {
+ name_t name;
+ uint8_t addr;
+ int flags;
+ } src, dst;
+ pgn_t pgn;
+ int msg_flags;
+ /* for tx, MSG_SYN will be used to sync on sockets */
+};
+#define J1939_MSG_RESERVED MSG_SYN
+#define J1939_MSG_SYNC MSG_SYN
+
+static inline int j1939cb_is_broadcast(const struct j1939_sk_buff_cb *cb)
+{
+ return (!cb->dst.name && (cb->dst.addr >= 0xff));
+}
+
+/* J1939 stack */
+enum {
+ j1939_level_can,
+ j1939_level_transport,
+ j1939_level_sky,
+};
+
+#define RESULT_STOP 1
+/*
+ * return RESULT_STOP when stack processing may stop.
+ * it is up to the stack entry itself to kfree_skb() the sk_buff
+ */
+
+extern int j1939_send(struct sk_buff *, int level);
+extern int j1939_recv(struct sk_buff *, int level);
+
+/* stack entries */
+extern int j1939_recv_promisc(struct sk_buff *);
+extern int j1939_send_transport(struct sk_buff *);
+extern int j1939_recv_transport(struct sk_buff *);
+extern int j1939_send_address_claim(struct sk_buff *);
+extern int j1939_recv_address_claim(struct sk_buff *);
+
+extern int j1939_recv_distribute(struct sk_buff *);
+
+/* network management */
+extern struct j1939_ecu *j1939_ecu_alloc(int ifindex, name_t name);
+extern void j1939_ecu_free(struct j1939_ecu *); /*
+ * should no be used for registered ecu's
+ */
+extern int j1939_ecu_register(struct j1939_ecu *);
+extern void j1939_ecu_unregister(struct j1939_ecu *);
+
+extern int j1939_segment_attach(struct net_device *);
+extern int j1939_segment_detach(struct net_device *);
+
+extern int j1939_segment_register(struct net_device *);
+extern void j1939_segment_unregister(struct j1939_segment *);
+extern struct j1939_segment *j1939_segment_find(int ifindex);
+
+extern void j1939sk_netdev_event(int ifindex, int error_code);
+
+/* add/remove receiver */
+extern int j1939_recv_add(void *vp, void (*fn)(struct sk_buff *, void *));
+extern int j1939_recv_remove(void *vp, void (*fn)(struct sk_buff *, void *));
+
+/*
+ * provide public access to this lock
+ * so sparse can verify the context balance
+ */
+extern rwlock_t j1939_receiver_rwlock;
+static inline void j1939_recv_suspend(void)
+{
+ write_lock_bh(&j1939_receiver_rwlock);
+}
+
+static inline void j1939_recv_resume(void)
+{
+ write_unlock_bh(&j1939_receiver_rwlock);
+}
+
+/* locks the recv module */
+extern void j1939_recv_suspend(void);
+extern void j1939_recv_resume(void);
+
+/* address claiming timer callback */
+enum hrtimer_restart j1939_ac_timer_handler(struct hrtimer *hrtimer);
+/*
+ * decrement pending skb for a j1939 socket
+ */
+extern void j1939_sock_pending_del(struct sock *sk);
+
+/* seperate module-init/modules-exit's */
+extern __init int j1939_proc_module_init(void);
+extern __init int j1939bus_module_init(void);
+extern __init int j1939sk_module_init(void);
+extern __init int j1939tp_module_init(void);
+
+extern void j1939_proc_module_exit(void);
+extern void j1939bus_module_exit(void);
+extern void j1939sk_module_exit(void);
+extern void j1939tp_module_exit(void);
+
+/* rtnetlink */
+extern const struct rtnl_af_ops j1939_rtnl_af_ops;
+extern int j1939rtnl_new_addr(struct sk_buff *, struct nlmsghdr *, void *arg);
+extern int j1939rtnl_del_addr(struct sk_buff *, struct nlmsghdr *, void *arg);
+extern int j1939rtnl_dump_addr(struct sk_buff *, struct netlink_callback *);
+
+#endif /* _J1939_PRIV_H_ */
diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c
new file mode 100644
index 0000000..71963d5
--- /dev/null
+++ b/net/can/j1939/main.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * Core of can-j1939 that links j1939 to CAN.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/if_arp.h>
+#include <net/tcp_states.h>
+
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include "j1939-priv.h"
+
+MODULE_DESCRIPTION("PF_CAN SAE J1939");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("EIA Electronics (Kurt Van Dijck & Pieter Beyens)");
+
+static struct {
+ struct notifier_block notifier;
+} s;
+
+/* LOWLEVEL CAN interface */
+
+/* CAN_HDR: #bytes before can_frame data part */
+#define CAN_HDR (offsetof(struct can_frame, data))
+/* CAN_FTR: #bytes beyond data part */
+#define CAN_FTR (sizeof(struct can_frame)-CAN_HDR-\
+ sizeof(((struct can_frame *)0)->data))
+
+static void j1939_recv_ecu_flags(struct sk_buff *skb, void *data)
+{
+ struct j1939_segment *jseg = data;
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct addr_ent *paddr;
+
+ if (!jseg)
+ return;
+ write_lock_bh(&jseg->lock);
+ paddr = &jseg->ents[cb->src.addr];
+ paddr->rxtime = ktime_get();
+ if (paddr->ecu) {
+ cb->src.flags = paddr->ecu->flags;
+ paddr->ecu->rxtime = paddr->rxtime;
+ } else {
+ if (!paddr->flags)
+ paddr->flags |= ECUFLAG_REMOTE;
+ cb->src.flags = paddr->flags;
+ }
+
+ if (j1939_address_is_unicast(cb->dst.addr)) {
+ paddr = &jseg->ents[cb->dst.addr];
+ if (paddr->ecu) {
+ cb->dst.flags = paddr->ecu->flags;
+ } else {
+ if (!paddr->flags)
+ paddr->flags |= ECUFLAG_REMOTE;
+ cb->dst.flags = paddr->flags;
+ }
+ }
+ write_unlock_bh(&jseg->lock);
+}
+
+/* lowest layer */
+static void j1939_can_recv(struct sk_buff *skb, void *data)
+{
+ int orig_len;
+ struct j1939_sk_buff_cb *sk_addr;
+ struct can_frame *msg;
+ uint8_t saved_cb[sizeof(skb->cb)];
+
+ BUILD_BUG_ON(sizeof(*sk_addr) > sizeof(skb->cb));
+ /*
+ * get a pointer to the header of the skb
+ * the skb payload (pointer) is moved, so that the next skb_data
+ * returns the actual payload
+ */
+ msg = (void *)skb->data;
+ orig_len = skb->len;
+ skb_pull(skb, CAN_HDR);
+ /* fix length, set to dlc, with 8 maximum */
+ skb_trim(skb, min_t(uint8_t, msg->can_dlc, 8));
+
+ /* set addr */
+ sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+ memcpy(saved_cb, sk_addr, sizeof(saved_cb));
+ memset(sk_addr, 0, sizeof(*sk_addr));
+ if (skb->dev)
+ sk_addr->ifindex = skb->dev->ifindex;
+ sk_addr->priority = (msg->can_id & 0x1c000000) >> 26;
+ sk_addr->src.addr = msg->can_id & 0xff;
+ sk_addr->pgn = (msg->can_id & 0x3ffff00) >> 8;
+ if (pgn_is_pdu1(sk_addr->pgn)) {
+ /* Type 1: with destination address */
+ sk_addr->dst.addr = sk_addr->pgn & 0xff;
+ /* normalize pgn: strip dst address */
+ sk_addr->pgn &= 0x3ff00;
+ } else {
+ /* set broadcast address */
+ sk_addr->dst.addr = J1939_NO_ADDR;
+ }
+ j1939_recv_ecu_flags(skb, data);
+ j1939_recv(skb, j1939_level_can);
+
+ /* restore the original skb, should always work */
+ skb_push(skb, CAN_HDR);
+ /* no safety check, it just restores the skbuf's contents */
+ __skb_trim(skb, orig_len);
+ memcpy(sk_addr, saved_cb, sizeof(saved_cb));
+}
+
+static int j1939_send_can(struct sk_buff *skb)
+{
+ int ret, dlc;
+ canid_t canid;
+ struct j1939_sk_buff_cb *sk_addr;
+ struct net_device *netdev = NULL;
+ struct can_frame *msg;
+
+ dlc = skb->len;
+ if (dlc > 8)
+ return -EMSGSIZE;
+ ret = pskb_expand_head(skb, SKB_DATA_ALIGN(CAN_HDR),
+ CAN_FTR + (8-dlc), GFP_ATOMIC);
+ if (ret < 0)
+ return ret;
+
+ msg = (void *)skb_push(skb, CAN_HDR);
+ BUG_ON(!msg);
+ /* make it a full can frame */
+ skb_put(skb, CAN_FTR + (8 - dlc));
+
+ sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+ canid = CAN_EFF_FLAG |
+ (sk_addr->src.addr & 0xff) |
+ ((sk_addr->priority & 0x7) << 26);
+ if (pgn_is_pdu1(sk_addr->pgn))
+ canid |= ((sk_addr->pgn & 0x3ff00) << 8) |
+ ((sk_addr->dst.addr & 0xff) << 8);
+ else
+ canid |= ((sk_addr->pgn & 0x3ffff) << 8);
+
+ msg->can_id = canid;
+ msg->can_dlc = dlc;
+
+ /* set net_device */
+ ret = -ENODEV;
+ if (!skb->dev) {
+ if (!sk_addr->ifindex)
+ goto failed;
+ netdev = dev_get_by_index(&init_net, sk_addr->ifindex);
+ if (!netdev)
+ goto failed;
+ skb->dev = netdev;
+ }
+
+ /* fix the 'always free' policy of can_send */
+ skb = skb_get(skb);
+ ret = can_send(skb, 1);
+ if (!ret) {
+ /* free when can_send succeeded */
+ kfree_skb(skb);
+ /* is this necessary ? */
+ ret = RESULT_STOP;
+ }
+failed:
+ if (netdev)
+ dev_put(netdev);
+ return ret;
+}
+
+static int j1939_send_normalize(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct j1939_segment *jseg;
+ struct addr_ent *paddr;
+ struct j1939_ecu *ecu;
+ int ret = 0;
+
+ /* apply sanity checks */
+ cb->pgn &= (pgn_is_pdu1(cb->pgn)) ? 0x3ff00 : 0x3ffff;
+ if (cb->priority > 7)
+ cb->priority = 6;
+
+ /* verify source */
+ if (!cb->ifindex)
+ return -ENETUNREACH;
+ jseg = j1939_segment_find(cb->ifindex);
+ if (!jseg)
+ return -ENETUNREACH;
+ read_lock_bh(&jseg->lock);
+ /* verify source */
+ if (cb->src.name) {
+ ecu = j1939_ecu_find_by_name(cb->src.name, cb->ifindex);
+ cb->src.flags = ecu ? ecu->flags : 0;
+ if (ecu)
+ put_j1939_ecu(ecu);
+ } else if (j1939_address_is_unicast(cb->src.addr)) {
+ paddr = &jseg->ents[cb->src.addr];
+ cb->src.flags = paddr->flags;
+ } else if (cb->src.addr == J1939_IDLE_ADDR) {
+ /* allow always */
+ cb->src.flags = ECUFLAG_LOCAL;
+ } else {
+ /* J1939_NO_ADDR */
+ cb->src.flags = 0;
+ }
+ if (cb->src.flags & ECUFLAG_REMOTE) {
+ ret = -EREMOTE;
+ goto failed;
+ } else if (!(cb->src.flags & ECUFLAG_LOCAL)) {
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ }
+
+ /* verify destination */
+ if (cb->dst.name) {
+ ecu = j1939_ecu_find_by_name(cb->dst.name, cb->ifindex);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ }
+ cb->dst.flags = ecu->flags;
+ put_j1939_ecu(ecu);
+ } else if (cb->dst.addr == J1939_IDLE_ADDR) {
+ /* not a valid destination */
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ } else if (j1939_address_is_unicast(cb->dst.addr)) {
+ paddr = &jseg->ents[cb->dst.addr];
+ cb->dst.flags = paddr->flags;
+ } else {
+ cb->dst.flags = 0;
+ }
+
+ ret = 0;
+failed:
+ read_unlock_bh(&jseg->lock);
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+/* TOPLEVEL interface */
+int j1939_recv(struct sk_buff *skb, int level)
+{
+ int ret;
+
+ /* this stack operates with fallthrough switch statement */
+ switch (level) {
+ default:
+ WARN_ONCE(1, "%s: unsupported level %i\n", __func__, level);
+ return 0;
+ case j1939_level_can:
+ ret = j1939_recv_address_claim(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_recv_promisc(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_recv_transport(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_transport:
+ case j1939_level_sky:
+ ret = j1939_recv_distribute(skb);
+ break;
+ }
+ if (ret == RESULT_STOP)
+ return 0;
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(j1939_recv);
+
+int j1939_send(struct sk_buff *skb, int level)
+{
+ int ret;
+ struct sock *sk = NULL;
+
+ /* this stack operates with fallthrough switch statement */
+ switch (level) {
+ default:
+ WARN_ONCE(1, "%s: unsupported level %i\n", __func__, level);
+ case j1939_level_sky:
+ sk = skb->sk;
+ if (sk)
+ sock_hold(sk);
+ ret = j1939_send_normalize(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_send_transport(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_transport:
+ ret = j1939_send_address_claim(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_can:
+ ret = j1939_send_can(skb);
+ break;
+ }
+ if (ret == RESULT_STOP)
+ ret = 0;
+ else if (!ret && sk)
+ j1939_sock_pending_del(sk);
+ if (sk)
+ sock_put(sk);
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(j1939_send);
+
+/* NETDEV MANAGEMENT */
+
+#define J1939_CAN_ID CAN_EFF_FLAG
+#define J1939_CAN_MASK (CAN_EFF_FLAG | CAN_RTR_FLAG)
+int j1939_segment_attach(struct net_device *netdev)
+{
+ int ret;
+ struct j1939_segment *jseg;
+
+ if (!netdev)
+ return -ENODEV;
+ if (netdev->type != ARPHRD_CAN)
+ return -EAFNOSUPPORT;
+
+ ret = j1939_segment_register(netdev);
+ if (ret < 0)
+ goto fail_register;
+ jseg = j1939_segment_find(netdev->ifindex);
+ ret = can_rx_register(netdev, J1939_CAN_ID, J1939_CAN_MASK,
+ j1939_can_recv, jseg, "j1939");
+ if (ret < 0)
+ goto fail_can_rx;
+ return 0;
+
+fail_can_rx:
+ j1939_segment_unregister(jseg);
+ put_j1939_segment(jseg);
+fail_register:
+ return ret;
+}
+
+int j1939_segment_detach(struct net_device *netdev)
+{
+ struct j1939_segment *jseg;
+
+ BUG_ON(!netdev);
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ return -EHOSTDOWN;
+ can_rx_unregister(netdev, J1939_CAN_ID, J1939_CAN_MASK,
+ j1939_can_recv, jseg);
+ j1939_segment_unregister(jseg);
+ put_j1939_segment(jseg);
+ j1939sk_netdev_event(netdev->ifindex, EHOSTDOWN);
+ return 0;
+}
+
+static int j1939_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *netdev = (struct net_device *)data;
+ struct j1939_segment *jseg;
+
+ if (!net_eq(dev_net(netdev), &init_net))
+ return NOTIFY_DONE;
+
+ if (netdev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+ case NETDEV_UNREGISTER:
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ break;
+ j1939_segment_unregister(jseg);
+ j1939sk_netdev_event(netdev->ifindex, ENODEV);
+ break;
+
+ case NETDEV_DOWN:
+ j1939sk_netdev_event(netdev->ifindex, ENETDOWN);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/* MODULE interface */
+
+static __init int j1939_module_init(void)
+{
+ int ret;
+
+ pr_info("can: SAE J1939\n");
+
+ ret = j1939_proc_module_init();
+ if (ret < 0)
+ goto fail_proc;
+
+ s.notifier.notifier_call = j1939_notifier;
+ register_netdevice_notifier(&s.notifier);
+
+ ret = j1939bus_module_init();
+ if (ret < 0)
+ goto fail_bus;
+ ret = j1939sk_module_init();
+ if (ret < 0)
+ goto fail_sk;
+ ret = j1939tp_module_init();
+ if (ret < 0)
+ goto fail_tp;
+ return 0;
+
+ j1939tp_module_exit();
+fail_tp:
+ j1939sk_module_exit();
+fail_sk:
+ j1939bus_module_exit();
+fail_bus:
+ unregister_netdevice_notifier(&s.notifier);
+
+ j1939_proc_module_exit();
+fail_proc:
+ return ret;
+}
+
+static __exit void j1939_module_exit(void)
+{
+ j1939tp_module_exit();
+ j1939sk_module_exit();
+ j1939bus_module_exit();
+
+ unregister_netdevice_notifier(&s.notifier);
+
+ j1939_proc_module_exit();
+}
+
+module_init(j1939_module_init);
+module_exit(j1939_module_exit);
diff --git a/net/can/j1939/proc.c b/net/can/j1939/proc.c
new file mode 100644
index 0000000..76acfa0
--- /dev/null
+++ b/net/can/j1939/proc.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+
+#include "j1939-priv.h"
+
+const char j1939_procname[] = "can-j1939";
+
+static struct proc_dir_entry *rootdir;
+
+static int j1939_proc_open(struct inode *inode, struct file *file)
+{
+ struct proc_dir_entry *pde = PDE(inode);
+ int (*fn)(struct seq_file *sqf, void *v) = pde->data;
+
+ return single_open(file, fn, pde);
+}
+
+/* copied from fs/proc/generic.c */
+static ssize_t
+proc_file_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct proc_dir_entry *dp;
+
+ dp = PDE(inode);
+
+ if (!dp->write_proc)
+ return -EIO;
+
+ /* FIXME: does this routine need ppos? probably... */
+ return dp->write_proc(file, buffer, count, dp->data);
+}
+
+static const struct file_operations j1939_proc_ops = {
+ .owner = THIS_MODULE,
+ .open = j1939_proc_open,
+ .read = seq_read,
+ .write = proc_file_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int j1939_proc_add(const char *file,
+ int (*seq_show)(struct seq_file *sqf, void *v),
+ write_proc_t write)
+{
+ struct proc_dir_entry *pde;
+ int mode = 0;
+
+ if (seq_show)
+ mode |= 0444;
+ if (write)
+ mode |= 0200;
+
+ if (!rootdir)
+ return -ENODEV;
+ pde = proc_create(file, mode, rootdir, &j1939_proc_ops);
+ if (!pde)
+ goto fail_create;
+ pde->data = seq_show;
+ pde->write_proc = write;
+ return 0;
+
+fail_create:
+ return -ENOENT;
+}
+EXPORT_SYMBOL(j1939_proc_add);
+
+void j1939_proc_remove(const char *file)
+{
+ remove_proc_entry(file, rootdir);
+}
+EXPORT_SYMBOL(j1939_proc_remove);
+
+__init int j1939_proc_module_init(void)
+{
+ /* create /proc/net/can directory */
+ rootdir = proc_mkdir(j1939_procname, init_net.proc_net);
+ if (!rootdir)
+ return -EINVAL;
+ return 0;
+}
+
+void j1939_proc_module_exit(void)
+{
+ if (rootdir)
+ proc_net_remove(&init_net, j1939_procname);
+}
+
diff --git a/net/can/j1939/promisc.c b/net/can/j1939/promisc.c
new file mode 100644
index 0000000..14be755
--- /dev/null
+++ b/net/can/j1939/promisc.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/sysctl.h>
+#include "j1939-priv.h"
+
+static atomic_t n_promisc = ATOMIC_INIT(0);
+
+void j1939_get_promisc_receiver(int ifindex)
+{
+ atomic_inc(&n_promisc);
+}
+EXPORT_SYMBOL_GPL(j1939_get_promisc_receiver);
+
+void j1939_put_promisc_receiver(int ifindex)
+{
+ atomic_dec(&n_promisc);
+}
+EXPORT_SYMBOL_GPL(j1939_put_promisc_receiver);
+
+int j1939_recv_promisc(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ if ((cb->src.flags & ECUFLAG_REMOTE) &&
+ (cb->dst.flags & ECUFLAG_REMOTE)) {
+ if (!atomic_read(&n_promisc))
+ /* stop receive path */
+ return RESULT_STOP;
+ }
+ return 0;
+}
+
diff --git a/net/can/j1939/rtnl.c b/net/can/j1939/rtnl.c
new file mode 100644
index 0000000..3345b51
--- /dev/null
+++ b/net/can/j1939/rtnl.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * j1939-rtnl.c - netlink addressing interface
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/if_arp.h>
+
+#include "j1939-priv.h"
+
+static const struct nla_policy j1939_ifa_policy[IFA_J1939_MAX] = {
+ [IFA_J1939_ADDR] = { .type = NLA_U8, },
+ [IFA_J1939_NAME] = { .type = NLA_U64, },
+};
+
+int j1939rtnl_del_addr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ int ret;
+ struct ifaddrmsg *ifm;
+ struct j1939_segment *jseg;
+ uint8_t jaddr = J1939_NO_ADDR;
+ uint64_t jname = J1939_NO_NAME;
+
+ struct nlattr *nla, *tb[IFA_J1939_MAX];
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return -EINVAL;
+
+ nla = nlmsg_find_attr(nlh, sizeof(*ifm), IFA_LOCAL);
+ if (!nla)
+ return -EINVAL;
+
+ nla_parse_nested(tb, IFA_J1939_MAX-1, nla, j1939_ifa_policy);
+ if (tb[IFA_J1939_ADDR])
+ jaddr = nla_get_u8(tb[IFA_J1939_ADDR]);
+ if (tb[IFA_J1939_NAME])
+ jname = be64_to_cpu(nla_get_u64(tb[IFA_J1939_NAME]));
+
+ ifm = nlmsg_data(nlh);
+ jseg = j1939_segment_find(ifm->ifa_index);
+ if (!jseg)
+ return -EHOSTDOWN;
+
+ ret = 0;
+ if (j1939_address_is_unicast(jaddr)) {
+ struct addr_ent *ent;
+
+ ent = &jseg->ents[jaddr];
+ write_lock_bh(&jseg->lock);
+ if (ent->flags & ECUFLAG_STATIC) {
+ ent->flags &= ~ECUFLAG_STATIC;
+ if (!ent->ecu || !(ent->ecu->flags & ECUFLAG_LOCAL))
+ ent->flags &= ~ECUFLAG_LOCAL;
+ ret = 0;
+ } else {
+ ret = -EADDRNOTAVAIL;
+ }
+ write_unlock_bh(&jseg->lock);
+ } else if (jname) {
+ struct j1939_ecu *ecu;
+
+ ecu = j1939_ecu_find_by_name(jname, ifm->ifa_index);
+ if (ecu) {
+ if (ecu->flags & ECUFLAG_LOCAL) {
+ j1939_ecu_unregister(ecu);
+ j1939_ecu_free(ecu);
+ } else {
+ ret = -EREMOTE;
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ }
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+int j1939rtnl_new_addr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct ifaddrmsg *ifm;
+ struct j1939_segment *jseg;
+ uint8_t jaddr = J1939_NO_ADDR;
+ uint64_t jname = J1939_NO_NAME;
+ struct addr_ent *ent;
+ int ret;
+ struct nlattr *nla, *tb[IFA_J1939_MAX];
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return -EINVAL;
+
+ nla = nlmsg_find_attr(nlh, sizeof(*ifm), IFA_LOCAL);
+ if (!nla)
+ return -EINVAL;
+
+ ifm = nlmsg_data(nlh);
+ jseg = j1939_segment_find(ifm->ifa_index);
+ if (!jseg)
+ return -EHOSTDOWN;
+
+ nla_parse_nested(tb, IFA_J1939_MAX-1, nla, j1939_ifa_policy);
+ if (tb[IFA_J1939_ADDR])
+ jaddr = nla_get_u8(tb[IFA_J1939_ADDR]);
+ if (tb[IFA_J1939_NAME])
+ jname = be64_to_cpu(nla_get_u64(tb[IFA_J1939_NAME]));
+
+
+ ret = 0;
+ if (j1939_address_is_unicast(jaddr)) {
+ ent = &jseg->ents[jaddr];
+ write_lock_bh(&jseg->lock);
+ if (ent->flags & ECUFLAG_REMOTE)
+ ret = -EREMOTE;
+ else
+ ent->flags |= ECUFLAG_LOCAL | ECUFLAG_STATIC;
+ write_unlock_bh(&jseg->lock);
+ } else if (jname) {
+ struct j1939_ecu *ecu;
+
+ ecu = j1939_ecu_find_by_name(jname, ifm->ifa_index);
+ if (ecu) {
+ put_j1939_ecu(ecu);
+ ret = -EBUSY;
+ } else {
+ ecu = j1939_ecu_alloc(ifm->ifa_index, jname);
+ if (ecu) {
+ ecu->flags |= ECUFLAG_LOCAL;
+ ret = j1939_ecu_register(ecu);
+ } else
+ ret = -ENOMEM;
+ }
+ }
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+static int j1939rtnl_fill_ifaddr(struct sk_buff *skb, int ifindex,
+ uint8_t addr, uint64_t name, int j1939_flags,
+ u32 pid, u32 seq, int event, unsigned int flags)
+{
+ struct ifaddrmsg *ifm;
+ struct nlmsghdr *nlh;
+ struct nlattr *nla;
+
+ nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ ifm = nlmsg_data(nlh);
+ ifm->ifa_family = AF_CAN;
+ ifm->ifa_prefixlen = CAN_J1939;
+ ifm->ifa_flags = name ? 0 : IFA_F_PERMANENT;
+ ifm->ifa_scope = RT_SCOPE_LINK;
+ ifm->ifa_index = ifindex;
+
+ nla = nla_nest_start(skb, IFA_LOCAL);
+ if (j1939_address_is_unicast(addr))
+ NLA_PUT_U8(skb, IFA_J1939_ADDR, addr);
+ if (name)
+ NLA_PUT_U64(skb, IFA_J1939_NAME, cpu_to_be64(name));
+ nla_nest_end(skb, nla);
+
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+int j1939rtnl_dump_addr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int ndev, addr, ret;
+ struct net_device *netdev;
+ struct j1939_segment *jseg;
+ struct j1939_ecu *ecu;
+ struct addr_ent *ent;
+ struct list_head *lst;
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return 0;
+
+ ndev = 0;
+ for_each_netdev(&init_net, netdev) {
+ ++ndev;
+ if (ndev < cb->args[1])
+ continue;
+ if (netdev->type != ARPHRD_CAN)
+ continue;
+
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ continue;
+
+ read_lock_bh(&jseg->lock);
+ for (addr = cb->args[2]; addr < J1939_IDLE_ADDR; ++addr) {
+ ent = &jseg->ents[addr];
+ if (!(ent->flags & ECUFLAG_STATIC))
+ continue;
+ ret = j1939rtnl_fill_ifaddr(skb, netdev->ifindex, addr,
+ 0, ent->flags, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR,
+ NLM_F_MULTI);
+ if (ret < 0) {
+ read_unlock_bh(&jseg->lock);
+ goto done;
+ }
+ cb->args[2] = addr + 1;
+ }
+
+ if (addr > J1939_IDLE_ADDR)
+ addr = J1939_IDLE_ADDR;
+ list_for_each(lst, &jseg->ecus) {
+ ecu = list_entry(lst, struct j1939_ecu, list);
+ if (addr++ < cb->args[2])
+ continue;
+ if (!(ecu->flags & ECUFLAG_LOCAL))
+ continue;
+ ret = j1939rtnl_fill_ifaddr(skb, netdev->ifindex,
+ ecu->sa, ecu->name, ecu->flags,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR,
+ NLM_F_MULTI);
+ if (ret < 0) {
+ read_unlock_bh(&jseg->lock);
+ goto done;
+ }
+ cb->args[2] = addr;
+ }
+ read_unlock_bh(&jseg->lock);
+ /* reset first address for device */
+ cb->args[2] = 0;
+ }
+ ++ndev;
+done:
+ cb->args[1] = ndev;
+
+ return skb->len;
+}
+
+/*
+ * rtnl_link_ops
+ */
+
+static const struct nla_policy j1939_ifla_policy[IFLA_J1939_MAX] = {
+ [IFLA_J1939_ENABLE] = { .type = NLA_U8, },
+};
+
+static size_t j1939_get_link_af_size(const struct net_device *dev)
+{
+ return nla_policy_len(j1939_ifla_policy, IFLA_J1939_MAX-1);
+}
+
+static int j1939_validate_link_af(const struct net_device *dev,
+ const struct nlattr *nla)
+{
+ return nla_validate_nested(nla, IFLA_J1939_MAX-1, j1939_ifla_policy);
+}
+
+static int j1939_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct j1939_segment *jseg;
+
+ if (!dev)
+ return -ENODEV;
+ jseg = j1939_segment_find(dev->ifindex);
+ if (jseg)
+ put_j1939_segment(jseg);
+ NLA_PUT_U8(skb, IFLA_J1939_ENABLE, jseg ? 1 : 0);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int j1939_set_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+ int ret;
+ struct nlattr *tb[IFLA_J1939_MAX];
+
+ ret = nla_parse_nested(tb, IFLA_J1939_MAX-1, nla, j1939_ifla_policy);
+ if (ret < 0)
+ return ret;
+
+ if (tb[IFLA_J1939_ENABLE]) {
+ if (nla_get_u8(tb[IFLA_J1939_ENABLE]))
+ ret = j1939_segment_attach(dev);
+ else
+ ret = j1939_segment_detach(dev);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+const struct rtnl_af_ops j1939_rtnl_af_ops = {
+ .family = AF_CAN,
+ .fill_link_af = j1939_fill_link_af,
+ .get_link_af_size = j1939_get_link_af_size,
+ .validate_link_af = j1939_validate_link_af,
+ .set_link_af = j1939_set_link_af,
+};
+
diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
new file mode 100644
index 0000000..c94e3b1
--- /dev/null
+++ b/net/can/j1939/socket.c
@@ -0,0 +1,983 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/if_arp.h>
+#include <net/tcp_states.h>
+
+#include <linux/can/core.h>
+#include <linux/can/j1939.h>
+#include "j1939-priv.h"
+
+struct j1939_sock {
+ struct sock sk; /* must be first to skip with memset */
+ struct list_head list;
+
+ int state;
+ #define JSK_BOUND BIT(0)
+ #define JSK_CONNECTED BIT(1)
+ #define PROMISC BIT(2)
+ #define RECV_OWN BIT(3)
+
+ struct {
+ name_t src, dst, mask;
+ pgn_t pgn;
+
+ uint8_t sa, da;
+ } addr;
+
+ struct j1939_filter *filters;
+ int nfilters;
+
+ int skb_pending;
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+};
+
+static inline struct j1939_sock *j1939_sk(const struct sock *sk)
+{
+ return container_of(sk, struct j1939_sock, sk);
+}
+
+/* skb_pending issues */
+static inline int j1939_sock_pending_add_first(struct sock *sk)
+{
+ int saved;
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ spin_lock_bh(&jsk->lock);
+ if (!jsk->skb_pending) {
+ ++jsk->skb_pending;
+ saved = 1;
+ } else
+ saved = 0;
+ spin_unlock_bh(&jsk->lock);
+ return saved;
+}
+
+static inline void j1939_sock_pending_add(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ spin_lock_bh(&jsk->lock);
+ ++jsk->skb_pending;
+ spin_unlock_bh(&jsk->lock);
+}
+
+void j1939_sock_pending_del(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int saved;
+
+ spin_lock_bh(&jsk->lock);
+ --jsk->skb_pending;
+ saved = jsk->skb_pending;
+ spin_unlock_bh(&jsk->lock);
+ if (!saved)
+ wake_up(&jsk->waitq);
+}
+
+
+static inline int j1939_no_address(const struct sock *sk)
+{
+ const struct j1939_sock *jsk = j1939_sk(sk);
+ return (jsk->addr.sa == J1939_NO_ADDR) && !jsk->addr.src;
+}
+
+/*
+ * list of sockets
+ */
+static struct {
+ struct mutex lock;
+ struct list_head socks;
+} s;
+
+/* matches skb control buffer (addr) with a j1939 filter */
+static inline int packet_match(const struct j1939_sk_buff_cb *cb,
+ const struct j1939_filter *f, int nfilter)
+{
+ /*
+ * Filters relying on the addr for static addressing _should_ get
+ * packets from dynamic addressed ECU's too if they match their SA.
+ * Sockets using dynamic addressing in their filters should not set it.
+ */
+ for (; nfilter; ++f, --nfilter) {
+ if ((cb->pgn & f->pgn_mask) != (f->pgn & f->pgn_mask))
+ return 0;
+ if ((cb->src.addr & f->addr_mask) != (f->addr & f->addr_mask))
+ return 0;
+ if ((cb->src.name & f->name_mask) != (f->name & f->name_mask))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * callback per socket, called from filter infrastructure
+ */
+static void j1939sk_recv_skb(struct sk_buff *oskb, void *data)
+{
+ struct sk_buff *skb;
+ struct j1939_sock *jsk = (struct j1939_sock *)data;
+ struct j1939_sk_buff_cb *cb = (void *)oskb->cb;
+
+ if (jsk->sk.sk_bound_dev_if && (jsk->sk.sk_bound_dev_if != cb->ifindex))
+ /* this socket does not take packets from this iface */
+ return;
+ if (!(jsk->state & PROMISC)) {
+ if (cb->dst.flags & ECUFLAG_REMOTE)
+ /*
+ * this msg was destined for an ECU associated
+ * with this socket
+ */
+ return;
+ if (jsk->addr.src) {
+ if (cb->dst.name &&
+ (cb->dst.name != jsk->addr.src))
+ /*
+ * the msg is not destined for the name
+ * that the socket is bound to
+ */
+ return;
+ } else if (j1939_address_is_unicast(jsk->addr.sa)) {
+ if (j1939_address_is_unicast(cb->dst.addr) &&
+ (cb->dst.addr != jsk->addr.sa))
+ /*
+ * the msg is not destined for the name
+ * that the socket is bound to
+ */
+ return;
+ }
+ }
+
+ if ((oskb->sk == &jsk->sk) && !(jsk->state & RECV_OWN))
+ /* own message */
+ return;
+
+ if (!packet_match(cb, jsk->filters, jsk->nfilters))
+ return;
+
+ skb = skb_clone(oskb, GFP_ATOMIC);
+ if (!skb) {
+ j1939_warning("skb clone failed\n");
+ return;
+ }
+ cb = (void *)skb->cb;
+ cb->msg_flags &= ~(MSG_DONTROUTE | MSG_CONFIRM);
+ if (oskb->sk)
+ cb->msg_flags |= MSG_DONTROUTE;
+ if (oskb->sk == &jsk->sk)
+ cb->msg_flags |= MSG_CONFIRM;
+
+ skb->sk = &jsk->sk;
+ if (sock_queue_rcv_skb(&jsk->sk, skb) < 0) {
+ kfree_skb(skb);
+ j1939_warning("sock_queue_rcv_skb failed\n");
+ }
+}
+
+static int j1939sk_init(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ INIT_LIST_HEAD(&jsk->list);
+ spin_lock_init(&jsk->lock);
+ init_waitqueue_head(&jsk->waitq);
+ jsk->sk.sk_priority = j1939_to_sk_priority(6);
+ jsk->sk.sk_reuse = 1; /* per default */
+ jsk->addr.sa = J1939_NO_ADDR;
+ jsk->addr.da = J1939_NO_ADDR;
+ /* per default, mask out the serial number in destinations */
+ jsk->addr.mask = 0xffffffffffe00000ULL;
+
+ return 0;
+}
+
+/*
+ * helper: return <0 for error, >0 for error to notify
+ */
+static int j1939sk_bind_netdev_helper(struct socket *sock)
+{
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ int ret;
+ struct net_device *netdev;
+ struct j1939_segment *jseg;
+
+ if (!jsk->sk.sk_bound_dev_if)
+ return 0;
+ ret = 0;
+
+ netdev = dev_get_by_index(&init_net, jsk->sk.sk_bound_dev_if);
+ if (!netdev) {
+ ret = -ENODEV;
+ goto fail_netdev;
+ }
+
+ /* no need to test for CAN device,
+ * implicitely done by j1939_segment
+ */
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg) {
+ ret = -EHOSTDOWN;
+ goto fail_segment;
+ }
+
+ if (!(netdev->flags & IFF_UP)) {
+ sock->sk->sk_err = ENETDOWN;
+ sock->sk->sk_error_report(sock->sk);
+ }
+ put_j1939_segment(jseg);
+fail_segment:
+ dev_put(netdev);
+fail_netdev:
+ return ret;
+}
+
+static int j1939sk_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ struct j1939_ecu *ecu = NULL;
+ int ret, old_state;
+
+ if (len < required_size(can_addr.j1939, *addr))
+ return -EINVAL;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ /* lock s.lock first, to avoid circular lock dependancy */
+ mutex_lock(&s.lock);
+ lock_sock(sock->sk);
+ if (jsk->state & JSK_BOUND) {
+ ret = -EBUSY;
+ if (addr->can_ifindex != jsk->sk.sk_bound_dev_if)
+ goto fail_locked;
+ /*
+ * do not allow to change addres after first bind(),
+ * (it would require updating the j1939_ecu list)
+ * but allow the change SA when using dynaddr,
+ * and allow to change PGN
+ */
+ if (!jsk->addr.src ||
+ (jsk->addr.src != addr->can_addr.j1939.name) ||
+ (jsk->addr.pgn != addr->can_addr.j1939.pgn))
+ goto fail_locked;
+ j1939_recv_suspend();
+ /* set to be able to send address claims */
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ j1939_recv_resume();
+ /* since this socket is bound already, we can skip a lot */
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return 0;
+ }
+
+ /* do netdev */
+ if (jsk->sk.sk_bound_dev_if && addr->can_ifindex &&
+ (jsk->sk.sk_bound_dev_if != addr->can_ifindex)) {
+ ret = -EBADR;
+ goto fail_locked;
+ }
+ if (!jsk->sk.sk_bound_dev_if)
+ jsk->sk.sk_bound_dev_if = addr->can_ifindex;
+
+ ret = j1939sk_bind_netdev_helper(sock);
+ if (ret < 0)
+ goto fail_locked;
+
+ /* bind name/addr */
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_by_name(addr->can_addr.j1939.name,
+ jsk->sk.sk_bound_dev_if);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ } else if (ecu->flags & ECUFLAG_REMOTE) {
+ ret = -EREMOTE;
+ goto fail_with_ecu;
+ } else if (jsk->sk.sk_bound_dev_if != ecu->ifindex) {
+ ret = -EHOSTUNREACH;
+ goto fail_with_ecu;
+ }
+ jsk->addr.src = ecu->name;
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else if (j1939_address_is_unicast(addr->can_addr.j1939.addr)) {
+ struct j1939_segment *jseg;
+ struct addr_ent *paddr;
+ int flags;
+
+ /* static addressing, netdev is required */
+ if (!jsk->sk.sk_bound_dev_if) {
+ ret = -EINVAL;
+ goto fail_locked;
+ }
+ jseg = j1939_segment_find(jsk->sk.sk_bound_dev_if);
+ if (!jseg) {
+ ret = -ENETUNREACH;
+ goto fail_locked;
+ }
+ paddr = &jseg->ents[addr->can_addr.j1939.addr];
+ ret = 0;
+ read_lock_bh(&jseg->lock);
+ flags = paddr->flags;
+ read_unlock_bh(&jseg->lock);
+ put_j1939_segment(jseg);
+ if (!(flags & ECUFLAG_STATIC)) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ }
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else if (addr->can_addr.j1939.addr == J1939_IDLE_ADDR) {
+ /* static addressing, netdev is required */
+ if (!jsk->sk.sk_bound_dev_if) {
+ ret = -EINVAL;
+ goto fail_locked;
+ }
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else {
+ /* no name, no addr */
+ }
+
+ /* set default transmit pgn/priority */
+ jsk->addr.pgn = addr->can_addr.j1939.pgn;
+
+ old_state = jsk->state;
+ jsk->state |= JSK_BOUND;
+
+ if (!(old_state & (JSK_BOUND | JSK_CONNECTED))) {
+ list_add_tail(&jsk->list, &s.socks);
+ j1939_recv_add(jsk, j1939sk_recv_skb);
+ }
+
+ ret = 0;
+
+fail_with_ecu:
+ if (ecu && !IS_ERR(ecu))
+ put_j1939_ecu(ecu);
+fail_locked:
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return ret;
+}
+
+static int j1939sk_connect(struct socket *sock, struct sockaddr *uaddr,
+ int len, int flags)
+{
+ int ret, old_state;
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ struct j1939_ecu *ecu;
+ int ifindex;
+
+ if (!uaddr)
+ return -EDESTADDRREQ;
+
+ if (len < required_size(can_addr.j1939, *addr))
+ return -EINVAL;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ mutex_lock(&s.lock);
+ lock_sock(sock->sk);
+ if (jsk->state & JSK_CONNECTED) {
+ ret = -EISCONN;
+ goto fail_locked;
+ }
+
+ ifindex = jsk->sk.sk_bound_dev_if;
+ if (ifindex && addr->can_ifindex && (ifindex != addr->can_ifindex)) {
+ ret = -ECONNREFUSED;
+ goto fail_locked;
+ }
+ if (!ifindex)
+ ifindex = addr->can_ifindex;
+
+ /* lookup destination */
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_name_masked(addr->can_addr.j1939.name,
+ jsk->addr.mask, ifindex);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ }
+ if (ifindex && (ifindex != ecu->ifindex)) {
+ ret = -EHOSTUNREACH;
+ goto fail_locked;
+ }
+ ifindex = ecu->ifindex;
+ jsk->addr.dst = ecu->name;
+ jsk->addr.da = ecu->sa;
+ put_j1939_ecu(ecu);
+ } else {
+ /* broadcast */
+ jsk->addr.dst = 0;
+ jsk->addr.da = addr->can_addr.j1939.addr;
+ }
+ /*
+ * take a default source when not present, so connected sockets
+ * will stick to the same source ECU
+ */
+ if (!jsk->addr.src && !j1939_address_is_valid(jsk->addr.sa)) {
+ ecu = j1939_ecu_find_segment_default_tx(ifindex,
+ &jsk->addr.src, &jsk->addr.sa);
+ if (IS_ERR(ecu)) {
+ ret = PTR_ERR(ecu);
+ goto fail_locked;
+ }
+ put_j1939_ecu(ecu);
+ }
+
+ /* start assigning, no problem can occur at this point anymore */
+ jsk->sk.sk_bound_dev_if = ifindex;
+
+ if (!(jsk->state & JSK_BOUND) || !pgn_is_valid(jsk->addr.pgn)) {
+ /*
+ * bind() takes precedence over connect() for the
+ * pgn to use ourselve
+ */
+ jsk->addr.pgn = addr->can_addr.j1939.pgn;
+ }
+
+ old_state = jsk->state;
+ jsk->state |= JSK_CONNECTED;
+
+ if (!(old_state & (JSK_BOUND | JSK_CONNECTED))) {
+ list_add_tail(&jsk->list, &s.socks);
+ j1939_recv_add(jsk, j1939sk_recv_skb);
+ }
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return 0;
+
+fail_locked:
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return ret;
+}
+
+static void j1939sk_sock2sockaddr_can(struct sockaddr_can *addr,
+ const struct j1939_sock *jsk, int peer)
+{
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = jsk->sk.sk_bound_dev_if;
+ addr->can_addr.j1939.name = peer ? jsk->addr.dst : jsk->addr.src;
+ addr->can_addr.j1939.pgn = jsk->addr.pgn;
+ addr->can_addr.j1939.addr = peer ? jsk->addr.da : jsk->addr.sa;
+}
+
+static int j1939sk_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *len, int peer)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret = 0;
+
+ lock_sock(sk);
+
+ if (peer && !(jsk->state & JSK_CONNECTED)) {
+ ret = -EADDRNOTAVAIL;
+ goto failure;
+ }
+
+ j1939sk_sock2sockaddr_can(addr, jsk, peer);
+ *len = sizeof(*addr);
+
+failure:
+ release_sock(sk);
+
+ return ret;
+}
+
+static int j1939sk_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ j1939_recv_remove(jsk, j1939sk_recv_skb);
+ mutex_lock(&s.lock);
+ list_del_init(&jsk->list);
+ mutex_unlock(&s.lock);
+
+ lock_sock(sk);
+ if (jsk->state & PROMISC)
+ j1939_put_promisc_receiver(jsk->sk.sk_bound_dev_if);
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int j1939sk_setsockopt_flag(struct j1939_sock *jsk,
+ char __user *optval, unsigned int optlen, int flag)
+{
+ int tmp;
+
+ if (optlen != sizeof(tmp))
+ return -EINVAL;
+ if (copy_from_user(&tmp, optval, optlen))
+ return -EFAULT;
+ lock_sock(&jsk->sk);
+ if (tmp)
+ jsk->state |= flag;
+ else
+ jsk->state &= ~flag;
+ release_sock(&jsk->sk);
+ return tmp;
+}
+
+static int j1939sk_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret = 0, tmp, count;
+ struct j1939_filter *filters, *ofilters;
+
+ if (level != SOL_CAN_J1939)
+ return -EINVAL;
+
+ switch (optname) {
+ case SO_J1939_FILTER:
+ if (optval) {
+ if (optlen % sizeof(*filters) != 0)
+ return -EINVAL;
+ count = optlen / sizeof(*filters);
+ filters = kmalloc(optlen, GFP_KERNEL);
+ if (!filters)
+ return -ENOMEM;
+ if (copy_from_user(filters, optval, optlen)) {
+ kfree(filters);
+ return -EFAULT;
+ }
+ } else {
+ filters = NULL;
+ count = 0;
+ }
+
+ j1939_recv_suspend();
+ ofilters = jsk->filters;
+ jsk->filters = filters;
+ jsk->nfilters = count;
+ j1939_recv_resume();
+ kfree(ofilters);
+ break;
+ case SO_J1939_PROMISC:
+ tmp = jsk->state & PROMISC;
+ ret = j1939sk_setsockopt_flag(jsk, optval, optlen, PROMISC);
+ if (ret && !tmp)
+ j1939_get_promisc_receiver(jsk->sk.sk_bound_dev_if);
+ else if (!ret && tmp)
+ j1939_put_promisc_receiver(jsk->sk.sk_bound_dev_if);
+ ret = 0;
+ break;
+ case SO_J1939_RECV_OWN:
+ j1939sk_setsockopt_flag(jsk, optval, optlen, RECV_OWN);
+ break;
+ case SO_J1939_SEND_PRIO:
+ if (optlen != sizeof(tmp))
+ return -EINVAL;
+ if (copy_from_user(&tmp, optval, optlen))
+ return -EFAULT;
+ if ((tmp < 0) || (tmp > 7))
+ return -EDOM;
+ if ((tmp < 2) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+ lock_sock(&jsk->sk);
+ jsk->sk.sk_priority = j1939_to_sk_priority(tmp);
+ release_sock(&jsk->sk);
+ break;
+ case SO_J1939_DEST_MASK:
+ lock_sock(&jsk->sk);
+ if (optlen != sizeof(jsk->addr.mask))
+ ret = -EINVAL;
+ else if (jsk->state & JSK_CONNECTED)
+ ret = -EBUSY;
+ else if (copy_from_user(&jsk->addr.mask, optval, optlen))
+ ret = -EFAULT;
+ else
+ ret = 0;
+ release_sock(&jsk->sk);
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ return ret;
+}
+
+static int j1939sk_getsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret, ulen;
+ /* set defaults for using 'int' properties */
+ int tmp = 0;
+ int len = sizeof(tmp);
+ void *val = &tmp;
+
+ if (level != SOL_CAN_J1939)
+ return -EINVAL;
+ if (get_user(ulen, optlen))
+ return -EFAULT;
+ if (ulen < 0)
+ return -EINVAL;
+
+ lock_sock(&jsk->sk);
+ switch (optname) {
+ case SO_J1939_PROMISC:
+ tmp = (jsk->state & PROMISC) ? 1 : 0;
+ break;
+ case SO_J1939_RECV_OWN:
+ tmp = (jsk->state & RECV_OWN) ? 1 : 0;
+ break;
+ case SO_J1939_SEND_PRIO:
+ tmp = j1939_prio(jsk->sk.sk_priority);
+ break;
+ case SO_J1939_DEST_MASK:
+ val = &jsk->addr.mask;
+ len = sizeof(jsk->addr.mask);
+ break;
+ default:
+ ret = -ENOPROTOOPT;
+ goto no_copy;
+ }
+
+ /*
+ * copy to user, based on 'len' & 'val'
+ * but most sockopt's are 'int' properties, and have 'len' & 'val'
+ * left unchanged, but instead modified 'tmp'
+ */
+ if (len > ulen)
+ ret = -EFAULT;
+ else if (put_user(len, optlen))
+ ret = -EFAULT;
+ else if (copy_to_user(optval, val, len))
+ ret = -EFAULT;
+ else
+ ret = 0;
+no_copy:
+ release_sock(&jsk->sk);
+ return ret;
+}
+
+static int j1939sk_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *sk_addr;
+ int ret = 0;
+
+ skb = skb_recv_datagram(sk, flags, 0, &ret);
+ if (!skb)
+ return ret;
+
+ if (size < skb->len)
+ msg->msg_flags |= MSG_TRUNC;
+ else
+ size = skb->len;
+
+ ret = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (ret < 0)
+ goto failed_with_skb;
+
+ sock_recv_timestamp(msg, sk, skb);
+ sk_addr = (void *)skb->cb;
+
+ if (j1939_address_is_valid(sk_addr->dst.addr))
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_DEST_ADDR,
+ sizeof(sk_addr->dst.addr), &sk_addr->dst.addr);
+
+ if (sk_addr->dst.name)
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_DEST_NAME,
+ sizeof(sk_addr->dst.name), &sk_addr->dst.name);
+
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_PRIO,
+ sizeof(sk_addr->priority), &sk_addr->priority);
+
+ if (msg->msg_name) {
+ struct sockaddr_can *paddr = msg->msg_name;
+
+ msg->msg_namelen = required_size(can_addr.j1939, *paddr);
+ memset(msg->msg_name, 0, msg->msg_namelen);
+ paddr->can_family = AF_CAN;
+ paddr->can_ifindex = sk_addr->ifindex;
+ paddr->can_addr.j1939.name = sk_addr->src.name;
+ paddr->can_addr.j1939.addr = sk_addr->src.addr;
+ paddr->can_addr.j1939.pgn = sk_addr->pgn;
+ }
+
+ skb_free_datagram(sk, skb);
+
+ return size;
+
+failed_with_skb:
+ skb_kill_datagram(sk, skb, flags);
+ return ret;
+}
+
+static int j1939sk_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ struct j1939_sk_buff_cb *skb_cb;
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct j1939_ecu *ecu;
+ int ifindex;
+ int ret;
+
+ if (!(jsk->state | JSK_BOUND))
+ return -ENOTCONN;
+
+ if (msg->msg_name && (msg->msg_namelen <
+ required_size(can_addr.j1939, struct sockaddr_can)))
+ return -EINVAL;
+
+ ifindex = jsk->sk.sk_bound_dev_if;
+ if (msg->msg_name) {
+ struct sockaddr_can *addr = msg->msg_name;
+ if (msg->msg_namelen < required_size(can_addr.j1939, *addr))
+ return -EFAULT;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+ if (ifindex && addr->can_ifindex &&
+ (ifindex != addr->can_ifindex))
+ return -ENONET;
+ if (!ifindex)
+ /* take destination intf when intf not yet set */
+ ifindex = addr->can_ifindex;
+ }
+
+ if (!ifindex)
+ return -EDESTADDRREQ;
+ if (j1939_no_address(&jsk->sk)) {
+ lock_sock(&jsk->sk);
+ ecu = j1939_ecu_find_segment_default_tx(
+ jsk->sk.sk_bound_dev_if,
+ &jsk->addr.src, &jsk->addr.sa);
+ release_sock(&jsk->sk);
+ if (IS_ERR(ecu))
+ return PTR_ERR(ecu);
+ }
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev)
+ return -ENXIO;
+
+ skb = sock_alloc_send_skb(sk, size,
+ msg->msg_flags & MSG_DONTWAIT, &ret);
+ if (!skb)
+ goto put_dev;
+
+ ret = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ if (ret < 0)
+ goto free_skb;
+ skb->dev = dev;
+ skb->sk = sk;
+
+ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(*skb_cb));
+
+ skb_cb = (void *) skb->cb;
+ memset(skb_cb, 0, sizeof(*skb_cb));
+ skb_cb->msg_flags = msg->msg_flags;
+ skb_cb->ifindex = ifindex;
+ skb_cb->src.name = jsk->addr.src;
+ skb_cb->dst.name = jsk->addr.dst;
+ skb_cb->pgn = jsk->addr.pgn;
+ skb_cb->priority = j1939_prio(jsk->sk.sk_priority);
+ skb_cb->src.addr = jsk->addr.sa;
+ skb_cb->dst.addr = jsk->addr.da;
+
+ if (msg->msg_name) {
+ struct sockaddr_can *addr = msg->msg_name;
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_name_masked(
+ addr->can_addr.j1939.name, jsk->addr.mask,
+ ifindex);
+ if (!ecu)
+ return -EADDRNOTAVAIL;
+ skb_cb->dst.name = ecu->name;
+ skb_cb->dst.addr = ecu->sa;
+ put_j1939_ecu(ecu);
+ } else {
+ skb_cb->dst.name = 0;
+ skb_cb->dst.addr = addr->can_addr.j1939.addr;
+ }
+ if (pgn_is_valid(addr->can_addr.j1939.pgn))
+ skb_cb->pgn = addr->can_addr.j1939.pgn;
+ }
+
+ if (skb_cb->msg_flags & J1939_MSG_SYNC) {
+ if (skb_cb->msg_flags & MSG_DONTWAIT) {
+ ret = j1939_sock_pending_add_first(&jsk->sk);
+ if (ret > 0)
+ ret = -EAGAIN;
+ } else {
+ ret = wait_event_interruptible(jsk->waitq,
+ j1939_sock_pending_add_first(&jsk->sk));
+ }
+ if (ret < 0)
+ goto free_skb;
+ } else {
+ j1939_sock_pending_add(&jsk->sk);
+ }
+
+ ret = j1939_send(skb, j1939_level_sky);
+ if (ret < 0)
+ goto decrement_pending;
+
+ dev_put(dev);
+ return size;
+
+decrement_pending:
+ j1939_sock_pending_del(&jsk->sk);
+free_skb:
+ kfree_skb(skb);
+put_dev:
+ dev_put(dev);
+ return ret;
+}
+
+/* PROC */
+static int j1939sk_proc_show(struct seq_file *sqf, void *v)
+{
+ struct list_head *lst;
+ struct j1939_sock *jsk;
+ struct net_device *netdev;
+
+ seq_printf(sqf, "iface\tflags\tlocal\tremote\tpgn\tprio\tpending\n");
+ mutex_lock(&s.lock);
+ list_for_each(lst, &s.socks) {
+ jsk = list_entry(lst, struct j1939_sock, list);
+ lock_sock(&jsk->sk);
+ netdev = NULL;
+ if (jsk->sk.sk_bound_dev_if)
+ netdev = dev_get_by_index(&init_net,
+ jsk->sk.sk_bound_dev_if);
+ seq_printf(sqf, "%s\t", netdev ? netdev->name : "-");
+ if (netdev)
+ dev_put(netdev);
+ seq_printf(sqf, "%c%c%c%c\t",
+ (jsk->state & JSK_BOUND) ? 'b' : '-',
+ (jsk->state & JSK_CONNECTED) ? 'c' : '-',
+ (jsk->state & PROMISC) ? 'P' : '-',
+ (jsk->state & RECV_OWN) ? 'o' : '-');
+ if (jsk->addr.src)
+ seq_printf(sqf, "%016llx", (long long)jsk->addr.src);
+ seq_printf(sqf, ",%02x\t", jsk->addr.sa);
+ if (jsk->addr.dst)
+ seq_printf(sqf, "%016llx", (long long)jsk->addr.dst);
+ seq_printf(sqf, "/%016llx", (long long)jsk->addr.mask);
+ seq_printf(sqf, ",%02x\t", jsk->addr.da);
+ seq_printf(sqf, "%05x\t", jsk->addr.pgn);
+ seq_printf(sqf, "%u", j1939_prio(jsk->sk.sk_priority));
+ seq_printf(sqf, "\t%u", jsk->skb_pending);
+
+ release_sock(&jsk->sk);
+ seq_printf(sqf, "\n");
+ }
+ mutex_unlock(&s.lock);
+ return 0;
+}
+
+void j1939sk_netdev_event(int ifindex, int error_code)
+{
+ struct list_head *lst;
+ struct j1939_sock *sock;
+
+ mutex_lock(&s.lock);
+ list_for_each(lst, &s.socks) {
+ sock = list_entry(lst, struct j1939_sock, list);
+ if (sock->sk.sk_bound_dev_if != ifindex)
+ continue;
+ sock->sk.sk_err = error_code;
+ if (!sock_flag(&sock->sk, SOCK_DEAD))
+ sock->sk.sk_error_report(&sock->sk);
+ /* do not remove filters here */
+ }
+ mutex_unlock(&s.lock);
+}
+
+static const struct proto_ops j1939_ops = {
+ .family = PF_CAN,
+ .release = j1939sk_release,
+ .bind = j1939sk_bind,
+ .connect = j1939sk_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = j1939sk_getname,
+ .poll = datagram_poll,
+ .ioctl = can_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = j1939sk_setsockopt,
+ .getsockopt = j1939sk_getsockopt,
+ .sendmsg = j1939sk_sendmsg,
+ .recvmsg = j1939sk_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct proto j1939_proto __read_mostly = {
+ .name = "CAN_J1939",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct j1939_sock),
+ .init = j1939sk_init,
+};
+
+static const struct can_proto j1939_can_proto = {
+ .type = SOCK_DGRAM,
+ .protocol = CAN_J1939,
+ .ops = &j1939_ops,
+ .prot = &j1939_proto,
+
+ .rtnl_link_ops = &j1939_rtnl_af_ops,
+ .rtnl_new_addr = j1939rtnl_new_addr,
+ .rtnl_del_addr = j1939rtnl_del_addr,
+ .rtnl_dump_addr = j1939rtnl_dump_addr,
+};
+
+__init int j1939sk_module_init(void)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&s.socks);
+ mutex_init(&s.lock);
+
+ ret = can_proto_register(&j1939_can_proto);
+ if (ret < 0)
+ pr_err("can: registration of j1939 protocol failed\n");
+ else
+ j1939_proc_add("sock", j1939sk_proc_show, NULL);
+ return ret;
+}
+
+void j1939sk_module_exit(void)
+{
+ j1939_proc_remove("sock");
+ can_proto_unregister(&j1939_can_proto);
+}
+
+MODULE_ALIAS("can-proto-" __stringify(CAN_J1939));
+
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
new file mode 100644
index 0000000..5e39c62
--- /dev/null
+++ b/net/can/j1939/transport.c
@@ -0,0 +1,1481 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/skbuff.h>
+#include <linux/hrtimer.h>
+#include <linux/version.h>
+#include <linux/if_arp.h>
+#include <linux/wait.h>
+#include "j1939-priv.h"
+
+#define REGULAR 0
+#define EXTENDED 1
+
+#define etp_pgn_ctl 0xc800
+#define etp_pgn_dat 0xc700
+#define tp_pgn_ctl 0xec00
+#define tp_pgn_dat 0xeb00
+
+#define tp_cmd_bam 0x20
+#define tp_cmd_rts 0x10
+#define tp_cmd_cts 0x11
+#define tp_cmd_eof 0x13
+#define tp_cmd_abort 0xff
+
+#define etp_cmd_rts 0x14
+#define etp_cmd_cts 0x15
+#define etp_cmd_dpo 0x16
+#define etp_cmd_eof 0x17
+#define etp_cmd_abort 0xff
+
+#define ABORT_BUSY 1
+#define ABORT_RESOURCE 2
+#define ABORT_TIMEOUT 3
+#define ABORT_GENERIC 4
+#define ABORT_FAULT 5
+
+#define MAX_TP_PACKET_SIZE (7*255)
+#define MAX_ETP_PACKET_SIZE (7*0xffffff)
+
+static int block = 255;
+static int max_packet_size = 1024*100;
+static int retry_ms = 20;
+
+struct session {
+ struct list_head list;
+ atomic_t refs;
+ spinlock_t lock;
+
+ struct j1939_sk_buff_cb *cb; /*
+ * ifindex, src, dst, pgn define the session block
+ * the are _never_ modified after insertion in the list
+ * this decreases locking problems a _lot_
+ */
+ struct sk_buff *skb;
+
+ /*
+ * all tx related stuff (last_txcmd, pkt.tx)
+ * is protected (modified only) with the txtask tasklet
+ * 'total' & 'block' are never changed,
+ * last_cmd, last & block are protected by ->lock
+ * this means that the tx may run after cts is received that should
+ * have stopped tx, but this time discrepancy is never avoided anyhow
+ */
+ uint8_t last_cmd, last_txcmd;
+ uint8_t transmission;
+ uint8_t extd;
+ struct {
+ /*
+ * these do not require 16 bit, they should fit in uint8_t
+ * but putting in int makes it easier to deal with
+ */
+ unsigned int total, done, last, tx;
+ unsigned int block; /* for TP */
+ unsigned int dpo; /* for ETP */
+ } pkt;
+ struct hrtimer txtimer, rxtimer;
+ struct tasklet_struct txtask;
+};
+
+static struct j1939tp {
+ spinlock_t lock;
+ struct list_head sessionq;
+ struct list_head extsessionq;
+ struct {
+ struct list_head sessionq;
+ spinlock_t lock;
+ struct work_struct work;
+ } del;
+ wait_queue_head_t wait;
+ struct notifier_block notifier;
+} s;
+
+static struct session *j1939session_new(struct sk_buff *skb);
+static struct session *j1939session_fresh_new(int size,
+ struct j1939_sk_buff_cb *rel_cb, pgn_t pgn);
+
+static inline void fix_cb(struct j1939_sk_buff_cb *cb)
+{
+ cb->msg_flags &= ~J1939_MSG_RESERVED;
+}
+
+static inline struct session *list_to_session(struct list_head *lst)
+{
+ return container_of(lst, struct session, list);
+}
+
+static inline struct list_head *sessionq(int extd)
+{
+ return extd ? &s.extsessionq : &s.sessionq;
+}
+
+static inline void j1939session_destroy(struct session *session)
+{
+ if (session->skb)
+ kfree_skb(session->skb);
+ hrtimer_cancel(&session->rxtimer);
+ hrtimer_cancel(&session->txtimer);
+ tasklet_disable(&session->txtask);
+ kfree(session);
+}
+
+/* clean up work queue */
+static void j1939tp_del_work(struct work_struct *work)
+{
+ struct session *session;
+ int cnt = 0;
+
+ do {
+ session = NULL;
+ spin_lock_bh(&s.del.lock);
+ if (list_empty(&s.del.sessionq)) {
+ spin_unlock_bh(&s.del.lock);
+ break;
+ }
+ session = list_first_entry(&s.del.sessionq,
+ struct session, list);
+ list_del_init(&session->list);
+ spin_unlock_bh(&s.del.lock);
+ j1939session_destroy(session);
+ ++cnt;
+ } while (1);
+}
+/* reference counter */
+static inline void get_session(struct session *session)
+{
+ atomic_inc(&session->refs);
+}
+
+static void put_session(struct session *session)
+{
+ BUG_ON(!session);
+ if (atomic_add_return(-1, &session->refs) >= 0)
+ /* not the last one */
+ return;
+ /* it should have been removed from any list long time ago */
+ BUG_ON(!list_empty(&session->list));
+
+ hrtimer_try_to_cancel(&session->rxtimer);
+ hrtimer_try_to_cancel(&session->txtimer);
+ tasklet_disable_nosync(&session->txtask);
+
+ if (in_interrupt()) {
+ spin_lock_bh(&s.del.lock);
+ list_add_tail(&session->list, &s.del.sessionq);
+ spin_unlock_bh(&s.del.lock);
+ schedule_work(&s.del.work);
+ } else {
+ /* destroy session right here */
+ j1939session_destroy(session);
+ }
+}
+
+/* transport status locking */
+static inline void session_lock(struct session *session)
+{
+ get_session(session); /* safety measure */
+ spin_lock_bh(&session->lock);
+}
+
+static inline void session_unlock(struct session *session)
+{
+ spin_unlock_bh(&session->lock);
+ put_session(session);
+}
+
+static inline void sessionlist_lock(void)
+{
+ spin_lock_bh(&s.lock);
+}
+
+static inline void sessionlist_unlock(void)
+{
+ spin_unlock_bh(&s.lock);
+}
+
+/*
+ * see if we are receiver
+ * returns 0 for broadcasts, although we will receive them
+ */
+static inline int j1939tp_im_receiver(const struct j1939_sk_buff_cb *cb)
+{
+ return (cb->dst.flags & ECUFLAG_LOCAL) ? 1 : 0;
+}
+
+/* see if we are sender */
+static inline int j1939tp_im_transmitter(const struct j1939_sk_buff_cb *cb)
+{
+ return (cb->src.flags & ECUFLAG_LOCAL) ? 1 : 0;
+}
+
+/* see if we are involved as either receiver or transmitter */
+/* reverse = -1 means : any direction */
+static int j1939tp_im_involved(const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ if (reverse < 0) {
+ return ((cb->src.flags | cb->dst.flags) & ECUFLAG_LOCAL)
+ ? 1 : 0;
+ } else if (reverse) {
+ return j1939tp_im_receiver(cb);
+ } else {
+ return j1939tp_im_transmitter(cb);
+ }
+}
+
+/* extract pgn from flow-ctl message */
+static inline pgn_t j1939xtp_ctl_to_pgn(const uint8_t *dat)
+{
+ pgn_t pgn;
+
+ pgn = (dat[7] << 16) | (dat[6] << 8) | (dat[5] << 0);
+ if (pgn_is_pdu1(pgn))
+ pgn &= 0xffff00;
+ return pgn;
+}
+
+static inline unsigned int j1939tp_ctl_to_size(const uint8_t *dat)
+{
+ return (dat[2] << 8) + (dat[1] << 0);
+}
+static inline unsigned int j1939etp_ctl_to_packet(const uint8_t *dat)
+{
+ return (dat[4] << 16) | (dat[3] << 8) | (dat[2] << 0);
+}
+static inline unsigned int j1939etp_ctl_to_size(const uint8_t *dat)
+{
+ return (dat[4] << 24) | (dat[3] << 16) |
+ (dat[2] << 8) | (dat[1] << 0);
+}
+
+/*
+ * find existing session:
+ * reverse: swap cb's src & dst
+ * there is no problem with matching broadcasts, since
+ * broadcasts (no dst, no da) would never call this
+ * with reverse==1
+ */
+static int j1939tp_match(const struct j1939_sk_buff_cb *a,
+ const struct j1939_sk_buff_cb *b, int reverse)
+{
+ if (a->ifindex != b->ifindex)
+ return 0;
+ if (!reverse) {
+ if (a->src.name) {
+ if (a->src.name != b->src.name)
+ return 0;
+ } else if (a->src.addr != b->src.addr)
+ return 0;
+ if (a->dst.name) {
+ if (a->dst.name != b->dst.name)
+ return 0;
+ } else if (a->dst.addr != b->dst.addr)
+ return 0;
+ } else {
+ if (a->src.name) {
+ if (a->src.name != b->dst.name)
+ return 0;
+ } else if (a->src.addr != b->dst.addr)
+ return 0;
+ if (a->dst.name) {
+ if (a->dst.name != b->src.name)
+ return 0;
+ } else if (a->dst.addr != b->src.addr)
+ return 0;
+ }
+ return 1;
+}
+
+static struct session *_j1939tp_find(struct list_head *root,
+ const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ struct list_head *lst;
+ struct session *session;
+
+ list_for_each(lst, root) {
+ session = list_to_session(lst);
+ get_session(session);
+ if (j1939tp_match(cb, session->cb, reverse))
+ return session;
+ put_session(session);
+ }
+ return NULL;
+}
+
+static struct session *j1939tp_find(struct list_head *root,
+ const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ struct session *session;
+ sessionlist_lock();
+ session = _j1939tp_find(root, cb, reverse);
+ sessionlist_unlock();
+ return session;
+}
+
+static void j1939_skbcb_swap(struct j1939_sk_buff_cb *cb)
+{
+ name_t name;
+ uint8_t addr;
+ int flags;
+
+ name = cb->dst.name;
+ cb->dst.name = cb->src.name;
+ cb->src.name = name;
+
+ addr = cb->dst.addr;
+ cb->dst.addr = cb->src.addr;
+ cb->src.addr = addr;
+
+ flags = cb->dst.flags;
+ cb->dst.flags = cb->src.flags;
+ cb->src.flags = flags;
+}
+/*
+ * TP transmit packet functions
+ *
+ * When PGN >= NO_PGN, take the 'related' pgn
+ */
+static int j1939tp_tx_dat(struct session *related,
+ const uint8_t *dat, int len)
+{
+ int ret;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *skb_cb;
+ uint8_t *skdat;
+
+ skb = dev_alloc_skb(8);
+ if (unlikely(!skb)) {
+ pr_alert("%s: out of memory?\n", __func__);
+ return -ENOMEM;
+ }
+ skb->protocol = related->skb->protocol;
+ skb->pkt_type = related->skb->pkt_type;
+ skb->ip_summed = related->skb->ip_summed;
+ skb->sk = related->skb->sk;
+
+ skb_cb = (void *)skb->cb;
+ *skb_cb = *(related->cb);
+ fix_cb(skb_cb);
+ /* fix pgn */
+ skb_cb->pgn = related->extd ? etp_pgn_dat : tp_pgn_dat;
+
+ skdat = skb_put(skb, len);
+ memcpy(skdat, dat, len);
+ ret = j1939_send(skb, j1939_level_transport);
+ if (ret < 0)
+ kfree_skb(skb);
+ return ret;
+}
+static int j1939xtp_do_tx_ctl(struct sk_buff *related, int extd,
+ int swap_src_dst, pgn_t pgn, const uint8_t dat[5])
+{
+ int ret;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *skb_cb, *rel_cb;
+ uint8_t *skdat;
+
+ rel_cb = (void *)related->cb;
+ if (!j1939tp_im_involved(rel_cb, swap_src_dst))
+ return 0;
+
+ skb = dev_alloc_skb(8);
+ if (unlikely(!skb)) {
+ pr_alert("%s: out of memory?\n", __func__);
+ return -ENOMEM;
+ }
+ skb->protocol = related->protocol;
+ skb->pkt_type = related->pkt_type;
+ skb->ip_summed = related->ip_summed;
+ skb->sk = related->sk;
+
+ skb_cb = (void *)skb->cb;
+ *skb_cb = *rel_cb;
+ fix_cb(skb_cb);
+ if (swap_src_dst)
+ j1939_skbcb_swap(skb_cb);
+ /* fix pgn */
+ if (!pgn_is_valid(pgn))
+ /* save related pgn */
+ pgn = rel_cb->pgn;
+ skb_cb->pgn = extd ? etp_pgn_ctl : tp_pgn_ctl;
+
+ skdat = skb_put(skb, 8);
+ memcpy(skdat, dat, 5);
+ skdat[7] = (pgn >> 16) & 0xff;
+ skdat[6] = (pgn >> 8) & 0xff;
+ skdat[5] = (pgn >> 0) & 0xff;
+
+ ret = j1939_send(skb, j1939_level_transport);
+ if (ret)
+ kfree_skb(skb);
+ return ret;
+}
+
+static inline int j1939tp_tx_ctl(struct session *session,
+ int swap_src_dst, const uint8_t dat[8])
+{
+ return j1939xtp_do_tx_ctl(session->skb, session->extd, swap_src_dst,
+ NO_PGN, dat);
+}
+
+static int j1939xtp_tx_abort(struct sk_buff *related, int extd,
+ int swap_src_dst, pgn_t pgn, int err)
+{
+ struct j1939_sk_buff_cb *cb = (void *)related->cb;
+ uint8_t dat[5];
+
+ if (!j1939tp_im_involved(cb, swap_src_dst))
+ return 0;
+
+ memset(dat, 0xff, sizeof(dat));
+ dat[0] = tp_cmd_abort;
+ if (!extd)
+ dat[1] = err ?: ABORT_GENERIC;
+ return j1939xtp_do_tx_ctl(related, extd, swap_src_dst, pgn, dat);
+}
+
+/* timer & scheduler functions */
+static inline void j1939session_schedule_txnow(struct session *session)
+{
+ tasklet_schedule(&session->txtask);
+}
+static enum hrtimer_restart j1939tp_txtimer(struct hrtimer *hrtimer)
+{
+ struct session *session =
+ container_of(hrtimer, struct session, txtimer);
+ j1939session_schedule_txnow(session);
+ return HRTIMER_NORESTART;
+}
+static inline void j1939tp_schedule_txtimer(struct session *session, int msec)
+{
+ hrtimer_start(&session->txtimer,
+ ktime_set(msec / 1000, (msec % 1000)*1000000UL),
+ HRTIMER_MODE_REL);
+}
+static inline void j1939tp_set_rxtimeout(struct session *session, int msec)
+{
+ hrtimer_start(&session->rxtimer,
+ ktime_set(msec / 1000, (msec % 1000)*1000000UL),
+ HRTIMER_MODE_REL);
+}
+
+/*
+ * session completion functions
+ */
+/*
+ * j1939session_drop
+ * removes a session from open session list
+ */
+static inline void j1939session_drop(struct session *session)
+{
+ sessionlist_lock();
+ list_del_init(&session->list);
+ sessionlist_unlock();
+
+ if (session->transmission) {
+ if (session->skb && session->skb->sk)
+ j1939_sock_pending_del(session->skb->sk);
+ wake_up_all(&s.wait);
+ }
+ put_session(session);
+}
+
+static inline void j1939session_completed(struct session *session)
+{
+ j1939_recv(session->skb, j1939_level_transport);
+ j1939session_drop(session);
+}
+
+static void j1939session_cancel(struct session *session, int err)
+{
+ if ((err >= 0) && j1939tp_im_involved(session->cb, -1)) {
+ if (!j1939cb_is_broadcast(session->cb)) {
+ /* do not send aborts on incoming broadcasts */
+ j1939xtp_tx_abort(session->skb, session->extd,
+ !j1939tp_im_transmitter(session->cb),
+ NO_PGN, err);
+ }
+ }
+ j1939session_drop(session);
+}
+
+static enum hrtimer_restart j1939tp_rxtimer(struct hrtimer *hrtimer)
+{
+ struct session *session =
+ container_of(hrtimer, struct session, rxtimer);
+ pr_alert("%s: timeout on %i\n", __func__, session->cb->ifindex);
+ j1939session_cancel(session, ABORT_TIMEOUT);
+ return HRTIMER_NORESTART;
+}
+
+/*
+ * receive packet functions
+ */
+static void _j1939xtp_rx_bad_message(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (session /*&& (session->cb->pgn == pgn)*/) {
+ /* do not allow TP control messages on 2 pgn's */
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ j1939xtp_tx_abort(skb, extd, 0, ABORT_FAULT, pgn);
+ if (!session)
+ return;
+ put_session(session); /* ~j1939tp_find */
+}
+
+/* abort packets may come in 2 directions */
+static void j1939xtp_rx_bad_message(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ pr_info("%s, pgn %05x\n", __func__, j1939xtp_ctl_to_pgn(skb->data));
+ _j1939xtp_rx_bad_message(skb, extd);
+ j1939_skbcb_swap(cb);
+ _j1939xtp_rx_bad_message(skb, extd);
+ /* restore skb */
+ j1939_skbcb_swap(cb);
+ return;
+}
+
+static void _j1939xtp_rx_abort(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session)
+ return;
+ if (session->transmission && !session->last_txcmd) {
+ /*
+ * empty block:
+ * do not drop session when a transmit session did not
+ * start yet
+ */
+ } else if (session->cb->pgn == pgn)
+ j1939session_drop(session);
+ /* another PGN had a bad message */
+ /*
+ * TODO: maybe cancel current connection
+ * as another pgn was communicated
+ */
+ put_session(session); /* ~j1939tp_find */
+}
+/* abort packets may come in 2 directions */
+static inline void j1939xtp_rx_abort(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ pr_info("%s %i, %05x\n", __func__, cb->ifindex, cb->pgn);
+ _j1939xtp_rx_abort(skb, extd);
+ j1939_skbcb_swap(cb);
+ _j1939xtp_rx_abort(skb, extd);
+ /* restore skb */
+ j1939_skbcb_swap(cb);
+ return;
+}
+
+static void j1939xtp_rx_eof(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ /* end of tx cycle */
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 1);
+ if (!session)
+ /*
+ * strange, we had EOF on closed connection
+ * do nothing, as EOF closes the connection anyway
+ */
+ return;
+
+ if (session->cb->pgn != pgn) {
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ } else {
+ /* transmitted without problems */
+ j1939session_completed(session);
+ }
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_cts(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+ unsigned int pkt;
+ const uint8_t *dat;
+
+ dat = skb->data;
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 1);
+ if (!session) {
+ /* 'CTS shall be ignored' */
+ return;
+ }
+ if (session->cb->pgn != pgn) {
+ /* what to do? */
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ session_lock(session);
+ pkt = extd ? j1939etp_ctl_to_packet(dat) : dat[2];
+ if (!dat[0])
+ hrtimer_cancel(&session->txtimer);
+ else if (!pkt)
+ goto bad_fmt;
+ else if (dat[1] > session->pkt.block /* 0xff for etp */)
+ goto bad_fmt;
+ else {
+ /* set packet counters only when not CTS(0) */
+ session->pkt.done = pkt - 1;
+ session->pkt.last = session->pkt.done + dat[1];
+ if (session->pkt.last > session->pkt.total)
+ /* safety measure */
+ session->pkt.last = session->pkt.total;
+ /* TODO: do not set tx here, do it in txtask */
+ session->pkt.tx = session->pkt.done;
+ }
+ session->last_cmd = dat[0];
+ session_unlock(session);
+ if (dat[1]) {
+ j1939tp_set_rxtimeout(session, 1250);
+ if (j1939tp_im_transmitter(session->cb))
+ j1939session_schedule_txnow(session);
+ } else {
+ /* CTS(0) */
+ j1939tp_set_rxtimeout(session, 550);
+ }
+ put_session(session); /* ~j1939tp_find */
+ return;
+bad_fmt:
+ session_unlock(session);
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_rts(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ int len;
+ const uint8_t *dat;
+ pgn_t pgn;
+
+ dat = skb->data;
+ pgn = j1939xtp_ctl_to_pgn(dat);
+
+ if ((tp_cmd_rts == dat[0]) && j1939cb_is_broadcast(cb)) {
+ pr_alert("%s: rts without destination (%i %02x)\n", __func__,
+ cb->ifindex, cb->src.addr);
+ return;
+ }
+ /*
+ * TODO: abort RTS when a similar
+ * TP is pending in the other direction
+ */
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (session && !j1939tp_im_transmitter(cb)) {
+ /* RTS on pending connection */
+ j1939session_cancel(session, ABORT_BUSY);
+ if ((pgn != session->cb->pgn) && (tp_cmd_bam != dat[0]))
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ } else if (!session && j1939tp_im_transmitter(cb)) {
+ pr_alert("%s: I should tx (%i %02x %02x)\n", __func__,
+ cb->ifindex, cb->src.addr, cb->dst.addr);
+ return;
+ }
+ if (session && (0 != session->last_cmd)) {
+ /* we received a second rts on the same connection */
+ pr_alert("%s: connection exists (%i %02x %02x)\n", __func__,
+ cb->ifindex, cb->src.addr, cb->dst.addr);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ if (session) {
+ /*
+ * make sure 'sa' & 'da' are correct !
+ * They may be 'not filled in yet' for sending
+ * skb's, since they did not pass the Address Claim ever.
+ */
+ session->cb->src.addr = cb->src.addr;
+ session->cb->dst.addr = cb->dst.addr;
+ } else {
+ int abort = 0;
+ if (extd) {
+ len = j1939etp_ctl_to_size(dat);
+ if (len > (max_packet_size ?: MAX_ETP_PACKET_SIZE))
+ abort = ABORT_RESOURCE;
+ else if (len <= MAX_TP_PACKET_SIZE)
+ abort = ABORT_FAULT;
+ } else {
+ len = j1939tp_ctl_to_size(dat);
+ if (len > MAX_TP_PACKET_SIZE)
+ abort = ABORT_FAULT;
+ else if (max_packet_size && (len > max_packet_size))
+ abort = ABORT_RESOURCE;
+ }
+ if (abort) {
+ j1939xtp_tx_abort(skb, extd, 1, abort, pgn);
+ return;
+ }
+ session = j1939session_fresh_new(len, cb, pgn);
+ if (!session) {
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_RESOURCE, pgn);
+ return;
+ }
+ session->extd = extd;
+ /* initialize the control buffer: plain copy */
+ session->pkt.total = (len+6)/7;
+ session->pkt.block = 0xff;
+ if (!extd) {
+ if (dat[3] != session->pkt.total)
+ pr_alert("%s: strange total,"
+ " %u != %u\n", __func__,
+ session->pkt.total, dat[3]);
+ session->pkt.total = dat[3];
+ session->pkt.block = dat[4];
+ }
+ session->pkt.done = session->pkt.tx = 0;
+ get_session(session); /* equivalent to j1939tp_find() */
+ sessionlist_lock();
+ list_add_tail(&session->list, sessionq(extd));
+ sessionlist_unlock();
+ }
+ session->last_cmd = dat[0];
+
+ j1939tp_set_rxtimeout(session, 1250);
+
+ if (j1939tp_im_receiver(session->cb)) {
+ if (extd || (tp_cmd_bam != dat[0]))
+ j1939session_schedule_txnow(session);
+ }
+ /*
+ * as soon as it's inserted, things can go fast
+ * protect against a long delay
+ * between spin_unlock & next statement
+ * so, only release here, at the end
+ */
+ put_session(session); /* ~j1939tp_find */
+ return;
+}
+
+static void j1939xtp_rx_dpo(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+ const uint8_t *dat = skb->data;
+
+ pgn = j1939xtp_ctl_to_pgn(dat);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session) {
+ pr_info("%s: %s\n", __func__, "no connection found");
+ return;
+ }
+
+ if (session->cb->pgn != pgn) {
+ pr_info("%s: different pgn\n", __func__);
+ j1939xtp_tx_abort(skb, 1, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ /* transmitted without problems */
+ session->pkt.dpo = j1939etp_ctl_to_packet(skb->data);
+ session->last_cmd = dat[0];
+ j1939tp_set_rxtimeout(session, 750);
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_dat(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ const uint8_t *dat;
+ uint8_t *tpdat;
+ int offset;
+ int nbytes;
+ int final;
+ int do_cts_eof;
+ int packet;
+
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session) {
+ pr_info("%s:%s\n", __func__, "no connection found");
+ return;
+ }
+ dat = skb->data;
+ if (skb->len <= 1)
+ /* makes no sense */
+ goto strange_packet_unlocked;
+
+ session_lock(session);
+
+ switch (session->last_cmd) {
+ case 0xff:
+ break;
+ case etp_cmd_dpo:
+ if (extd)
+ break;
+ case tp_cmd_bam:
+ case tp_cmd_cts:
+ if (!extd)
+ break;
+ default:
+ pr_info("%s: last %02x\n", __func__,
+ session->last_cmd);
+ goto strange_packet;
+ }
+
+ packet = (dat[0]-1+session->pkt.dpo);
+ offset = packet * 7;
+ if ((packet > session->pkt.total) ||
+ (session->pkt.done+1) > session->pkt.total) {
+ pr_info("%s: should have been completed\n", __func__);
+ goto strange_packet;
+ }
+ nbytes = session->skb->len - offset;
+ if (nbytes > 7)
+ nbytes = 7;
+ if ((nbytes <= 0) || ((nbytes + 1) > skb->len)) {
+ pr_info("%s: nbytes %i, len %i\n", __func__, nbytes,
+ skb->len);
+ goto strange_packet;
+ }
+ tpdat = session->skb->data;
+ memcpy(&tpdat[offset], &dat[1], nbytes);
+ if (packet == session->pkt.done)
+ ++session->pkt.done;
+
+ if (!extd && j1939cb_is_broadcast(session->cb)) {
+ final = session->pkt.done >= session->pkt.total;
+ do_cts_eof = 0;
+ } else {
+ final = 0; /* never final, an EOF must follow */
+ do_cts_eof = (session->pkt.done >= session->pkt.last);
+ }
+ session_unlock(session);
+ if (final) {
+ j1939session_completed(session);
+ } else if (do_cts_eof) {
+ j1939tp_set_rxtimeout(session, 1250);
+ if (j1939tp_im_receiver(session->cb))
+ j1939session_schedule_txnow(session);
+ } else {
+ j1939tp_set_rxtimeout(session, 250);
+ }
+ session->last_cmd = 0xff;
+ put_session(session); /* ~j1939tp_find */
+ return;
+
+strange_packet:
+ /* unlock session (spinlock) before trying to send */
+ session_unlock(session);
+strange_packet_unlocked:
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+}
+
+/*
+ * transmit function
+ */
+static int j1939tp_txnext(struct session *session)
+{
+ uint8_t dat[8];
+ const uint8_t *tpdat;
+ int ret, offset, len, pkt_done, pkt_end;
+ unsigned int pkt;
+
+ memset(dat, 0xff, sizeof(dat));
+ get_session(session); /* do not loose it */
+
+ switch (session->last_cmd) {
+ case 0:
+ if (!j1939tp_im_transmitter(session->cb))
+ break;
+ dat[1] = (session->skb->len >> 0) & 0xff;
+ dat[2] = (session->skb->len >> 8) & 0xff;
+ dat[3] = session->pkt.total;
+ if (session->extd) {
+ dat[0] = etp_cmd_rts;
+ dat[1] = (session->skb->len >> 0) & 0xff;
+ dat[2] = (session->skb->len >> 8) & 0xff;
+ dat[3] = (session->skb->len >> 16) & 0xff;
+ dat[4] = (session->skb->len >> 24) & 0xff;
+ } else if (j1939cb_is_broadcast(session->cb)) {
+ dat[0] = tp_cmd_bam;
+ /* fake cts for broadcast */
+ session->pkt.tx = 0;
+ } else {
+ dat[0] = tp_cmd_rts;
+ dat[4] = dat[3];
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 0, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ /* must lock? */
+ if (tp_cmd_bam == dat[0])
+ j1939tp_schedule_txtimer(session, 50);
+ j1939tp_set_rxtimeout(session, 1250);
+ break;
+ case tp_cmd_rts:
+ case etp_cmd_rts:
+ if (!j1939tp_im_receiver(session->cb))
+ break;
+tx_cts:
+ ret = 0;
+ len = session->pkt.total - session->pkt.done;
+ if (len > 255)
+ len = 255;
+ if (len > session->pkt.block)
+ len = session->pkt.block;
+ if (block && (len > block))
+ len = block;
+
+ if (session->extd) {
+ pkt = session->pkt.done+1;
+ dat[0] = etp_cmd_cts;
+ dat[1] = len;
+ dat[2] = (pkt >> 0) & 0xff;
+ dat[3] = (pkt >> 8) & 0xff;
+ dat[4] = (pkt >> 16) & 0xff;
+ } else {
+ dat[0] = tp_cmd_cts;
+ dat[1] = len;
+ dat[2] = session->pkt.done+1;
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 1, dat);
+ if (ret < 0)
+ goto failed;
+ if (len)
+ /* only mark cts done when len is set */
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ break;
+ case etp_cmd_cts:
+ if (j1939tp_im_transmitter(session->cb) && session->extd &&
+ (etp_cmd_dpo != session->last_txcmd)) {
+ /* do dpo */
+ dat[0] = etp_cmd_dpo;
+ session->pkt.dpo = session->pkt.done;
+ pkt = session->pkt.dpo;
+ dat[1] = session->pkt.last - session->pkt.done;
+ dat[2] = (pkt >> 0) & 0xff;
+ dat[3] = (pkt >> 8) & 0xff;
+ dat[4] = (pkt >> 16) & 0xff;
+ ret = j1939tp_tx_ctl(session, 0, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ session->pkt.tx = session->pkt.done;
+ }
+ case tp_cmd_cts:
+ case 0xff: /* did some data */
+ case etp_cmd_dpo:
+ if ((session->extd || !j1939cb_is_broadcast(session->cb)) &&
+ j1939tp_im_receiver(session->cb)) {
+ if (session->pkt.done >= session->pkt.total) {
+ if (session->extd) {
+ dat[0] = etp_cmd_eof;
+ dat[1] = session->skb->len >> 0;
+ dat[2] = session->skb->len >> 8;
+ dat[3] = session->skb->len >> 16;
+ dat[4] = session->skb->len >> 24;
+ } else {
+ dat[0] = tp_cmd_eof;
+ dat[1] = session->skb->len;
+ dat[2] = session->skb->len >> 8;
+ dat[3] = session->pkt.total;
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 1, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ /* wait for the EOF packet to come in */
+ break;
+ } else if (session->pkt.done >= session->pkt.last) {
+ session->last_txcmd = 0;
+ goto tx_cts;
+ }
+ }
+ case tp_cmd_bam:
+ if (!j1939tp_im_transmitter(session->cb))
+ break;
+ tpdat = session->skb->data;
+ ret = 0;
+ pkt_done = 0;
+ pkt_end = (!session->extd && j1939cb_is_broadcast(session->cb))
+ ? session->pkt.total : session->pkt.last;
+
+ while (session->pkt.tx < pkt_end) {
+ dat[0] = session->pkt.tx - session->pkt.dpo+1;
+ offset = session->pkt.tx * 7;
+ len = session->skb->len - offset;
+ if (len > 7)
+ len = 7;
+ memcpy(&dat[1], &tpdat[offset], len);
+ ret = j1939tp_tx_dat(session, dat, len+1);
+ if (ret < 0)
+ break;
+ session->last_txcmd = 0xff;
+ ++pkt_done;
+ ++session->pkt.tx;
+ if (j1939cb_is_broadcast(session->cb)) {
+ if (session->pkt.tx < session->pkt.total)
+ j1939tp_schedule_txtimer(session, 50);
+ break;
+ }
+ }
+ if (pkt_done)
+ j1939tp_set_rxtimeout(session, 250);
+ if (ret)
+ goto failed;
+ break;
+ }
+ put_session(session);
+ return 0;
+failed:
+ put_session(session);
+ return ret;
+}
+
+static void j1939tp_txtask(unsigned long val)
+{
+ struct session *session = (void *)val;
+ int ret;
+ ktime_t ktime;
+
+ get_session(session);
+ ret = j1939tp_txnext(session);
+ if (ret < 0) {
+ if (ret == -EADDRNOTAVAIL) {
+ /*
+ * during address claiming delays, postpone
+ * the rxtimer too
+ */
+ ktime = hrtimer_get_remaining(&session->rxtimer);
+ if ((ktime_to_ns(ktime)*NSEC_PER_MSEC) < (retry_ms+20))
+ j1939tp_set_rxtimeout(session, retry_ms+20);
+ }
+ j1939tp_schedule_txtimer(session, retry_ms);
+ }
+ put_session(session);
+}
+
+static inline int j1939tp_tx_initial(struct session *session)
+{
+ int ret;
+
+ get_session(session);
+ while (1) {
+ ret = j1939tp_txnext(session);
+ if (!ret)
+ break;
+ else if (session->cb->msg_flags & MSG_DONTWAIT)
+ break;
+ else if (ret == -EADDRNOTAVAIL) {
+ /* address claim delay */
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ msleep(retry_ms);
+ }
+ }
+
+ /* clear MSG_DONTWAIT flag here, it makes no sense from this point on */
+ session->cb->msg_flags &= ~MSG_DONTWAIT;
+
+ put_session(session);
+ return ret;
+}
+
+/* this call is to be used as probe within wait_event_xxx() */
+static int j1939session_insert(struct session *session)
+{
+ struct session *pending;
+
+ sessionlist_lock();
+ pending = _j1939tp_find(sessionq(session->extd), session->cb, 0);
+ if (pending)
+ /* revert the effect of find() */
+ put_session(pending);
+ else
+ list_add_tail(&session->list, sessionq(session->extd));
+ sessionlist_unlock();
+ return pending ? 0 : 1;
+}
+/*
+ * j1939 main intf
+ */
+int j1939_send_transport(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ int ret;
+
+ if ((tp_pgn_dat == cb->pgn) || (tp_pgn_ctl == cb->pgn) ||
+ (etp_pgn_dat == cb->pgn) || (etp_pgn_ctl == cb->pgn))
+ /* avoid conflict */
+ return -EDOM;
+ if (skb->len <= 8)
+ return 0;
+ else if (skb->len > (max_packet_size ?: MAX_ETP_PACKET_SIZE))
+ return -EMSGSIZE;
+
+ if (skb->len > MAX_TP_PACKET_SIZE) {
+ if (j1939cb_is_broadcast(cb))
+ return -EDESTADDRREQ;
+ }
+
+ /* prepare new session */
+ session = j1939session_new(skb);
+ if (!session)
+ return -ENOMEM;
+
+ session->extd = (skb->len > MAX_TP_PACKET_SIZE) ? EXTENDED : REGULAR;
+ session->transmission = 1;
+ session->pkt.total = (skb->len + 6)/7;
+ session->pkt.block = session->extd ? 255 :
+ (block ?: session->pkt.total);
+ if (j1939cb_is_broadcast(session->cb))
+ /* set the end-packet for broadcast */
+ session->pkt.last = session->pkt.total;
+
+ /* insert into queue, but avoid collision with pending session */
+ if (session->cb->msg_flags & MSG_DONTWAIT)
+ ret = j1939session_insert(session) ? 0 : -EAGAIN;
+ else
+ ret = wait_event_interruptible(s.wait,
+ j1939session_insert(session));
+ if (ret < 0)
+ goto failed;
+
+ ret = j1939tp_tx_initial(session);
+ if (!ret)
+ /* transmission started */
+ return RESULT_STOP;
+ sessionlist_lock();
+ list_del_init(&session->list);
+ sessionlist_unlock();
+failed:
+ /*
+ * hide the skb from j1939session_drop, as it would
+ * kfree_skb, but our caller will kfree_skb(skb) too.
+ */
+ session->skb = NULL;
+ j1939session_drop(session);
+ return ret;
+}
+
+int j1939_recv_transport(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ const uint8_t *dat;
+
+ switch (cb->pgn) {
+ case etp_pgn_dat:
+ j1939xtp_rx_dat(skb, EXTENDED);
+ break;
+ case etp_pgn_ctl:
+ if (skb->len < 8) {
+ j1939xtp_rx_bad_message(skb, EXTENDED);
+ break;
+ }
+ dat = skb->data;
+ switch (*dat) {
+ case etp_cmd_rts:
+ j1939xtp_rx_rts(skb, EXTENDED);
+ break;
+ case etp_cmd_cts:
+ j1939xtp_rx_cts(skb, EXTENDED);
+ break;
+ case etp_cmd_dpo:
+ j1939xtp_rx_dpo(skb, EXTENDED);
+ break;
+ case etp_cmd_eof:
+ j1939xtp_rx_eof(skb, EXTENDED);
+ break;
+ case etp_cmd_abort:
+ j1939xtp_rx_abort(skb, EXTENDED);
+ break;
+ default:
+ j1939xtp_rx_bad_message(skb, EXTENDED);
+ break;
+ }
+ break;
+ case tp_pgn_dat:
+ j1939xtp_rx_dat(skb, REGULAR);
+ break;
+ case tp_pgn_ctl:
+ if (skb->len < 8) {
+ j1939xtp_rx_bad_message(skb, REGULAR);
+ break;
+ }
+ dat = skb->data;
+ switch (*dat) {
+ case tp_cmd_bam:
+ case tp_cmd_rts:
+ j1939xtp_rx_rts(skb, REGULAR);
+ break;
+ case tp_cmd_cts:
+ j1939xtp_rx_cts(skb, REGULAR);
+ break;
+ case tp_cmd_eof:
+ j1939xtp_rx_eof(skb, REGULAR);
+ break;
+ case tp_cmd_abort:
+ j1939xtp_rx_abort(skb, REGULAR);
+ break;
+ default:
+ j1939xtp_rx_bad_message(skb, REGULAR);
+ break;
+ }
+ break;
+ default:
+ return 0;
+ }
+ return RESULT_STOP;
+}
+
+static struct session *j1939session_fresh_new(int size,
+ struct j1939_sk_buff_cb *rel_cb, pgn_t pgn)
+{
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *cb;
+ struct session *session;
+
+ skb = dev_alloc_skb(size);
+ if (!skb)
+ return NULL;
+ cb = (void *)skb->cb;
+ *cb = *rel_cb;
+ fix_cb(cb);
+ cb->pgn = pgn;
+
+ session = j1939session_new(skb);
+ if (!session) {
+ kfree(skb);
+ return NULL;
+ }
+ /* alloc data area */
+ skb_put(skb, size);
+ return session;
+}
+static struct session *j1939session_new(struct sk_buff *skb)
+{
+ struct session *session;
+
+ session = kzalloc(sizeof(*session), gfp_any());
+ if (!session)
+ return NULL;
+ INIT_LIST_HEAD(&session->list);
+ spin_lock_init(&session->lock);
+ session->skb = skb;
+
+ session->cb = (void *)session->skb->cb;
+ hrtimer_init(&session->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ session->txtimer.function = j1939tp_txtimer;
+ hrtimer_init(&session->rxtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ session->rxtimer.function = j1939tp_rxtimer;
+ tasklet_init(&session->txtask, j1939tp_txtask, (unsigned long)session);
+ return session;
+}
+
+static int j1939tp_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *netdev = (struct net_device *)data;
+ struct session *session;
+ struct list_head *lst, *saved;
+
+ if (!net_eq(dev_net(netdev), &init_net))
+ return NOTIFY_DONE;
+
+ if (netdev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ if (msg != NETDEV_UNREGISTER)
+ return NOTIFY_DONE;
+
+ sessionlist_lock();
+ list_for_each_safe(lst, saved, &s.sessionq) {
+ session = list_to_session(lst);
+ if (session->cb->ifindex != netdev->ifindex)
+ continue;
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ list_for_each_safe(lst, saved, &s.extsessionq) {
+ session = list_to_session(lst);
+ if (session->cb->ifindex != netdev->ifindex)
+ continue;
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ sessionlist_unlock();
+ return NOTIFY_DONE;
+}
+
+/* SYSCTL */
+static struct ctl_table_header *j1939tp_table_header;
+
+static int min_block = 1;
+static int max_block = 255;
+static int min_packet = 8;
+static int max_packet = ((2 << 24)-1)*7;
+
+static int min_retry = 5;
+static int max_retry = 5000;
+
+static ctl_table j1939tp_table[] = {
+ {
+ .procname = "transport_cts_nr_of_frames",
+ .data = &block,
+ .maxlen = sizeof(block),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_block,
+ .extra2 = &max_block,
+ },
+ {
+ .procname = "transport_max_payload_in_bytes",
+ .data = &max_packet_size,
+ .maxlen = sizeof(max_packet_size),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_packet,
+ .extra2 = &max_packet,
+ },
+ {
+ .procname = "transport_tx_retry_ms",
+ .data = &retry_ms,
+ .maxlen = sizeof(retry_ms),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_retry,
+ .extra2 = &max_retry,
+ },
+ { },
+};
+
+static struct ctl_path j1939tp_path[] = {
+ { .procname = "net", },
+ { .procname = j1939_procname, },
+ { }
+};
+
+/* PROC */
+static int j1939tp_proc_show_session(struct seq_file *sqf,
+ struct session *session)
+{
+ seq_printf(sqf, "%i", session->cb->ifindex);
+ if (session->cb->src.name)
+ seq_printf(sqf, "\t%016llx", session->cb->src.name);
+ else
+ seq_printf(sqf, "\t%02x", session->cb->src.addr);
+ if (session->cb->dst.name)
+ seq_printf(sqf, "\t%016llx", session->cb->dst.name);
+ else if (j1939_address_is_unicast(session->cb->dst.addr))
+ seq_printf(sqf, "\t%02x", session->cb->dst.addr);
+ else
+ seq_printf(sqf, "\t-");
+ seq_printf(sqf, "\t%05x\t%u/%u\n", session->cb->pgn,
+ session->pkt.done*7, session->skb->len);
+ return 0;
+}
+
+static int j1939tp_proc_show(struct seq_file *sqf, void *v)
+{
+ struct list_head *lst;
+
+ seq_printf(sqf, "iface\tsrc\tdst\tpgn\tdone/total\n");
+ sessionlist_lock();
+ list_for_each(lst, &s.sessionq)
+ j1939tp_proc_show_session(sqf, list_to_session(lst));
+ list_for_each(lst, &s.extsessionq)
+ j1939tp_proc_show_session(sqf, list_to_session(lst));
+ sessionlist_unlock();
+ return 0;
+}
+
+int __init j1939tp_module_init(void)
+{
+ spin_lock_init(&s.lock);
+ INIT_LIST_HEAD(&s.sessionq);
+ INIT_LIST_HEAD(&s.extsessionq);
+ spin_lock_init(&s.del.lock);
+ INIT_LIST_HEAD(&s.del.sessionq);
+ INIT_WORK(&s.del.work, j1939tp_del_work);
+
+ s.notifier.notifier_call = j1939tp_notifier;
+ register_netdevice_notifier(&s.notifier);
+
+ j1939_proc_add("transport", j1939tp_proc_show, NULL);
+ j1939tp_table_header =
+ register_sysctl_paths(j1939tp_path, j1939tp_table);
+ init_waitqueue_head(&s.wait);
+ return 0;
+}
+
+void j1939tp_module_exit(void)
+{
+ struct session *session;
+
+ wake_up_all(&s.wait);
+
+ unregister_sysctl_table(j1939tp_table_header);
+ unregister_netdevice_notifier(&s.notifier);
+ j1939_proc_remove("transport");
+ sessionlist_lock();
+ while (!list_empty(&s.extsessionq)) {
+ session = list_to_session(s.extsessionq.next);
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ while (!list_empty(&s.sessionq)) {
+ session = list_to_session(s.sessionq.next);
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ sessionlist_unlock();
+ flush_scheduled_work();
+}
+
^ permalink raw reply related
* [RFC v3 5/6] j1939: add documentation and MAINTAINERS
From: Kurt Van Dijck @ 2011-03-14 13:59 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>
This patch adds the documentation and MAINTAINERS.
This is seperated from 4/6 for size constraints
on berlios' SocketCAN mailing list.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/Documentation/networking/j1939.txt b/Documentation/networking/j1939.txt
new file mode 100644
index 0000000..690e0a0
--- /dev/null
+++ b/Documentation/networking/j1939.txt
@@ -0,0 +1,532 @@
+============================================================================
+
+j1939.txt
+
+Readme file for the J1939 Protocol
+
+This file contains
+
+ 1 Overview / What is j1939
+ 1.1 specifications used
+
+ 2 Motivation
+
+ 3 J1939 concepts
+ 3.1 socket type
+ 3.2 addressing
+ 3.3 priority
+ 3.4 PGN
+ 3.5 filtering
+ 3.6 destinations with dynamic address
+
+ 4 How to use J1939
+ 4.1 rtnetlink interface
+ 4.2 API calls
+ 4.2.1 Message flags during sendmsg
+ 4.3 dynamic addressing
+
+ 5 socket options
+ 5.1 SO_J1939_FILTER
+ 5.2 SO_J1939_PROMISC
+ 5.3 SO_J1939_RECV_OWN
+ 5.4 SO_J1939_RECV_DEST
+ 5.5 SO_J1939_RECV_PRIO
+ 5.6 SO_J1939_SEND_PRIO
+ 5.7 SO_J1939_DEST_MASK
+
+ 6 can-j1939 procfs interface
+ 6.1 /proc/net/can-j1939/ecu
+ 6.2 /proc/net/can-j1939/filter
+ 6.3 /proc/net/can-j1939/sock
+ 6.4 /proc/net/can-j1939/transport
+
+ 7 can-j1939 SYSCTL
+ 7.1 /proc/sys/net/can-j1939/transport_max_payload_in_bytes
+ 7.2 /proc/sys/net/can-j1939/transport_cts_nr_of_frames
+ 7.3 /proc/sys/net/can-j1939/transport_tx_retry_ms
+
+ 8 Credits
+
+============================================================================
+
+1. Introduction
+--------------------------------
+
+ SAE J1939 defines a higher layer protocol on CAN. It implements a more
+ sophisticated addressing scheme and extends the maximum packet size above
+ 8 bytes. Several derived specifications exists, which differ from the
+ original j1939 on the application level, like MilCAN, NMEA2000 and
+ especially ISO-11783 (ISOBUS). This last one specifies the so-called ETP
+ (Extended Transport Protocol) which is has been included in this
+ implementation. This inclusion results in a maximum packet size of
+ ((2^24)-1)*7 bytes
+
+
+1.1 specifications used
+
+ SAE J1939-21 : data link layer
+ SAE J1939-81 : network management
+ ISO 11783-6 : Virtual Terminal (Extended Transport Protocol)
+
+
+2. Motivation
+--------------------------------
+
+ Given the fact there's something like SocketCAN with an API similar to BSD
+ sockets, we found some reasons to justify a kernel implementation for the
+ addressing and transport methods used by J1939.
+
+ * addressing:
+ When a process on an ECU communicates via j1939, it should not necessarily
+ know its source address. Although at least 1 process per ECU should know
+ the source address. Other processes should be able to reuse that address.
+ This way, address parameters for different processes cooperating for the
+ same ECU, are not duplicated.
+ This way of working is closely related to the unix concept where programs
+ do just 1 thing, and do it well.
+
+ * dynamic addressing:
+ Address Claiming in J1939 is time critical. Furthermore data transport
+ should be handled properly during the address negotiation. Putting these
+ functionality in the kernel eliminates this functionality as a requirement
+ for _every_ userspace process that communicates via J1939. This results in
+ a consistent J1939 bus with proper addressing.
+
+ * transport:
+ Both TP & ETP reuse some PGN's to relay big packets over them. Different
+ processes may thus use the same TP & ETP PGN's without actually knowing it.
+ The individual TP & ETP sessions _must_ be serialized (synchronised)
+ between different processes. The kernel solves this problem properly, and
+ eliminates the serialisation (synchronisation) as a requirement for
+ _every_ userspace process that communicates via J1939.
+
+ J1939 defines some other features (relaying, gateway, Fast Packet transport,
+ ...). In-kernel code for these would not contribute to protocol stability.
+ Therefore, these parts are left to userspace.
+
+ The j1939 sockets operate on CAN network devices (see SocketCAN). Any j1939
+ userspace library operating on CAN raw sockets will still operate properly.
+ Since such library does not communicate with the in-kernel implementation,
+ care must be taken that these 2 do not interfere. In practice, this means
+ they cannot share ECU addresses. A single ECU (or virtual ECU) address is
+ used by the library exclusively, or by the in-kernel system exclusively.
+
+
+3. J1939 concepts
+--------------------------------
+
+3.1 PGN
+
+ The PGN (Parameter Group Number) is a number to identify a packet. The PGN
+ is composed as follows:
+ 1 bit : Reserved Bit
+ 1 bit : Data Page
+ 8 bits : PF (PDU Format)
+ 8 bits : PS (PDU Specific)
+
+ In J1939-21, distinction is made between PDU1 Format (where PF < 240) and
+ PDU2 Format (where PF >= 240). Furthermore, when using PDU2 Format, the
+ PS-field contains a so-called Group Extension, which is part of the PGN.
+ When using PDU2 Format, the Group Extension is set in the PS-field.
+
+ On the other hand, when using PDU1 Format, the PS-field contains a so-called
+ Destination Address, which is _not_ part of the PGN. When communicating a
+ PGN from userspace to kernel (or visa versa) and PDU2 Format is used, the
+ PS-field of the PGN shall be set to zero. The Destination Address shall be
+ set elsewhere.
+
+ Regarding PGN mapping to 29-bit CAN identifier, the Destination Address
+ shall be get/set from/to the apropriate bits of the identifier by the kernel.
+
+
+3.2 addressing
+
+ Both static and dynamic addressing methods can be used.
+
+ For static addresses, no extra checks are made by the kernel, and provided
+ addresses are considered right. This responsibility is for the OEM or system
+ integrator.
+
+ For dynamic addressing, so-called Address Claiming, extra support is forseen
+ in the kernel. In J1939 any ECU is known by it's 64-bit NAME. At the moment
+ of succesfull address claim, the kernel keeps track of both NAME and source
+ address being claimed. This serves as a base for filter schemes. By default,
+ packets with a destination that is not locally, will be rejected soon after
+ reception.
+
+ Mixed mode packets (from a static to a dynamic address or vice versa) are
+ allowed. The BSD sockets define seperate API calls for getting/setting the
+ local & remote address and are applicable for J1939 sockets.
+
+
+3.3 Filtering
+
+ Similar to SocketCAN, j1939 defines filters per socket that a user can set
+ in order to receive a subset of the j1939 traffic. Filtering can base on
+ * SA
+ * NAME
+ * PGN
+
+ There is a semantic difference with SocketCAN with regard to filtering.
+ When multiple filters are in place for a single socket, and a packet comes
+ in that matches several of those filters, the packet is only received once
+ for that socket.
+ The rationale behind this difference originates in the filter capabilities.
+ Where SocketCAN filters on only 1 orthogonal (can id), J1939 can filter
+ on 3 orthogonal properties (sa, name, pgn).
+
+ When a filter on the SA is set, j1939 traffic with a matching SA, but with
+ its NAME set (aka having claimed SA successfully) will match, although
+ the filter would not match its NAME.
+
+ Filtering on priority is _not_ supported.
+
+
+4. How to use J1939
+--------------------------------
+
+4.1 rtnetlink interface
+
+ Per default j1939 is not active. Specifying can_ifindex != 0 in bind(2)
+ or connect(2) needs an active j1939 on that interface. You must have done
+ $ ip link set canX j1939 on
+ on that interface.
+
+ $ ip link set canX j1939 down
+ disables j1939 on canX.
+
+ Assigning addresses is done via
+ $ ip addr add dev canX j1939 0xXX
+ statically or
+ $ ip addr add dev canX j1939 name 0xXX
+ dynamically. In the latter case, address claiming must take place
+ before other traffic can leave.
+
+ Removing addresses is done similarly via
+ $ ip addr del dev canX j1939 0xXX
+ $ ip addr del dev canX j1939 name 0xXX
+
+ A static address cannot be assigned together with a 64bit name.
+
+4.2 API calls
+
+ Like TCP/IP and CAN, you first need to open a socket for communicating over a
+ CAN network. To use j1939, include <include/linux/j1939.h>. From there,
+ <include/linux/can.h> will be included too.
+ To open a socket, you would write
+
+ s = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
+
+ J1939 does use SOCK_DGRAM sockets. In the j1939 specification, connections are
+ mentioned in the context of transport protocol sessions. These still deliver
+ packets to the other end (using several CAN packets).
+ SOCK_STREAM is never appropriate.
+
+ After the successful creation of the socket, you would normally use the
+ bind(2) and/or connect(2) system call to bind the socket to a CAN interface
+ (which is different from TCP/IP due to different addressing) After binding
+ and/or connecting the socket, you can read(2) and write(2) from/to the socket
+ or use send(2), sendto(2), sendmsg(2) and the recv* counterpart operations on
+ the socket as usual. There are also J1939 specific socket options described
+ below.
+
+ In order to send data, a bind(2) must have succeeded. bind(2) assigns a local
+ address to a socket. For this to succeed, you can only choose addresses
+ that have been assigned earlier (see 4.1). When an empty address is assigned
+ (ie. SA=0xff && name=0), a default is taken for the device that is bound to.
+
+ Different from CAN is that the payload data is just the data that get send,
+ without it's header info. The header info is derived from the sockaddr
+ supplied to bind(2), connect(2), sendto(2) and recvfrom(2). A write(2) with
+ size 4 will result in a packet with 4 bytes.
+
+ The sockaddr structure has extensions for use with j1939 as specified below:
+ struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct {
+ __u64 name;
+ __u32 pgn;
+ __u8 addr;
+ } j1939;
+ } can_addr;
+ }
+
+ can_family & can_ifindex serve the same purpose as for other SocketCAN sockets.
+
+ can_addr.j1939.pgn specifies the PGN (max 0x3ffff). Individual bits are
+ specified above.
+
+ can_addr.j1939.name contains the 64-bit J1939 NAME.
+
+ can_addr.j1939.addr contains the source address.
+
+ When sending data, the source address is applied as follows: If
+ can_addr.j1939.name != 0 the NAME is looked up by the kernel and the
+ corresponding Source Address is used. If can_addr.j1939.name == 0,
+ can_addr.j1939.addr is used.
+
+ After a bind(2), the local address is assigned, i.e. the source address.
+ After a connect(2), the remote address is assigned, i.e. the destination
+ address.
+
+ Both write(2) and send(2) will send a packet with local address from bind,
+ remote address from connect(2). When the address was not set, a broadcast is
+ sent. The PGN is used from bind(2) or overruled with sendto(2), which will
+ override the destination address when valid, and the PGN when valid.
+
+ Both read(2) and recv(2) will receive packets matching the sockets filters.
+ recvfrom(2) will receive these packets with originator's address.
+
+ When creating a socket, reasonable defaults have been set. Some options can be
+ modified with setsockopt(2) & getsockopt(2).
+
+4.2.1 Message flags during sendmsg
+
+ send(2), sendto(2) and sendmsg(2) take a 'flags' argument. J1939 interpretes
+ these flags during outgoing traffic:
+
+ * MSG_DONTWAIT determines nonblocking operation. When a packet must wait for
+ any reason, -EAGAIN is returned.
+
+ * MSG_SYN
+ Packets flagged with MSG_SYN will wait for all pending packets on a socket
+ to be sent before trying to send. This means that if a socket just started
+ a Transport Protocol session, a packet with MSG_SYN will wait for that
+ session to complete before proceeding.
+ Traffic without MSG_SYN (on that very same socket) will still continue.
+
+4.3 Dynamic Addressing
+
+ Distinction has to be made in and using the claimed address and doing an
+ address claim. To use an already claimed address, one has to fill in the
+ j1939.name member and provide it to bind(2). If the name had claimed an
+ address earlier, all further PGN's being sent will use that address. And the
+ j1939.addr member will be ignored.
+
+ An exception on this is pgn 0x0ee00. This is the "Address Claim/Cannot Claim
+ Address" message and when the kernel will use the j1939.addr member for that
+ pgn if necessary.
+
+ To claim an address, bind(2) with:
+ j1939.pgn set to 0x0ee00
+ j1939.addr set to the desired Source Address.
+ j1939.name set to the NAME you want the Source Address to claim to.
+
+ Afterwards do a write(2) with data set to the NAME (Little Endian). If the
+ NAME provided, does not match the j1939.name provided to bind(2), EPROTO
+ will be returned. One might use sendto(2) also to send the Addres Claim. In
+ that case, the j1939.addr member must be set to the broadcast address (255)
+ and the j1939.pgn must be set to 0x0ee00. If This combination is not given,
+ EPROTO is returned.
+
+ If no-one else contest the address claim within 250ms after transmission, the
+ kernel marks the NAME-SA assignment as valid. The valid assignment will be
+ kept, among other valid NAME-SA assignments. From that point, any socket
+ bound to the NAME can send packets.
+
+ If another ECU claims the address, the kernel will mark the NAME-SA expired.
+ No socket bound to the NAME can send packets (other than address claims).
+ To claim another address, some socket bound to NAME, must bind(2) again,
+ but with only j1939.addr changed to the new SA, and must then send a
+ valid address claim packet. This restarts the state machine in the kernel
+ (and any other participant on the bus) for this NAME.
+
+
+5 Socket Options
+--------------------------------
+
+ j1939 sockets have some options that are configurable via setsockopt(2).
+ Each of those options is initialized with a reasonable default.
+
+
+5.1 SO_J1939_FILTER
+
+ As mentioned above, J1939 supports filtering in both NAME, Source Address
+ and PGN. All members must match.
+
+ struct j1939_filter filter = {
+ .name = ...
+ .name_mask = ...
+ .addr = ...
+ .addr_mask = ...
+ .pgn = ...
+ .pgn_mask = ...
+ }
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_FILTER, &filter, sizeof(filter));
+
+
+5.2 SO_J1939_PROMISC
+
+ When set, j1939 will receive all packets, not just those with a destination
+ on the local system.
+ default off.
+
+ int promisc = 1; /* 0 = disabled (default), 1 = enabled */
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_PROMISC, &promisc, sizeof(promisc));
+
+
+5.3 SO_J1939_RECV_OWN
+
+ All the sent j1939 packets are looped back in the system.
+ The reception of the j1939 packets on the same socket that was
+ sending the j1939 packet is assumed to be unwanted and therefore
+ disabled by default. This default behaviour may be changed on
+ demand:
+
+ int recv_own_msgs = 1; /* 0 = disabled (default), 1 = enabled */
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_RECV_OWN,
+ &recv_own_msgs, sizeof(recv_own_msgs));
+
+
+5.4 SO_J1939_RECV_DEST
+
+ Received j1939 packets that make their way up to the socket, had a destination
+ address matching the socket's local address. This can have several reasons:
+ - broadcast packet.
+ - destination spec matches the local address
+ - destination spec matches _a_ local address on the system, and the socket
+ had no local address defined.
+ - SO_J1939_PROMISC was set
+ If the user is interested in the original destination spec, SO_J1939_RECV_DEST
+ can be changed to 1 to receive the destination spec with each packet.
+ The destination is attached to the msghdr in the recvmsg(2) call.
+ It can be extracted using cmsg(3) macros, with
+ cmsg_level == SOL_J1939 && cmsg_type == SCM_J1939_DEST.
+ the returned data is a struct sockaddr_can, with the regular j1939 fields
+ stuffed. Mind that PGN is the same as the PGN in sockaddr after recvfrom.
+
+
+5.5 SO_J1939_RECV_PRIO
+
+ As stated earlier, the priority field is stripped very soon. In order to
+ allow retreiving the packet's priority, SO_J1939_RECV_PRIO can be set to 1.
+
+ As a result, an extra int will be attached during recvmsg(2), similar
+ as in SO_J1939_RECV_DEST, but with cmsg_type == SCM_J1939_PRIO
+
+6.6 SO_J1939_SEND_PRIO
+
+ To set the priority field for outgoing packets, the SO_J1939_SEND_PRIO can
+ be changed. This int field specifies the priority that will be used.
+ j1939 defines a priority between 0 and 7 inclusive,
+ with 7 the lowest priority.
+ Per default, the priority is set to 6 (conforming J1939).
+ This priority socket option operates on the same value that is modified
+ with
+
+ setsockopt(s, SOL_SOCKET, SO_PRIORITY, &pri, sizeof(pri))
+
+ socketoption, with a difference that SOL_SOCKET/SO_PRIORITY is defined with
+ 0 the lowest priority. SOL_CAN_J1939/SO_J1939_SEND_PRIO inverts this value
+ for you.
+
+5.7 SO_J1939_DEST_MASK
+
+ When a destination is specified by its name (and thus using dynamic addressing),
+ and such name should be unique amongst the world, it may be hard to predict the
+ name of eg. a gearbox controller on the bus, although its type and manufacturer
+ are know. This is because the serial number is part of the name.
+ To simplify specifying a destination, a per-socket destination mask is provided
+ that is activated whenever a destination name is wanted. Any bits cleared in
+ this mask are ignored during the lookup. As a result, more than 1 ECU may match
+ this name/mask pair. In all cases, the first match is used.
+ The API is thus capable of specifying a name for eg. the gearbox controller,
+ without knowing its serial number.
+ This mask can mask out any part in the name. Note there's only 1 mask per socket.
+
+ this mask is default set to mask the serial number.
+
+ when can_addr.j1939.name is used for destination in outgoing packets
+ (see bind(2), sendto(2)), the exact name is often not known due to serial
+ numbers in it.
+ Therefore, SO_J1939_DEST_MASK sets an uint64_t mask that will be used
+ for resolving these names. Only the bits set to 1 in the mask will be
+ evaluated for find the destination name.
+ Per default, the mask is set to mask out the serial number
+ (0xffffffffffe00000ULL)
+
+ to mask out only the manufacturer code (bits 21-31), do
+
+ uint64_t mask = 0xffffffff001fffffULL;
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_DEST_MASK, &mask, sizeof(mask));
+
+
+6. /proc/net/can-j1939 Interface.
+--------------------------------
+
+ Files giving you a view on the in-kernel operation of J1939 are located at:
+ /proc/net/j1939.
+
+6.1 /proc/net/can-j1939/ecu
+
+ This file gives an overview of the known ECU's to the kernel.
+ - iface : network interface they operate on.
+ - SA : current address.
+ - name : 64bit NAME
+ - flags : 'L' = local, 'R' = remote
+
+6.2 /proc/net/can-j1939/filter
+
+6.3 /proc/net/can-j1939/sock
+
+ This file gives a list of all j1939 sockets currently open.
+ - iface : network interface
+ - flags :
+ 'b' : bound
+ 'c' : connected
+ 'P' : PROMISC
+ 'o' : RECV_OWN
+ 'd' : RECV_DEST
+ 'p' : RECV_PRIO
+ - local: [NAME],SA
+ - remote: [NAME]/MASK,DA
+ - pgn : PGN
+ - prio : priority
+ - pending : # packets pending (see MSG_SYN on 4.2.1)
+
+6.4 /proc/net/can-j1939/transport
+
+ This file shows a list of pending transport sessions
+ - iface
+ - src : XX (addr) or XXXXXXXXXXXXXXXX (name)
+ - dst : XX or XXXXXXXXXXXXXXXX or '-' (broadcast)
+ - pgn :
+ - done/total : current # transferred bytes / total
+
+
+7. /proc/sys/net/can-j1939 - SYSCTL
+--------------------------------
+
+ Via these sysctl files, some parameters of the j1939 module can be tuned.
+
+7.1 /proc/sys/net/can-j1939/transport_max_payload_in_bytes [int]
+
+ Is the maximum packet size to accept on both transmit & receive side.
+ Bigger packets will be rejected (local sender), aborted (local receiver)
+ or ignored (broadcasts & remote recievers in PROMISC).
+
+7.2 /proc/sys/net/can-j1939/transport_cts_nr_frames [int]
+
+ Controls the number of packets to allow between consecutive CTS frames
+ (default 255).
+ This number is communicated within the CTS frame from receiver to transmitter.
+ Setting this has effect on received transport sessions only.
+
+7.3 /proc/sys/net/can-j1939/transport_tx_retry_ms [int]
+
+ Controls how many time to wait before retrying to send an individual TP
+ flow or data packet after transmission failure (default 20).
+
+
+8. Credits
+--------------------------------
+
+ Kurt Van Dijck (j1939 core, transport protocol, API)
+ Pieter Beyens (j1939 core, address claiming)
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 75760e7..a7d7c17 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1631,6 +1631,14 @@ F: include/linux/can/error.h
F: include/linux/can/netlink.h
F: include/linux/can/platform/
+CAN-J1939 NETWORK LAYER
+M: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+L: socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org
+L: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S: Maintained
+F: net/can/j1939/
+F: include/linux/can/j1939.h
+
CELL BROADBAND ENGINE ARCHITECTURE
M: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
L: linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
^ permalink raw reply related
* [RFC v3 6/6] iproute2: add CAN and J1939 rtnetlink support
From: Kurt Van Dijck @ 2011-03-14 14:05 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>
Add iproute2 support for CAN and J1939 rtnetlink.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/Makefile b/Makefile
index c03d74c..efd06de 100644
--- a/Makefile
+++ b/Makefile
@@ -27,9 +27,12 @@ ADDLIB+=dnet_ntop.o dnet_pton.o
#options for ipx
ADDLIB+=ipx_ntop.o ipx_pton.o
+#options for j1939
+ADDLIB+=j1939.o
+
CC = gcc
HOSTCC = gcc
-CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall
+CCOPTS = -D_GNU_SOURCE -Wstrict-prototypes -Wall
CFLAGS = $(CCOPTS) -I../include $(DEFINES)
YACCFLAGS = -d -t -v
diff --git a/include/linux/can.h b/include/linux/can.h
new file mode 100644
index 0000000..9c2523c
--- /dev/null
+++ b/include/linux/can.h
@@ -0,0 +1,129 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>
+ * Urs Thuermann <urs.thuermann-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_J1939 7 /* SAE J1939 */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* J1939 address information */
+ struct {
+ /* 8 byte name when using dynamic addressing */
+ __u64 name;
+ /*
+ * pgn:
+ * 8bit: PS in PDU2 case, else 0
+ * 8bit: PF
+ * 1bit: DP
+ * 1bit: reserved
+ */
+ __u32 pgn;
+
+ /* 1byte address */
+ __u8 addr;
+ } j1939;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
diff --git a/include/linux/can/j1939.h b/include/linux/can/j1939.h
new file mode 100644
index 0000000..fa62562
--- /dev/null
+++ b/include/linux/can/j1939.h
@@ -0,0 +1,93 @@
+/*
+ * j1939.h
+ *
+ */
+
+#ifndef _J1939_H_
+#define _J1939_H_
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/can.h>
+
+#define J1939_NO_ADDR 0xff
+#define J1939_NO_NAME 0
+#define J1939_NO_PGN 0x7ffff
+/*
+ * J1939 Parameter Group Number
+ *
+ * bit 0-7 : PDU Specific (PS)
+ * bit 8-15 : PDU Format (PF)
+ * bit 16 : Data Page (DP)
+ * bit 17 : Reserved (R)
+ * bit 19-31 : set to zero
+ */
+typedef __u32 pgn_t;
+
+/*
+ * J1939 Priority
+ *
+ * bit 0-2 : Priority (P)
+ * bit 3-7 : set to zero
+ */
+typedef __u8 priority_t;
+
+/*
+ * J1939 NAME
+ *
+ * bit 0-20 : Identity Number
+ * bit 21-31 : Manufacturer Code
+ * bit 32-34 : ECU Instance
+ * bit 35-39 : Function Instance
+ * bit 40-47 : Function
+ * bit 48 : Reserved
+ * bit 49-55 : Vehicle System
+ * bit 56-59 : Vehicle System Instance
+ * bit 60-62 : Industry Group
+ * bit 63 : Arbitrary Address Capable
+ */
+typedef __u64 name_t;
+
+/*
+ * J1939 socket options
+ */
+#define SOL_CAN_J1939 (SOL_CAN_BASE + CAN_J1939)
+enum {
+ SO_J1939_FILTER = 1, /* set filters */
+ SO_J1939_PROMISC = 2, /* set/clr promiscuous mode */
+ SO_J1939_RECV_OWN = 3,
+ SO_J1939_RECV_DEST = 4, /* set/clr attach dest control message */
+ SO_J1939_RECV_PRIO = 5,
+ SO_J1939_SEND_PRIO = 6,
+ SO_J1939_DEST_MASK = 7, /* mask names in connect() & sendto() */
+};
+
+#define SCM_J1939_DEST SO_J1939_RECV_DEST
+#define SCM_J1939_PRIO SO_J1939_RECV_PRIO
+
+struct j1939_filter {
+ name_t name;
+ name_t name_mask;
+ __u8 addr;
+ __u8 addr_mask;
+ pgn_t pgn;
+ pgn_t pgn_mask;
+};
+
+/*
+ * RTNETLINK
+ */
+enum {
+ IFLA_J1939_UNSPEC,
+ IFLA_J1939_ENABLE,
+ IFLA_J1939_MAX,
+};
+
+enum {
+ IFA_J1939_UNSPEC,
+ IFA_J1939_ADDR,
+ IFA_J1939_NAME,
+ IFA_J1939_MAX,
+};
+
+#endif /* _J1939_H_ */
diff --git a/include/utils.h b/include/utils.h
index 3da6998..b6344d5 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -95,8 +95,15 @@ extern __u8* hexstring_a2n(const char *str, __u8 *buf, int blen);
extern const char *format_host(int af, int len, const void *addr,
char *buf, int buflen);
-extern const char *rt_addr_n2a(int af, int len, const void *addr,
+/* 'address with protocol' n2a */
+extern const char *rt_addrpr_n2a(int af, int protocol, int len, const void *addr,
char *buf, int buflen);
+static inline const char *rt_addr_n2a(int af, int len, const void *addr,
+ char *buf, int buflen)
+{
+ return rt_addrpr_n2a(af, 0, len, addr, buf, buflen);
+
+}
void missarg(const char *) __attribute__((noreturn));
void invarg(const char *, const char *) __attribute__((noreturn));
@@ -111,6 +118,16 @@ int dnet_pton(int af, const char *src, void *addr);
const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
int ipx_pton(int af, const char *src, void *addr);
+/* j1939 */
+extern const char *j1939_ntop(int af, const void *addr, size_t vlen,
+ char *str, size_t len);
+extern const char *j1939_link_attrtop(struct rtattr *nla);
+
+extern int j1939_addr_args(int argc, char *argv[],
+ struct nlmsghdr *msg, int msg_size);
+extern int j1939_link_args(int argc, char *argv[],
+ struct nlmsghdr *msg, int msg_size);
+
extern int __iproute2_hz_internal;
extern int __get_hz(void);
diff --git a/ip/ip.c b/ip/ip.c
index b127d57..50fcb1c 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -46,7 +46,7 @@ static void usage(void)
"where OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n"
" tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm }\n"
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
-" -f[amily] { inet | inet6 | ipx | dnet | link } |\n"
+" -f[amily] { inet | inet6 | ipx | dnet | link | can} |\n"
" -l[oops] { maximum-addr-flush-attempts } |\n"
" -o[neline] | -t[imestamp] | -b[atch] [filename] |\n"
" -rc[vbuf] [size]}\n");
@@ -181,6 +181,8 @@ int main(int argc, char **argv)
preferred_family = AF_PACKET;
else if (strcmp(argv[1], "ipx") == 0)
preferred_family = AF_IPX;
+ else if (strcmp(argv[1], "can") == 0)
+ preferred_family = AF_CAN;
else if (strcmp(argv[1], "help") == 0)
usage();
else
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index a775ecd..09db9fd 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -27,6 +27,7 @@
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/sockios.h>
+#include <linux/can.h>
#include "rt_names.h"
#include "utils.h"
@@ -68,6 +69,8 @@ static void usage(void)
fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n");
fprintf(stderr, " [ label STRING ] [ scope SCOPE-ID ]\n");
+ fprintf(stderr, " | j1939 J1939IFADDR\n");
+ fprintf(stderr, " \n");
fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n");
fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
fprintf(stderr, "FLAG := [ permanent | dynamic | secondary | primary |\n");
@@ -77,6 +80,10 @@ static void usage(void)
fprintf(stderr, "CONFFLAG := [ home | nodad ]\n");
fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
fprintf(stderr, "LFT := forever | SECONDS\n");
+ fprintf(stderr, " \n");
+ fprintf(stderr, "J1939IFADDR := [SA] [ name NODENAME ]\n");
+ fprintf(stderr, "SA := U8\n");
+ fprintf(stderr, "NODENAME := U64\n");
exit(-1);
}
@@ -421,6 +428,19 @@ int print_linkinfo(const struct sockaddr_nl *who,
}
fprintf(fp, "\n");
+
+ if (do_link && tb[IFLA_AF_SPEC]) {
+ struct rtattr *af[AF_MAX];
+
+ parse_rtattr_nested(af, AF_MAX, tb[IFLA_AF_SPEC]);
+ if (af[AF_CAN]) {
+ struct rtattr *prot[CAN_NPROTO];
+
+ parse_rtattr_nested(prot, CAN_NPROTO, af[AF_CAN]);
+ if (prot[CAN_J1939])
+ fprintf(fp, " %s\n", j1939_link_attrtop(prot[CAN_J1939]));
+ }
+ }
fflush(fp);
return 0;
}
@@ -445,6 +465,13 @@ static int set_lifetime(unsigned int *lifetime, char *argv)
return 0;
}
+static const int af_use_prefix[AF_MAX] = {
+ [AF_INET] = 1,
+ [AF_INET6] = 1,
+ [AF_DECnet] = 1,
+ [AF_IPX] = 1,
+};
+
int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
void *arg)
{
@@ -452,6 +479,7 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
int deprecated = 0;
+ int protocol = 0;
/* Use local copy of ifa_flags to not interfere with filtering code */
unsigned int ifa_flags;
struct rtattr * rta_tb[IFA_MAX+1];
@@ -471,10 +499,12 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
- if (!rta_tb[IFA_LOCAL])
- rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
- if (!rta_tb[IFA_ADDRESS])
- rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+ if (af_use_prefix[ifa->ifa_family]) {
+ if (!rta_tb[IFA_LOCAL])
+ rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+ if (!rta_tb[IFA_ADDRESS])
+ rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+ }
if (filter.ifindex && filter.ifindex != ifa->ifa_index)
return 0;
@@ -536,38 +566,64 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
fprintf(fp, " dnet ");
else if (ifa->ifa_family == AF_IPX)
fprintf(fp, " ipx ");
+ else if (ifa->ifa_family == AF_CAN) {
+ /* ifa->ifa_prefixlen is abused for protocol number */
+ const char *sprotocol;
+ char num[16];
+
+ /* 1st: set protocol, as this is rather tricky */
+ protocol = ifa->ifa_prefixlen;
+
+ /* 2nd: set label */
+ switch (protocol) {
+ case CAN_J1939:
+ sprotocol = "j1939";
+ break;
+ default:
+ sprintf(num, "%i", ifa->ifa_prefixlen);
+ sprotocol = num;
+ break;
+ }
+ fprintf(fp, " can-%s ", sprotocol);
+ }
else
fprintf(fp, " family %d ", ifa->ifa_family);
if (rta_tb[IFA_LOCAL]) {
- fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
+ fprintf(fp, "%s", rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
RTA_DATA(rta_tb[IFA_LOCAL]),
abuf, sizeof(abuf)));
if (rta_tb[IFA_ADDRESS] == NULL ||
memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
- fprintf(fp, "/%d ", ifa->ifa_prefixlen);
} else {
- fprintf(fp, " peer %s/%d ",
- rt_addr_n2a(ifa->ifa_family,
+ fprintf(fp, " peer %s",
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
RTA_DATA(rta_tb[IFA_ADDRESS]),
- abuf, sizeof(abuf)),
- ifa->ifa_prefixlen);
+ abuf, sizeof(abuf)));
}
+ if (af_use_prefix[ifa->ifa_family])
+ fprintf(fp, "/%d", ifa->ifa_prefixlen);
+ fprintf(fp, " ");
+ } else if (rta_tb[IFA_ADDRESS]) {
+ fprintf(fp, "peer %s ", rt_addrpr_n2a(ifa->ifa_family, protocol,
+ RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
+ RTA_DATA(rta_tb[IFA_ADDRESS]),
+ abuf, sizeof(abuf)));
}
if (rta_tb[IFA_BROADCAST]) {
fprintf(fp, "brd %s ",
- rt_addr_n2a(ifa->ifa_family,
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
RTA_DATA(rta_tb[IFA_BROADCAST]),
abuf, sizeof(abuf)));
}
if (rta_tb[IFA_ANYCAST]) {
fprintf(fp, "any %s ",
- rt_addr_n2a(ifa->ifa_family,
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
RTA_DATA(rta_tb[IFA_ANYCAST]),
abuf, sizeof(abuf)));
@@ -1091,12 +1147,18 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
req.ifa.ifa_flags |= IFA_F_HOMEADDRESS;
} else if (strcmp(*argv, "nodad") == 0) {
req.ifa.ifa_flags |= IFA_F_NODAD;
+ } else if (matches(*argv, "j1939") == 0) {
+ int ret;
+
+ ret = j1939_addr_args(argc, argv, &req.n, sizeof(req));
+ if (ret < 0)
+ return ret;
+ argc -= ret;
+ argv += ret;
} else {
if (strcmp(*argv, "local") == 0) {
NEXT_ARG();
}
- if (matches(*argv, "help") == 0)
- usage();
if (local_len)
duparg2("local", *argv);
lcl_arg = *argv;
@@ -1202,8 +1264,9 @@ int do_ipaddr(int argc, char **argv)
return ipaddr_list_or_flush(argc-1, argv+1, 0);
if (matches(*argv, "flush") == 0)
return ipaddr_list_or_flush(argc-1, argv+1, 1);
- if (matches(*argv, "help") == 0)
+ if (matches(*argv, "help") == 0) {
usage();
+ }
fprintf(stderr, "Command \"%s\" is unknown, try \"ip addr help\".\n", *argv);
exit(-1);
}
diff --git a/ip/iplink.c b/ip/iplink.c
index cb2c4f5..06fc34c 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -27,6 +27,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
+#include <linux/can.h>
#include "rt_names.h"
#include "utils.h"
@@ -71,6 +72,7 @@ void iplink_usage(void)
fprintf(stderr, " [ vf NUM [ mac LLADDR ]\n");
fprintf(stderr, " [ vlan VLANID [ qos VLAN-QOS ] ]\n");
fprintf(stderr, " [ rate TXRATE ] ] \n");
+ fprintf(stderr, " [ j1939 { on | off } ]\n");
fprintf(stderr, " ip link show [ DEVICE ]\n");
if (iplink_have_newlink()) {
@@ -383,6 +385,12 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
*argv, strlen(*argv));
argc--; argv++;
break;
+ } else if (matches(*argv, "j1939") == 0) {
+ ret = j1939_link_args(argc, argv, &req->n, sizeof(*req));
+ if (ret < 0)
+ return ret;
+ argc -= ret;
+ argv += ret;
} else {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
diff --git a/lib/j1939.c b/lib/j1939.c
new file mode 100644
index 0000000..157a8ce
--- /dev/null
+++ b/lib/j1939.c
@@ -0,0 +1,153 @@
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <endian.h>
+#include <linux/can/j1939.h>
+
+#include "utils.h"
+
+#ifndef htobe64
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define htobe64(x) __bswap_64 (x)
+# define htole64(x) (x)
+# define be64toh(x) __bswap_64 (x)
+# define le64toh(x) (x)
+# else
+# define htobe64(x) (x)
+# define htole64(x) __bswap_64 (x)
+# define be64toh(x) (x)
+# define le64toh(x) __bswap_64 (x)
+# endif
+#endif
+/*
+ * print J1939 name
+ * for use from rt_addr_n2a
+ */
+const char *j1939_ntop(int af, const void *vaddr, size_t vlen,
+ char *str, size_t len)
+{
+ struct rtattr *tb[IFA_J1939_MAX];
+ int strdone = 0;
+
+ /* cast vaddr to non-const pointer */
+ parse_rtattr(tb, IFA_J1939_MAX-1, (void *)vaddr, vlen);
+ if (tb[IFA_J1939_ADDR]) {
+ strdone += sprintf(&str[strdone], "0x%02x",
+ *(uint8_t *)RTA_DATA(tb[IFA_J1939_ADDR]));
+ if (tb[IFA_J1939_NAME])
+ str[strdone++] = ' ';
+ }
+ if (tb[IFA_J1939_NAME])
+ strdone += sprintf(&str[strdone], "name %016llx",
+ (unsigned long long)be64toh(*(uint64_t *)RTA_DATA(tb[IFA_J1939_NAME])));
+ errno = 0;
+ return str;
+}
+
+/*
+ * fill an ifaddr message from program arguments
+ */
+int j1939_addr_args(int argc, char *argv[], struct nlmsghdr *msg, int msg_size)
+{
+ int saved_argc = argc;
+ struct ifaddrmsg *ifa = (void *)&msg[1];
+ struct rtattr *local;
+
+ if (ifa->ifa_family == AF_UNSPEC)
+ ifa->ifa_family = AF_CAN;
+ else {
+ fprintf(stderr, "j1939 only allowed for AF_CAN\n");
+ return -1;
+ }
+ if (!ifa->ifa_prefixlen)
+ ifa->ifa_prefixlen = CAN_J1939;
+ else {
+ fprintf(stderr, "CAN protocol %i already specified",
+ ifa->ifa_prefixlen);
+ return -1;
+ }
+ NEXT_ARG();
+ /* j1939 SA & NAME never need to be specified together */
+ if (matches(*argv, "name") == 0) {
+ uint64_t name;
+
+ NEXT_ARG();
+ name = htobe64(strtoull(*argv, 0, 16));
+ if (!name) {
+ fprintf(stderr, "0 name is not valid\n");
+ return -1;
+ }
+ local = addattr_nest(msg, msg_size, IFA_LOCAL);
+ addattr_l(msg, msg_size, IFA_J1939_NAME, &name, sizeof(name));
+ addattr_nest_end(msg, local);
+ } else {
+ unsigned int laddr;
+ uint8_t addr;
+
+ addr = laddr = strtoul(*argv, 0, 0);
+ if (laddr >= 0xfe) {
+ fprintf(stderr, "address '%s' not valid\n", *argv);
+ return -1;
+ }
+ local = addattr_nest(msg, msg_size, IFA_LOCAL);
+ addattr_l(msg, msg_size, IFA_J1939_ADDR, &addr, sizeof(addr));
+ addattr_nest_end(msg, local);
+ }
+
+ return saved_argc - argc;
+}
+
+/*
+ * fill an link_af message from program arguments
+ */
+int j1939_link_args(int argc, char *argv[], struct nlmsghdr *msg, int msg_size)
+{
+ int saved_argc = argc;
+ struct rtattr *afspec, *can, *j1939;
+ uint8_t enable;
+
+ NEXT_ARG();
+ if (strcmp(*argv, "on") == 0) {
+ enable = 1;
+ } else if (strcmp(*argv, "off") == 0) {
+ enable = 0;
+ } else {
+ enable = 1;
+ /* revert arguments */
+ ++argc;
+ --argv;
+ }
+
+ afspec = addattr_nest(msg, msg_size, IFLA_AF_SPEC);
+ can = addattr_nest(msg, msg_size, AF_CAN);
+ j1939 = addattr_nest(msg, msg_size, CAN_J1939);
+ addattr_l(msg, msg_size, IFLA_J1939_ENABLE, &enable, sizeof(enable));
+ addattr_nest_end(msg, j1939);
+ addattr_nest_end(msg, can);
+ addattr_nest_end(msg, afspec);
+
+ return saved_argc - argc;
+}
+
+/*
+ * process the returned IFLA_AF_SPEC/AF_CAN/CAN_J1939 attribute
+ */
+const char *j1939_link_attrtop(struct rtattr *nla)
+{
+ static char str[32];
+ int pos;
+ struct rtattr *tb[IFLA_J1939_MAX];
+
+ pos = 0;
+ str[0] = 0;
+ parse_rtattr_nested(tb, IFLA_J1939_MAX-1, nla);
+ if (tb[IFLA_J1939_ENABLE]) {
+ uint8_t *u8ptr;
+
+ u8ptr = RTA_DATA(tb[IFLA_J1939_ENABLE]);
+ pos += sprintf(&str[pos], "j1939 %s", *u8ptr ? "on" : "off");
+ }
+ return str;
+}
+
diff --git a/lib/utils.c b/lib/utils.c
index a60d884..02dda80 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -25,6 +25,7 @@
#include <linux/pkt_sched.h>
#include <time.h>
#include <sys/time.h>
+#include <linux/can.h>
#include "utils.h"
@@ -513,7 +514,8 @@ int __get_user_hz(void)
return sysconf(_SC_CLK_TCK);
}
-const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen)
+const char *rt_addrpr_n2a(int af, int protocol, int len, const void *addr,
+ char *buf, int buflen)
{
switch (af) {
case AF_INET:
@@ -527,6 +529,11 @@ const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen
memcpy(dna.a_addr, addr, 2);
return dnet_ntop(af, &dna, buf, buflen);
}
+ case AF_CAN:
+ switch (protocol) {
+ case CAN_J1939:
+ return j1939_ntop(af, addr, len, buf, buflen);
+ }
default:
return "???";
}
diff --git a/man/man8/ip.8 b/man/man8/ip.8
index 8d55fa9..d17e06a 100644
--- a/man/man8/ip.8
+++ b/man/man8/ip.8
@@ -23,7 +23,7 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
\fB\-s\fR[\fItatistics\fR] |
\fB\-r\fR[\fIesolve\fR] |
\fB\-f\fR[\fIamily\fR] {
-.BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | "
+.BR inet " | " inet6 " | " ipx " | " dnet " | " link " | " can " } | "
\fB\-o\fR[\fIneline\fR] }
.ti -8
@@ -95,7 +95,9 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
.B qos
.IR VLAN-QOS " ] ] ["
.B rate
-.IR TXRATE " ]"
+.IR TXRATE " ] |"
+.br
+.BR j1939 " { " on " | " off " }"
.ti -8
.B ip link show
@@ -126,7 +128,9 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
.B label
.IR STRING " ] [ "
.B scope
-.IR SCOPE-ID " ]"
+.IR SCOPE-ID " ] | "
+.B j1939
+.IR IFADDRJ1939 " ] "
.ti -8
.IR SCOPE-ID " := "
@@ -142,6 +146,15 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
tentative " | " deprecated " | " dadfailed " | " temporary " ]"
.ti -8
+.IR J1939IFADDR " := [ " SA " ] [ "
+.B name
+.IR " NODENAME " ]
+.br
+.IR SA " := " U8
+.br
+.IR NODENAME " := " U64
+
+.ti -8
.BR "ip addrlabel" " { " add " | " del " } " prefix
.BR PREFIX " [ "
.B dev
@@ -1038,6 +1051,11 @@ Setting this parameter to 0 disables rate limiting. The
parameter must be specified.
.in -8
+.TP
+.BR "j1939 on " or "j1939 off"
+Enable or disable SAE J1939 on the device. This will only
+work when the device is a CAN device.
+
.PP
.B Warning:
If multiple parameter changes are requested,
^ permalink raw reply related
* Re: [RFC v3 3/6] can: make struct proto const
From: Eric Dumazet @ 2011-03-14 14:09 UTC (permalink / raw)
To: Kurt Van Dijck; +Cc: socketcan-core, netdev
In-Reply-To: <20110314134721.GD333@e-circ.dyndns.org>
Le lundi 14 mars 2011 à 14:47 +0100, Kurt Van Dijck a écrit :
> can_ioctl is the only reason for struct proto to be non-const.
> script/check-patch.pl suggests struct proto be const.
> This patch performs the necessary change.
>
> Signed-off-by: Kurt Van Dijck <kurt.van.dijck@eia.be>
> ---
> diff --git a/include/linux/can/core.h b/include/linux/can/core.h
> index 430c446..0767cc6 100644
> --- a/include/linux/can/core.h
> +++ b/include/linux/can/core.h
> @@ -39,7 +39,7 @@
> struct can_proto {
> int type;
> int protocol;
> - struct proto_ops *ops;
> + const struct proto_ops *ops;
> struct proto *prot;
>
> const struct rtnl_af_ops *rtnl_link_ops;
> @@ -78,6 +78,8 @@ struct rtgencanmsg {
>
> extern int can_proto_register(const struct can_proto *cp);
> extern void can_proto_unregister(const struct can_proto *cp);
> +extern int can_sock_ioctl(struct socket *sock, unsigned int cmd,
> + unsigned long arg);
>
> extern int can_rx_register(struct net_device *dev, canid_t can_id,
> canid_t mask,
> diff --git a/net/can/af_can.c b/net/can/af_can.c
> index db59c6e..c1f8c05 100644
> --- a/net/can/af_can.c
> +++ b/net/can/af_can.c
> @@ -139,7 +139,7 @@ static inline void can_put_proto(const struct can_proto *cp)
> * af_can socket functions
> */
>
> -static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> +int can_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> {
> struct sock *sk = sock->sk;
>
> @@ -152,6 +152,7 @@ static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> return -ENOIOCTLCMD;
> }
> }
> +EXPORT_SYMBOL(can_sock_ioctl);
>
> static void can_sock_destruct(struct sock *sk)
> {
> @@ -720,10 +721,6 @@ int can_proto_register(const struct can_proto *cp)
> err = -EBUSY;
> } else {
> proto_tab[proto] = cp;
> -
> - /* use generic ioctl function if not defined by module */
> - if (!cp->ops->ioctl)
> - cp->ops->ioctl = can_ioctl;
Hmm, you actually fixed a race / bug, since we installed in proto_tab[]
a pointer to a not yet initted ops structure.
I suggest you change patch title and changelog to properly describe the
bug fix.
^ permalink raw reply
* Re: [PATCH] pktgen: fix transmission headers with frags=0
From: Daniel Turull @ 2011-03-14 14:13 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev, Robert Olsson, Jens Laas, Voravit Tanyingyong
In-Reply-To: <1300110977.3423.16.camel@edumazet-laptop>
On 03/14/2011 02:56 PM, Eric Dumazet wrote:
> Le lundi 14 mars 2011 à 14:22 +0100, Daniel Turull a écrit :
>> The headers of pktgen were incorrectly added in a pktgen packet
>> without frags (frags=0). There was an offset in the pktgen headers.
>>
>> The cause was in reusing the pgh variable as a return variable in skb_put
>> when adding the payload to the skb.
>>
>> A rename of the variable is done.
>>
>> Signed-off-by: Daniel Turull <daniel.turull@gmail.com>
>> ---
>> The PKTGEN magic (be9b e955) now starts in the correct offset.
>> Before the patch, it was starting at the end of the packet (be9b)
>>
>
> not exactly, but offseted +16 bytes
> (sizeof(struct pktgen_hdr))
True, I shouldn't say end.
>
>> Capture from tcpdump:
>>
>> before patch:
>> 14:57:37.854812 IP 10.0.0.2.discard > 10.254.254.84.discard: UDP, length 18
>> 0x0000: 001b 2157 ed84 001b 215d 01d0 0800 4500
>> 0x0010: 002e 0004 0000 2011 8767 0a00 0002 0afe
>> 0x0020: fe54 0009 0009 001a 0000 0000 0000 b072
>> 0x0030: 9102 00ea ffff 0010 0000 be9b
>>
>> after patch:
>> 14:44:32.896048 IP 10.0.0.2.discard > 10.217.234.56.discard: UDP, length 18
>> 0x0000: 001b 2157 ed84 001b 215d 01d0 0800 4500
>> 0x0010: 002e 0000 0000 2011 9bac 0a00 0002 0ad9
>> 0x0020: ea38 0009 0009 001a 0000 be9b e955 0000
>> 0x0030: 0001 4d7e 1b09 0005 5f23 af00
>>
>> ---
>> diff --git a/net/core/pktgen.c b/net/core/pktgen.c
>> index f0aec6c..5baa9d9 100644
>> --- a/net/core/pktgen.c
>> +++ b/net/core/pktgen.c
>> @@ -2615,13 +2615,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
>> {
>> struct timeval timestamp;
>> struct pktgen_hdr *pgh;
>> + void *data;
>>
>> pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
>> datalen -= sizeof(*pgh);
>>
>> if (pkt_dev->nfrags <= 0) {
>> - pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
>> - memset(pgh + 1, 0, datalen);
>> + data = skb_put(skb, datalen);
>> + memset(data + 1, 0, datalen);
>> } else {
>> int frags = pkt_dev->nfrags;
>> int i, len;
>> --
>
> Good catch !
>
> Hmm this patch is not correct, why memset(data + 1, ...) ?
I kept the +1 as it was in the original code, but I supposed it can be avoided.
> Also, this patch is needed for net-next-2.6 only
> (bug introduced by commit 26ad787962ef84677a48c560
> (pktgen: speedup fragmented skbs)
>
> I would avoid the "void *data;" declaration and just use following :
>
> diff --git a/net/core/pktgen.c b/net/core/pktgen.c
> index f0aec6c..f727c83 100644
> --- a/net/core/pktgen.c
> +++ b/net/core/pktgen.c
> @@ -2620,7 +2620,7 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
> datalen -= sizeof(*pgh);
>
> if (pkt_dev->nfrags <= 0) {
> - pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
> + skb_put(skb, datalen);
> memset(pgh + 1, 0, datalen);
> } else {
> int frags = pkt_dev->nfrags;
>
>
>
> or even :
>
> memset(skb_put(skb, datalen), 0, datalen);
>
I think that the second is better and more compact.
Do I resend the new patch?
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC v3 1/6] can: extend sockaddr_can to include j1939 members
From: Eric Dumazet @ 2011-03-14 14:15 UTC (permalink / raw)
To: Kurt Van Dijck; +Cc: socketcan-core, netdev
In-Reply-To: <20110314132409.GB333@e-circ.dyndns.org>
Le lundi 14 mars 2011 à 14:24 +0100, Kurt Van Dijck a écrit :
> +/*
> + * required_size
> + * macro to find the minimum size of a struct
> + * that includes a requested member
> + */
> +#define required_size(member, struct_type) \
> + (offsetof(typeof(struct_type), member) + \
> + sizeof(((typeof(struct_type) *)(0))->member))
> +
Hmm, most similar macros reverse arguments.
(For example : offsetof())
What about
#define required_size(TYPE, MEMBER) ...
^ permalink raw reply
* Re: [PATCH] pktgen: fix transmission headers with frags=0
From: Eric Dumazet @ 2011-03-14 14:18 UTC (permalink / raw)
To: Daniel Turull; +Cc: netdev, Robert Olsson, Jens Laas, Voravit Tanyingyong
In-Reply-To: <4D7E22A5.1000708@gmail.com>
Le lundi 14 mars 2011 à 15:13 +0100, Daniel Turull a écrit :
> On 03/14/2011 02:56 PM, Eric Dumazet wrote:
> > Good catch !
> >
> > Hmm this patch is not correct, why memset(data + 1, ...) ?
>
> I kept the +1 as it was in the original code, but I supposed it can be avoided.
>
original code was assuming pgh was at right location.
pgh + 1 : really pointed to first byte after pktgen header.
data + 1 : points one byte off (so we leak one byte of kernel memory,
security guys would complain a lot...)
> I think that the second is better and more compact.
>
> Do I resend the new patch?
Yes please ;)
^ permalink raw reply
* [PATCH net-next-2.6 v2] ftmac100: use GFP_ATOMIC allocations where needed
From: Eric Dumazet @ 2011-03-14 14:26 UTC (permalink / raw)
To: Po-Yu Chuang, David Miller; +Cc: netdev, linux-kernel, Po-Yu Chuang
In-Reply-To: <AANLkTi=grwkfYpBFJEyQHXbFaTov5Tc+tAR2ebzw0Bht@mail.gmail.com>
When running in softirq context, we should use GFP_ATOMIC allocations
instead of GFP_KERNEL ones.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Tested-by: Po-Yu Chuang <ratbert@faraday-tech.com>
Acked-by: Po-Yu Chuang <ratbert@faraday-tech.com>
---
v2: fixed a typo
drivers/net/ftmac100.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c
index df70368..1d6f4b8 100644
--- a/drivers/net/ftmac100.c
+++ b/drivers/net/ftmac100.c
@@ -80,7 +80,8 @@ struct ftmac100 {
struct mii_if_info mii;
};
-static int ftmac100_alloc_rx_page(struct ftmac100 *priv, struct ftmac100_rxdes *rxdes);
+static int ftmac100_alloc_rx_page(struct ftmac100 *priv,
+ struct ftmac100_rxdes *rxdes, gfp_t gfp);
/******************************************************************************
* internal functions (hardware register access)
@@ -441,7 +442,7 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
skb->truesize += length;
__pskb_pull_tail(skb, min(length, 64));
- ftmac100_alloc_rx_page(priv, rxdes);
+ ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
ftmac100_rx_pointer_advance(priv);
@@ -659,13 +660,14 @@ static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
/******************************************************************************
* internal functions (buffer)
*****************************************************************************/
-static int ftmac100_alloc_rx_page(struct ftmac100 *priv, struct ftmac100_rxdes *rxdes)
+static int ftmac100_alloc_rx_page(struct ftmac100 *priv,
+ struct ftmac100_rxdes *rxdes, gfp_t gfp)
{
struct net_device *netdev = priv->netdev;
struct page *page;
dma_addr_t map;
- page = alloc_page(GFP_KERNEL);
+ page = alloc_page(gfp);
if (!page) {
if (net_ratelimit())
netdev_err(netdev, "failed to allocate rx page\n");
@@ -736,7 +738,7 @@ static int ftmac100_alloc_buffers(struct ftmac100 *priv)
for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
- if (ftmac100_alloc_rx_page(priv, rxdes))
+ if (ftmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
goto err;
}
^ permalink raw reply related
* [GIT/PATCH v5] xen network backend driver
From: Ian Campbell @ 2011-03-14 14:27 UTC (permalink / raw)
To: netdev@vger.kernel.org, xen-devel
Cc: Jeremy Fitzhardinge, Herbert Xu, Konrad Rzeszutek Wilk,
Francois Romieu, Ben Hutchings, Stephen Hemminger,
Michał Mirosław
The following patch is the fifth iteration of the Xen network backend
driver for upstream Linux.
This driver ("netback") is the host side counterpart to the frontend
driver in drivers/net/xen-netfront.c. The PV protocol is also
implemented by frontend drivers in other OSes too, such as the BSDs and
even Windows.
Changes since the fourth posting, due to review from Stephen Hemminger:
* Treat dev->features as u32 (it effectively is already and
becomes literally so in net-next -- there's no harm in netback
switching earlier)
* Use stats from struct netdevice instead of a local copy.
I also added switching to per-CPU/64 bit stats (Stephen again) and
netdev->hw_features (from Michał Mirosław) to my TODO list post merge.
The following changes since commit 2e820f58f7ad8eaca2f194ccdfea0de63e9c6d78:
Ian Campbell (1):
xen/irq: implement bind_interdomain_evtchn_to_irqhandler for backend drivers
are available in the git repository at:
git://xenbits.xen.org/people/ianc/linux-2.6.git upstream/dom0/backend/netback
Bastian Blank (1):
xen: netback: Fix null-pointer access in netback_uevent
Christophe Saout (1):
xen: netback: use dev_name() instead of removed ->bus_id.
Dongxiao Xu (5):
xen: netback: Move global/static variables into struct xen_netbk.
xen: netback: Introduce a new struct type page_ext.
xen: netback: Multiple tasklets support.
xen: netback: Use Kernel thread to replace the tasklet.
xen: netback: Set allocated memory to zero from vmalloc.
Ian Campbell (63):
xen: netback: Initial import of linux-2.6.18-xen.hg netback driver.
xen: netback: first cut at porting to upstream and cleaning up
xen: netback: add ethtool stat to track copied skbs.
xen: netback: make queue length parameter writeable in sysfs
xen: netback: parent sysfs device should be set before registering.
xen: rename netbk module xen-netback.
xen: netback: remove unused xen_network_done code
xen: netback: factor disconnect from backend into new function.
xen: netback: wait for hotplug scripts to complete before signalling connected to frontend
xen: netback: Always pull through PKT_PROT_LEN bytes into the linear part of an skb.
xen: netback: Allow setting of large MTU before rings have connected.
xen: netback: correctly setup skb->ip_summed on receive
xen: netback: handle NET_SKBUFF_DATA_USES_OFFSET correctly
xen: netback: drop frag member from struct netbk_rx_meta
xen: netback: linearise SKBs as we copy them into guest memory on guest-RX.
xen: netback: drop more relics of flipping mode
xen: netback: check if foreign pages are actually netback-created foreign pages.
xen: netback: do not unleash netback threads until initialisation is complete
xen: netback: save interrupt state in add_to_net_schedule_list_tail
xen: netback: increase size of rx_meta array.
xen: netback: take net_schedule_list_lock when removing entry from net_schedule_list
xen: netback: Drop GSO SKBs which do not have csum_blank.
xen: netback: completely remove tx_queue_timer
Revert "xen: netback: Drop GSO SKBs which do not have csum_blank."
xen: netback: handle incoming GSO SKBs which are not CHECKSUM_PARTIAL
xen: netback: rationalise types used in count_skb_slots
xen: netback: refactor logic for moving to a new receive buffer.
xen: netback: refactor code to get next rx buffer into own function.
xen: netback: simplify use of netbk_add_frag_responses
xen: netback: cleanup coding style
xen: netback: drop private ?PRINTK macros in favour of pr_*
xen: netback: move under drivers/net/xen-netback/
xen: netback: remove queue_length module option
xen: netback: correct error return from ethtool hooks.
xen: netback: avoid leading _ in function parameter names.
xen: netback: drop unused debug interrupt handler.
xen: netif: properly namespace the Xen netif protocol header.
xen: netif: improve Kconfig help text for front- and backend drivers.
xen: netback: drop ethtool drvinfo callback
xen: netback: use xen_netbk prefix where appropriate
xen: netback: refactor to make all xen_netbk knowledge internal to netback.c
xen: netback: use xenvif_ prefix where appropriate
xen: netback: add reference from xenvif to xen_netbk
xen: netback: refactor to separate network device from worker pools
xen: netback: switch to kthread mode and drop tasklet mode
xen: netback: handle frames whose head crosses a page boundary
xen: netback: return correct values from start_xmit
xen: netback: remove useless memset to zero.
xen: netback: use register_netdev()
xen: netback: simplify unwinding netback_init's work on failure.
xen: netback: use core network carrier flag.
xen: netback: s/xenvif_queue_full/xenvif_rx_queue_full/
xen: netback: add xenvif_rx_schedulable
xen: netback: further separate xen_netbk and xenvif
xen: netback: use netdev_LEVEL instead of pr_LEVEL
xen: netback: drop rx_notify and notify_list array in favour of a normal list
xen: netback: Make dependency on PageForeign conditional
xen: netback: completely drop foreign page support
xen: netback: ethtool stats fields should be unsigned long
xen: netback: do not update last_rx on receive.
xen: netback: Allow headroom for VLAN header in SG MTU calculation.
xen: netback: use stats from struct net_device
xen: netback: netdevice features should be u32
James Harper (1):
xen: netback: avoid null-pointer access in netback_uevent
Jan Beulich (1):
xen: netback: unmap tx ring gref when mapping of rx ring gref failed
Jeremy Fitzhardinge (21):
xen: netback: don't include xen/evtchn.h
xen: netback: use mod_timer
xen: netback: use NET_SKB_PAD rather than "16"
xen: netback: completely drop flip support
xen: netback: demacro MASK_PEND_IDX
xen: netback: convert PEND_RING_IDX into a proper typedef name
xen: netback: rename NR_PENDING_REQS to nr_pending_reqs()
xen: netback: pre-initialize list and spinlocks; use empty list to indicate not on list
xen: netback: remove CONFIG_XEN_NETDEV_PIPELINED_TRANSMITTER
xen: netback: make netif_get/put inlines
xen: netback: move code around
xen: netback: document PKT_PROT_LEN
xen: netback: convert to net_device_ops
xen: netback: reinstate missing code
xen: netback: remove debug noise
xen: netback: don't screw around with packet gso state
xen: netback: use dev_get/set_drvdata() inteface
xen: netback: include linux/sched.h for TASK_* definitions
xen: netback: use get_sset_count rather than obsolete get_stats_count
xen: netback: minor code formatting fixup
xen: netback: only initialize for PV domains
Keir Fraser (1):
xen: netback: Fixes for delayed copy of tx network packets.
Konrad Rzeszutek Wilk (1):
Fix compile warnings: ignoring return value of 'xenbus_register_backend' ..
Paul Durrant (8):
xen: netback: Fix basic indentation issue
xen: netback: Add a new style of passing GSO packets to frontends.
xen: netback: Make frontend features distinct from netback feature flags.
xen: netback: Re-define PKT_PROT_LEN to be bigger.
xen: netback: Don't count packets we don't actually receive.
xen: netback: Remove the 500ms timeout to restart the netif queue.
xen: netback: Add a missing test to tx_work_todo.
xen: netback: Re-factor net_tx_action_dealloc() slightly.
Steven Smith (2):
xen: netback: make sure that pg->mapping is never NULL for a page mapped from a foreign domain.
xen: netback: try to pull a minimum of 72 bytes into the skb data area
drivers/net/Kconfig | 38 +-
drivers/net/Makefile | 1 +
drivers/net/xen-netback/Makefile | 3 +
drivers/net/xen-netback/common.h | 161 ++++
drivers/net/xen-netback/interface.c | 424 +++++++++
drivers/net/xen-netback/netback.c | 1745 +++++++++++++++++++++++++++++++++++
drivers/net/xen-netback/xenbus.c | 490 ++++++++++
drivers/net/xen-netfront.c | 20 +-
include/xen/interface/io/netif.h | 80 +-
9 files changed, 2908 insertions(+), 54 deletions(-)
create mode 100644 drivers/net/xen-netback/Makefile
create mode 100644 drivers/net/xen-netback/common.h
create mode 100644 drivers/net/xen-netback/interface.c
create mode 100644 drivers/net/xen-netback/netback.c
create mode 100644 drivers/net/xen-netback/xenbus.c
Changes made in the fourth posting since the third posting, due to
review from Ben Hutchings:
* Correct type of ethtool stats variable
* Correct maximum MTU in SG mode
* Don't update last_rx.
Changes since the second posting, mostly due to Francois Romieu and
Konrad Rzeszutek Wilk's review, include:
* Rebased ontop of 2.6.38-rc2 (my branch with the generic xenbus
backend support got rebased somewhere on its journey into
mainline 2.6.38-rc1 and so this rebase was needed to resolve
that). The rebase also included moving the patches on top of the
last precursor patch (addition of
bind_interdomain_evtchn_to_irqhandler, which is in linux-next).
* Dropped receive notify arrays in favour of simple lists.
* Dropped private carrier flag. It's not clear that the reasons
for adding this, which may well have been valid in 2.6.18, are
still valid today. We can revisit in the future if and when
required.
* A slight refactoring to more completely encapsulate driver stuff
in interface.c and backend worker pool stuff in netback.c
* Use netdev_XXX instead of pr_XXX where appropriate
* Various coding style etc related tweaks suggested during review.
Changes since the first posting, many due to Ben Hutching's review,
include:
* Improved Kconfig description for XEN_NETDEV_BACKEND and
XEN_NETDEV_FRONTEND.
* Avoid the core networking namespaces (skb_*, netif_*, net_*).
This led to a major refactoring since the current namespace use
was something of a mess. Now the code tries to consistently use
xenvif* for the device driver related stuff (interface.c) and
xen_netbk* for the backend worker pool related stuff
(netback.c). This cleanup extended to the
xen/interface/io/netif.h header which required changes to
netfront too.
* Dropped the tasklet mode for the backend worker leaving only the
kthread mode. I will revisit the suggestion to use NAPI on the
driver side in the future, I think it's somewhat orthogonal to
the use of kthread here, but it seems likely to be a worthwhile
improvement either way.
* Dropped netbk_copy_skb. Ben requested this function be made
generic and moved to the networking core but it turns out it was
trivial to remove netback's reliance on this functionality, and
avoid a bunch of unnecessary copying in the process. The
function's semantics were a bit odd in any case so I couldn't
imagine many other users.
* Handle incoming GSO SKBs which are not CHECKSUM_PARTIAL
correctly. Changed from previous behaviour (dropping the skb) to
doing a fixup after discussion of equivalent frontend patch
which became e0ce4af920eb028f38bfd680b1d733f4c7a0b7cf.
* Other improvements suggested by Ben (e.g. dropping pointless
filename references from top of file comments, not including
version.h, correct return values from ethtool hooks, dropped
queue_length module parameter, dropped unused debug interrupt,
etc)
Changes made for the initial upstream post of the driver vs. the out of
tree xen.git pvops version include:
* The driver has been put through the checkpatch.pl wringer plus
several manual cleanup passes.
* Moved from drivers/xen/netback to drivers/net/xen-netback.
* Most significantly the guest transmit path (i.e. what looks like
receive to netback) has been significantly reworked to remove
the dependency on the out of tree PageForeign page flag (a core
kernel patch which enables a per page destructor callback on the
final put_page). This page flag is needed in order to implement
a grant map based transmit path (where guest pages are mapped
directly into SKB frags). Instead this version of netback uses
grant copy operations into regular memory belonging to the
backend domain. Reinstating the grant map functionality is
something which I would like to revisit in the future.
Ian.
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 0382332..1826d5d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2966,12 +2966,38 @@ config XEN_NETDEV_FRONTEND
select XEN_XENBUS_FRONTEND
default y
help
- The network device frontend driver allows the kernel to
- access network devices exported exported by a virtual
- machine containing a physical network device driver. The
- frontend driver is intended for unprivileged guest domains;
- if you are compiling a kernel for a Xen guest, you almost
- certainly want to enable this.
+ This driver provides support for Xen paravirtual network
+ devices exported by a Xen network driver domain (often
+ domain 0).
+
+ The corresponding Linux backend driver is enabled by the
+ CONFIG_XEN_NETDEV_BACKEND option.
+
+ If you are compiling a kernel for use as Xen guest, you
+ should say Y here. To compile this driver as a module, chose
+ M here: the module will be called xen-netfront.
+
+config XEN_NETDEV_BACKEND
+ tristate "Xen backend network device"
+ depends on XEN_BACKEND
+ help
+ This driver allows the kernel to act as a Xen network driver
+ domain which exports paravirtual network devices to other
+ Xen domains. These devices can be accessed by any operating
+ system that implements a compatible front end.
+
+ The corresponding Linux frontend driver is enabled by the
+ CONFIG_XEN_NETDEV_FRONTEND configuration option.
+
+ The backend driver presents a standard network device
+ endpoint for each paravirtual network device to the driver
+ domain network stack. These can then be bridged or routed
+ etc in order to provide full network connectivity.
+
+ If you are compiling a kernel to run in a Xen network driver
+ domain (often this is domain 0) you should say Y here. To
+ compile this driver as a module, chose M here: the module
+ will be called xen-netback.
config ISERIES_VETH
tristate "iSeries Virtual Ethernet driver support"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90738d..145dfd7 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -171,6 +171,7 @@ obj-$(CONFIG_SLIP) += slip.o
obj-$(CONFIG_SLHC) += slhc.o
obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o
+obj-$(CONFIG_XEN_NETDEV_BACKEND) += xen-netback/
obj-$(CONFIG_DUMMY) += dummy.o
obj-$(CONFIG_IFB) += ifb.o
diff --git a/drivers/net/xen-netback/Makefile b/drivers/net/xen-netback/Makefile
new file mode 100644
index 0000000..e346e81
--- /dev/null
+++ b/drivers/net/xen-netback/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_XEN_NETDEV_BACKEND) := xen-netback.o
+
+xen-netback-y := netback.o xenbus.o interface.o
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
new file mode 100644
index 0000000..5d7bbf2
--- /dev/null
+++ b/drivers/net/xen-netback/common.h
@@ -0,0 +1,161 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __XEN_NETBACK__COMMON_H__
+#define __XEN_NETBACK__COMMON_H__
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/io.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include <xen/interface/io/netif.h>
+#include <xen/interface/grant_table.h>
+#include <xen/grant_table.h>
+#include <xen/xenbus.h>
+
+struct xen_netbk;
+
+struct xenvif {
+ /* Unique identifier for this interface. */
+ domid_t domid;
+ unsigned int handle;
+
+ /* Reference to netback processing backend. */
+ struct xen_netbk *netbk;
+
+ u8 fe_dev_addr[6];
+
+ /* Physical parameters of the comms window. */
+ grant_handle_t tx_shmem_handle;
+ grant_ref_t tx_shmem_ref;
+ grant_handle_t rx_shmem_handle;
+ grant_ref_t rx_shmem_ref;
+ unsigned int irq;
+
+ /* List of frontends to notify after a batch of frames sent. */
+ struct list_head notify_list;
+
+ /* The shared rings and indexes. */
+ struct xen_netif_tx_back_ring tx;
+ struct xen_netif_rx_back_ring rx;
+ struct vm_struct *tx_comms_area;
+ struct vm_struct *rx_comms_area;
+
+ /* Flags that must not be set in dev->features */
+ u32 features_disabled;
+
+ /* Frontend feature information. */
+ u8 can_sg:1;
+ u8 gso:1;
+ u8 gso_prefix:1;
+ u8 csum:1;
+
+ /* Internal feature information. */
+ u8 can_queue:1; /* can queue packets for receiver? */
+
+ /*
+ * Allow xenvif_start_xmit() to peek ahead in the rx request
+ * ring. This is a prediction of what rx_req_cons will be
+ * once all queued skbs are put on the ring.
+ */
+ RING_IDX rx_req_cons_peek;
+
+ /* Transmit shaping: allow 'credit_bytes' every 'credit_usec'. */
+ unsigned long credit_bytes;
+ unsigned long credit_usec;
+ unsigned long remaining_credit;
+ struct timer_list credit_timeout;
+
+ /* Statistics */
+ unsigned long rx_gso_checksum_fixup;
+
+ /* Miscellaneous private stuff. */
+ struct list_head schedule_list;
+ atomic_t refcnt;
+ struct net_device *dev;
+
+ wait_queue_head_t waiting_to_free;
+};
+
+#define XEN_NETIF_TX_RING_SIZE __RING_SIZE((struct xen_netif_tx_sring *)0, PAGE_SIZE)
+#define XEN_NETIF_RX_RING_SIZE __RING_SIZE((struct xen_netif_rx_sring *)0, PAGE_SIZE)
+
+struct xenvif *xenvif_alloc(struct device *parent,
+ domid_t domid,
+ unsigned int handle);
+
+int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
+ unsigned long rx_ring_ref, unsigned int evtchn);
+void xenvif_disconnect(struct xenvif *vif);
+
+void xenvif_get(struct xenvif *vif);
+void xenvif_put(struct xenvif *vif);
+
+int xenvif_xenbus_init(void);
+
+int xenvif_schedulable(struct xenvif *vif);
+
+int xen_netbk_rx_ring_full(struct xenvif *vif);
+
+int xen_netbk_must_stop_queue(struct xenvif *vif);
+
+/* (Un)Map communication rings. */
+void xen_netbk_unmap_frontend_rings(struct xenvif *vif);
+int xen_netbk_map_frontend_rings(struct xenvif *vif,
+ grant_ref_t tx_ring_ref,
+ grant_ref_t rx_ring_ref);
+
+/* (De)Register a xenvif with the netback backend. */
+void xen_netbk_add_xenvif(struct xenvif *vif);
+void xen_netbk_remove_xenvif(struct xenvif *vif);
+
+/* (De)Schedule backend processing for a xenvif */
+void xen_netbk_schedule_xenvif(struct xenvif *vif);
+void xen_netbk_deschedule_xenvif(struct xenvif *vif);
+
+/* Check for SKBs from frontend and schedule backend processing */
+void xen_netbk_check_rx_xenvif(struct xenvif *vif);
+/* Receive an SKB from the frontend */
+void xenvif_receive_skb(struct xenvif *vif, struct sk_buff *skb);
+
+/* Queue an SKB for transmission to the frontend */
+void xen_netbk_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb);
+/* Notify xenvif that ring now has space to send an skb to the frontend */
+void xenvif_notify_tx_completion(struct xenvif *vif);
+
+/* Returns number of ring slots required to send an skb to the frontend */
+unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb);
+
+#endif /* __XEN_NETBACK__COMMON_H__ */
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
new file mode 100644
index 0000000..160f831
--- /dev/null
+++ b/drivers/net/xen-netback/interface.c
@@ -0,0 +1,424 @@
+/*
+ * Network-device interface management.
+ *
+ * Copyright (c) 2004-2005, Keir Fraser
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "common.h"
+
+#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_vlan.h>
+
+#include <xen/events.h>
+#include <asm/xen/hypercall.h>
+
+#define XENVIF_QUEUE_LENGTH 32
+
+void xenvif_get(struct xenvif *vif)
+{
+ atomic_inc(&vif->refcnt);
+}
+
+void xenvif_put(struct xenvif *vif)
+{
+ if (atomic_dec_and_test(&vif->refcnt))
+ wake_up(&vif->waiting_to_free);
+}
+
+int xenvif_schedulable(struct xenvif *vif)
+{
+ return netif_running(vif->dev) && netif_carrier_ok(vif->dev);
+}
+
+static int xenvif_rx_schedulable(struct xenvif *vif)
+{
+ return xenvif_schedulable(vif) && !xen_netbk_rx_ring_full(vif);
+}
+
+static irqreturn_t xenvif_interrupt(int irq, void *dev_id)
+{
+ struct xenvif *vif = dev_id;
+
+ if (vif->netbk == NULL)
+ return IRQ_NONE;
+
+ xen_netbk_schedule_xenvif(vif);
+
+ if (xenvif_rx_schedulable(vif))
+ netif_wake_queue(vif->dev);
+
+ return IRQ_HANDLED;
+}
+
+static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct xenvif *vif = netdev_priv(dev);
+
+ BUG_ON(skb->dev != dev);
+
+ if (vif->netbk == NULL)
+ goto drop;
+
+ /* Drop the packet if the target domain has no receive buffers. */
+ if (!xenvif_rx_schedulable(vif))
+ goto drop;
+
+ /* Reserve ring slots for the worst-case number of fragments. */
+ vif->rx_req_cons_peek += xen_netbk_count_skb_slots(vif, skb);
+ xenvif_get(vif);
+
+ if (vif->can_queue && xen_netbk_must_stop_queue(vif))
+ netif_stop_queue(dev);
+
+ xen_netbk_queue_tx_skb(vif, skb);
+
+ return NETDEV_TX_OK;
+
+ drop:
+ vif->dev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+void xenvif_receive_skb(struct xenvif *vif, struct sk_buff *skb)
+{
+ netif_rx_ni(skb);
+}
+
+void xenvif_notify_tx_completion(struct xenvif *vif)
+{
+ if (netif_queue_stopped(vif->dev) && xenvif_rx_schedulable(vif))
+ netif_wake_queue(vif->dev);
+}
+
+static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
+{
+ struct xenvif *vif = netdev_priv(dev);
+ return &vif->dev->stats;
+}
+
+static void xenvif_up(struct xenvif *vif)
+{
+ xen_netbk_add_xenvif(vif);
+ enable_irq(vif->irq);
+ xen_netbk_check_rx_xenvif(vif);
+}
+
+static void xenvif_down(struct xenvif *vif)
+{
+ disable_irq(vif->irq);
+ xen_netbk_deschedule_xenvif(vif);
+ xen_netbk_remove_xenvif(vif);
+}
+
+static int xenvif_open(struct net_device *dev)
+{
+ struct xenvif *vif = netdev_priv(dev);
+ if (netif_carrier_ok(dev))
+ xenvif_up(vif);
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int xenvif_close(struct net_device *dev)
+{
+ struct xenvif *vif = netdev_priv(dev);
+ if (netif_carrier_ok(dev))
+ xenvif_down(vif);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static int xenvif_change_mtu(struct net_device *dev, int mtu)
+{
+ struct xenvif *vif = netdev_priv(dev);
+ int max = vif->can_sg ? 65535 - VLAN_ETH_HLEN : ETH_DATA_LEN;
+
+ if (mtu > max)
+ return -EINVAL;
+ dev->mtu = mtu;
+ return 0;
+}
+
+static void xenvif_set_features(struct xenvif *vif)
+{
+ struct net_device *dev = vif->dev;
+ u32 features = dev->features;
+
+ if (vif->can_sg)
+ features |= NETIF_F_SG;
+ if (vif->gso || vif->gso_prefix)
+ features |= NETIF_F_TSO;
+ if (vif->csum)
+ features |= NETIF_F_IP_CSUM;
+
+ features &= ~(vif->features_disabled);
+
+ if (!(features & NETIF_F_SG) && dev->mtu > ETH_DATA_LEN)
+ dev->mtu = ETH_DATA_LEN;
+
+ dev->features = features;
+}
+
+static int xenvif_set_tx_csum(struct net_device *dev, u32 data)
+{
+ struct xenvif *vif = netdev_priv(dev);
+ if (data) {
+ if (!vif->csum)
+ return -EOPNOTSUPP;
+ vif->features_disabled &= ~NETIF_F_IP_CSUM;
+ } else {
+ vif->features_disabled |= NETIF_F_IP_CSUM;
+ }
+
+ xenvif_set_features(vif);
+ return 0;
+}
+
+static int xenvif_set_sg(struct net_device *dev, u32 data)
+{
+ struct xenvif *vif = netdev_priv(dev);
+ if (data) {
+ if (!vif->can_sg)
+ return -EOPNOTSUPP;
+ vif->features_disabled &= ~NETIF_F_SG;
+ } else {
+ vif->features_disabled |= NETIF_F_SG;
+ }
+
+ xenvif_set_features(vif);
+ return 0;
+}
+
+static int xenvif_set_tso(struct net_device *dev, u32 data)
+{
+ struct xenvif *vif = netdev_priv(dev);
+ if (data) {
+ if (!vif->gso && !vif->gso_prefix)
+ return -EOPNOTSUPP;
+ vif->features_disabled &= ~NETIF_F_TSO;
+ } else {
+ vif->features_disabled |= NETIF_F_TSO;
+ }
+
+ xenvif_set_features(vif);
+ return 0;
+}
+
+static const struct xenvif_stat {
+ char name[ETH_GSTRING_LEN];
+ u16 offset;
+} xenvif_stats[] = {
+ {
+ "rx_gso_checksum_fixup",
+ offsetof(struct xenvif, rx_gso_checksum_fixup)
+ },
+};
+
+static int xenvif_get_sset_count(struct net_device *dev, int string_set)
+{
+ switch (string_set) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(xenvif_stats);
+ default:
+ return -EINVAL;
+ }
+}
+
+static void xenvif_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 * data)
+{
+ void *vif = netdev_priv(dev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++)
+ data[i] = *(int *)(vif + xenvif_stats[i].offset);
+}
+
+static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data)
+{
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++)
+ memcpy(data + i * ETH_GSTRING_LEN,
+ xenvif_stats[i].name, ETH_GSTRING_LEN);
+ break;
+ }
+}
+
+static struct ethtool_ops xenvif_ethtool_ops = {
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = xenvif_set_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = xenvif_set_sg,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = xenvif_set_tso,
+ .get_link = ethtool_op_get_link,
+
+ .get_sset_count = xenvif_get_sset_count,
+ .get_ethtool_stats = xenvif_get_ethtool_stats,
+ .get_strings = xenvif_get_strings,
+};
+
+static struct net_device_ops xenvif_netdev_ops = {
+ .ndo_start_xmit = xenvif_start_xmit,
+ .ndo_get_stats = xenvif_get_stats,
+ .ndo_open = xenvif_open,
+ .ndo_stop = xenvif_close,
+ .ndo_change_mtu = xenvif_change_mtu,
+};
+
+struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
+ unsigned int handle)
+{
+ int err;
+ struct net_device *dev;
+ struct xenvif *vif;
+ char name[IFNAMSIZ] = {};
+
+ snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle);
+ dev = alloc_netdev(sizeof(struct xenvif), name, ether_setup);
+ if (dev == NULL) {
+ pr_warn("Could not allocate netdev\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ SET_NETDEV_DEV(dev, parent);
+
+ vif = netdev_priv(dev);
+ vif->domid = domid;
+ vif->handle = handle;
+ vif->netbk = NULL;
+ vif->can_sg = 1;
+ vif->csum = 1;
+ atomic_set(&vif->refcnt, 1);
+ init_waitqueue_head(&vif->waiting_to_free);
+ vif->dev = dev;
+ INIT_LIST_HEAD(&vif->schedule_list);
+ INIT_LIST_HEAD(&vif->notify_list);
+
+ vif->credit_bytes = vif->remaining_credit = ~0UL;
+ vif->credit_usec = 0UL;
+ init_timer(&vif->credit_timeout);
+ /* Initialize 'expires' now: it's used to track the credit window. */
+ vif->credit_timeout.expires = jiffies;
+
+ dev->netdev_ops = &xenvif_netdev_ops;
+ xenvif_set_features(vif);
+ SET_ETHTOOL_OPS(dev, &xenvif_ethtool_ops);
+
+ dev->tx_queue_len = XENVIF_QUEUE_LENGTH;
+
+ /*
+ * Initialise a dummy MAC address. We choose the numerically
+ * largest non-broadcast address to prevent the address getting
+ * stolen by an Ethernet bridge for STP purposes.
+ * (FE:FF:FF:FF:FF:FF)
+ */
+ memset(dev->dev_addr, 0xFF, ETH_ALEN);
+ dev->dev_addr[0] &= ~0x01;
+
+ netif_carrier_off(dev);
+
+ err = register_netdev(dev);
+ if (err) {
+ netdev_warn(dev, "Could not register device: err=%d\n", err);
+ free_netdev(dev);
+ return ERR_PTR(err);
+ }
+
+ netdev_dbg(dev, "Successfully created xenvif\n");
+ return vif;
+}
+
+int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
+ unsigned long rx_ring_ref, unsigned int evtchn)
+{
+ int err = -ENOMEM;
+
+ /* Already connected through? */
+ if (vif->irq)
+ return 0;
+
+ xenvif_set_features(vif);
+
+ err = xen_netbk_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref);
+ if (err < 0)
+ goto err;
+
+ err = bind_interdomain_evtchn_to_irqhandler(
+ vif->domid, evtchn, xenvif_interrupt, 0,
+ vif->dev->name, vif);
+ if (err < 0)
+ goto err_unmap;
+ vif->irq = err;
+ disable_irq(vif->irq);
+
+ xenvif_get(vif);
+
+ rtnl_lock();
+ netif_carrier_on(vif->dev);
+ if (netif_running(vif->dev))
+ xenvif_up(vif);
+ rtnl_unlock();
+
+ return 0;
+err_unmap:
+ xen_netbk_unmap_frontend_rings(vif);
+err:
+ return err;
+}
+
+void xenvif_disconnect(struct xenvif *vif)
+{
+ struct net_device *dev = vif->dev;
+ if (netif_carrier_ok(dev)) {
+ rtnl_lock();
+ netif_carrier_off(dev); /* discard queued packets */
+ if (netif_running(dev))
+ xenvif_down(vif);
+ rtnl_unlock();
+ xenvif_put(vif);
+ }
+
+ atomic_dec(&vif->refcnt);
+ wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0);
+
+ del_timer_sync(&vif->credit_timeout);
+
+ if (vif->irq)
+ unbind_from_irqhandler(vif->irq, vif);
+
+ unregister_netdev(vif->dev);
+
+ xen_netbk_unmap_frontend_rings(vif);
+
+ free_netdev(vif->dev);
+}
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
new file mode 100644
index 0000000..0e4851b
--- /dev/null
+++ b/drivers/net/xen-netback/netback.c
@@ -0,0 +1,1745 @@
+/*
+ * Back-end of the driver for virtual network devices. This portion of the
+ * driver exports a 'unified' network-device interface that can be accessed
+ * by any operating system that implements a compatible front end. A
+ * reference front-end implementation can be found in:
+ * drivers/net/xen-netfront.c
+ *
+ * Copyright (c) 2002-2005, K A Fraser
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "common.h"
+
+#include <linux/kthread.h>
+#include <linux/if_vlan.h>
+#include <linux/udp.h>
+
+#include <net/tcp.h>
+
+#include <xen/events.h>
+#include <xen/interface/memory.h>
+
+#include <asm/xen/hypercall.h>
+#include <asm/xen/page.h>
+
+struct pending_tx_info {
+ struct xen_netif_tx_request req;
+ struct xenvif *vif;
+};
+typedef unsigned int pending_ring_idx_t;
+
+struct netbk_rx_meta {
+ int id;
+ int size;
+ int gso_size;
+};
+
+#define MAX_PENDING_REQS 256
+
+#define MAX_BUFFER_OFFSET PAGE_SIZE
+
+/* extra field used in struct page */
+union page_ext {
+ struct {
+#if BITS_PER_LONG < 64
+#define IDX_WIDTH 8
+#define GROUP_WIDTH (BITS_PER_LONG - IDX_WIDTH)
+ unsigned int group:GROUP_WIDTH;
+ unsigned int idx:IDX_WIDTH;
+#else
+ unsigned int group, idx;
+#endif
+ } e;
+ void *mapping;
+};
+
+struct xen_netbk {
+ wait_queue_head_t wq;
+ struct task_struct *task;
+
+ struct sk_buff_head rx_queue;
+ struct sk_buff_head tx_queue;
+
+ struct timer_list net_timer;
+
+ struct page *mmap_pages[MAX_PENDING_REQS];
+
+ pending_ring_idx_t pending_prod;
+ pending_ring_idx_t pending_cons;
+ struct list_head net_schedule_list;
+
+ /* Protect the net_schedule_list in netif. */
+ spinlock_t net_schedule_list_lock;
+
+ atomic_t netfront_count;
+
+ struct pending_tx_info pending_tx_info[MAX_PENDING_REQS];
+ struct gnttab_copy tx_copy_ops[MAX_PENDING_REQS];
+
+ u16 pending_ring[MAX_PENDING_REQS];
+
+ /*
+ * Given MAX_BUFFER_OFFSET of 4096 the worst case is that each
+ * head/fragment page uses 2 copy operations because it
+ * straddles two buffers in the frontend.
+ */
+ struct gnttab_copy grant_copy_op[2*XEN_NETIF_RX_RING_SIZE];
+ struct netbk_rx_meta meta[2*XEN_NETIF_RX_RING_SIZE];
+};
+
+static struct xen_netbk *xen_netbk;
+static int xen_netbk_group_nr;
+
+void xen_netbk_add_xenvif(struct xenvif *vif)
+{
+ int i;
+ int min_netfront_count;
+ int min_group = 0;
+ struct xen_netbk *netbk;
+
+ min_netfront_count = atomic_read(&xen_netbk[0].netfront_count);
+ for (i = 0; i < xen_netbk_group_nr; i++) {
+ int netfront_count = atomic_read(&xen_netbk[i].netfront_count);
+ if (netfront_count < min_netfront_count) {
+ min_group = i;
+ min_netfront_count = netfront_count;
+ }
+ }
+
+ netbk = &xen_netbk[min_group];
+
+ vif->netbk = netbk;
+ atomic_inc(&netbk->netfront_count);
+}
+
+void xen_netbk_remove_xenvif(struct xenvif *vif)
+{
+ struct xen_netbk *netbk = vif->netbk;
+ vif->netbk = NULL;
+ atomic_dec(&netbk->netfront_count);
+}
+
+static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx);
+static void make_tx_response(struct xenvif *vif,
+ struct xen_netif_tx_request *txp,
+ s8 st);
+static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
+ u16 id,
+ s8 st,
+ u16 offset,
+ u16 size,
+ u16 flags);
+
+static inline unsigned long idx_to_pfn(struct xen_netbk *netbk,
+ unsigned int idx)
+{
+ return page_to_pfn(netbk->mmap_pages[idx]);
+}
+
+static inline unsigned long idx_to_kaddr(struct xen_netbk *netbk,
+ unsigned int idx)
+{
+ return (unsigned long)pfn_to_kaddr(idx_to_pfn(netbk, idx));
+}
+
+/* extra field used in struct page */
+static inline void set_page_ext(struct page *pg, struct xen_netbk *netbk,
+ unsigned int idx)
+{
+ unsigned int group = netbk - xen_netbk;
+ union page_ext ext = { .e = { .group = group + 1, .idx = idx } };
+
+ BUILD_BUG_ON(sizeof(ext) > sizeof(ext.mapping));
+ pg->mapping = ext.mapping;
+}
+
+static int get_page_ext(struct page *pg,
+ unsigned int *pgroup, unsigned int *pidx)
+{
+ union page_ext ext = { .mapping = pg->mapping };
+ struct xen_netbk *netbk;
+ unsigned int group, idx;
+
+ group = ext.e.group - 1;
+
+ if (group < 0 || group >= xen_netbk_group_nr)
+ return 0;
+
+ netbk = &xen_netbk[group];
+
+ idx = ext.e.idx;
+
+ if ((idx < 0) || (idx >= MAX_PENDING_REQS))
+ return 0;
+
+ if (netbk->mmap_pages[idx] != pg)
+ return 0;
+
+ *pgroup = group;
+ *pidx = idx;
+
+ return 1;
+}
+
+/*
+ * This is the amount of packet we copy rather than map, so that the
+ * guest can't fiddle with the contents of the headers while we do
+ * packet processing on them (netfilter, routing, etc).
+ */
+#define PKT_PROT_LEN (ETH_HLEN + \
+ VLAN_HLEN + \
+ sizeof(struct iphdr) + MAX_IPOPTLEN + \
+ sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE)
+
+static inline pending_ring_idx_t pending_index(unsigned i)
+{
+ return i & (MAX_PENDING_REQS-1);
+}
+
+static inline pending_ring_idx_t nr_pending_reqs(struct xen_netbk *netbk)
+{
+ return MAX_PENDING_REQS -
+ netbk->pending_prod + netbk->pending_cons;
+}
+
+static void xen_netbk_kick_thread(struct xen_netbk *netbk)
+{
+ wake_up(&netbk->wq);
+}
+
+static int max_required_rx_slots(struct xenvif *vif)
+{
+ int max = DIV_ROUND_UP(vif->dev->mtu, PAGE_SIZE);
+
+ if (vif->can_sg || vif->gso || vif->gso_prefix)
+ max += MAX_SKB_FRAGS + 1; /* extra_info + frags */
+
+ return max;
+}
+
+int xen_netbk_rx_ring_full(struct xenvif *vif)
+{
+ RING_IDX peek = vif->rx_req_cons_peek;
+ RING_IDX needed = max_required_rx_slots(vif);
+
+ return ((vif->rx.sring->req_prod - peek) < needed) ||
+ ((vif->rx.rsp_prod_pvt + XEN_NETIF_RX_RING_SIZE - peek) < needed);
+}
+
+int xen_netbk_must_stop_queue(struct xenvif *vif)
+{
+ if (!xen_netbk_rx_ring_full(vif))
+ return 0;
+
+ vif->rx.sring->req_event = vif->rx_req_cons_peek +
+ max_required_rx_slots(vif);
+ mb(); /* request notification /then/ check the queue */
+
+ return xen_netbk_rx_ring_full(vif);
+}
+
+/*
+ * Returns true if we should start a new receive buffer instead of
+ * adding 'size' bytes to a buffer which currently contains 'offset'
+ * bytes.
+ */
+static bool start_new_rx_buffer(int offset, unsigned long size, int head)
+{
+ /* simple case: we have completely filled the current buffer. */
+ if (offset == MAX_BUFFER_OFFSET)
+ return true;
+
+ /*
+ * complex case: start a fresh buffer if the current frag
+ * would overflow the current buffer but only if:
+ * (i) this frag would fit completely in the next buffer
+ * and (ii) there is already some data in the current buffer
+ * and (iii) this is not the head buffer.
+ *
+ * Where:
+ * - (i) stops us splitting a frag into two copies
+ * unless the frag is too large for a single buffer.
+ * - (ii) stops us from leaving a buffer pointlessly empty.
+ * - (iii) stops us leaving the first buffer
+ * empty. Strictly speaking this is already covered
+ * by (ii) but is explicitly checked because
+ * netfront relies on the first buffer being
+ * non-empty and can crash otherwise.
+ *
+ * This means we will effectively linearise small
+ * frags but do not needlessly split large buffers
+ * into multiple copies tend to give large frags their
+ * own buffers as before.
+ */
+ if ((offset + size > MAX_BUFFER_OFFSET) &&
+ (size <= MAX_BUFFER_OFFSET) && offset && !head)
+ return true;
+
+ return false;
+}
+
+/*
+ * Figure out how many ring slots we're going to need to send @skb to
+ * the guest. This function is essentially a dry run of
+ * netbk_gop_frag_copy.
+ */
+unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb)
+{
+ unsigned int count;
+ int i, copy_off;
+
+ count = DIV_ROUND_UP(
+ offset_in_page(skb->data)+skb_headlen(skb), PAGE_SIZE);
+
+ copy_off = skb_headlen(skb) % PAGE_SIZE;
+
+ if (skb_shinfo(skb)->gso_size)
+ count++;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ unsigned long size = skb_shinfo(skb)->frags[i].size;
+ unsigned long bytes;
+ while (size > 0) {
+ BUG_ON(copy_off > MAX_BUFFER_OFFSET);
+
+ if (start_new_rx_buffer(copy_off, size, 0)) {
+ count++;
+ copy_off = 0;
+ }
+
+ bytes = size;
+ if (copy_off + bytes > MAX_BUFFER_OFFSET)
+ bytes = MAX_BUFFER_OFFSET - copy_off;
+
+ copy_off += bytes;
+ size -= bytes;
+ }
+ }
+ return count;
+}
+
+struct netrx_pending_operations {
+ unsigned copy_prod, copy_cons;
+ unsigned meta_prod, meta_cons;
+ struct gnttab_copy *copy;
+ struct netbk_rx_meta *meta;
+ int copy_off;
+ grant_ref_t copy_gref;
+};
+
+static struct netbk_rx_meta *get_next_rx_buffer(struct xenvif *vif,
+ struct netrx_pending_operations *npo)
+{
+ struct netbk_rx_meta *meta;
+ struct xen_netif_rx_request *req;
+
+ req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+
+ meta = npo->meta + npo->meta_prod++;
+ meta->gso_size = 0;
+ meta->size = 0;
+ meta->id = req->id;
+
+ npo->copy_off = 0;
+ npo->copy_gref = req->gref;
+
+ return meta;
+}
+
+/*
+ * Set up the grant operations for this fragment. If it's a flipping
+ * interface, we also set up the unmap request from here.
+ */
+static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
+ struct netrx_pending_operations *npo,
+ struct page *page, unsigned long size,
+ unsigned long offset, int *head)
+{
+ struct gnttab_copy *copy_gop;
+ struct netbk_rx_meta *meta;
+ /*
+ * These variables a used iff get_page_ext returns true,
+ * in which case they are guaranteed to be initialized.
+ */
+ unsigned int uninitialized_var(group), uninitialized_var(idx);
+ int foreign = get_page_ext(page, &group, &idx);
+ unsigned long bytes;
+
+ /* Data must not cross a page boundary. */
+ BUG_ON(size + offset > PAGE_SIZE);
+
+ meta = npo->meta + npo->meta_prod - 1;
+
+ while (size > 0) {
+ BUG_ON(npo->copy_off > MAX_BUFFER_OFFSET);
+
+ if (start_new_rx_buffer(npo->copy_off, size, *head)) {
+ /*
+ * Netfront requires there to be some data in the head
+ * buffer.
+ */
+ BUG_ON(*head);
+
+ meta = get_next_rx_buffer(vif, npo);
+ }
+
+ bytes = size;
+ if (npo->copy_off + bytes > MAX_BUFFER_OFFSET)
+ bytes = MAX_BUFFER_OFFSET - npo->copy_off;
+
+ copy_gop = npo->copy + npo->copy_prod++;
+ copy_gop->flags = GNTCOPY_dest_gref;
+ if (foreign) {
+ struct xen_netbk *netbk = &xen_netbk[group];
+ struct pending_tx_info *src_pend;
+
+ src_pend = &netbk->pending_tx_info[idx];
+
+ copy_gop->source.domid = src_pend->vif->domid;
+ copy_gop->source.u.ref = src_pend->req.gref;
+ copy_gop->flags |= GNTCOPY_source_gref;
+ } else {
+ void *vaddr = page_address(page);
+ copy_gop->source.domid = DOMID_SELF;
+ copy_gop->source.u.gmfn = virt_to_mfn(vaddr);
+ }
+ copy_gop->source.offset = offset;
+ copy_gop->dest.domid = vif->domid;
+
+ copy_gop->dest.offset = npo->copy_off;
+ copy_gop->dest.u.ref = npo->copy_gref;
+ copy_gop->len = bytes;
+
+ npo->copy_off += bytes;
+ meta->size += bytes;
+
+ offset += bytes;
+ size -= bytes;
+
+ /* Leave a gap for the GSO descriptor. */
+ if (*head && skb_shinfo(skb)->gso_size && !vif->gso_prefix)
+ vif->rx.req_cons++;
+
+ *head = 0; /* There must be something in this buffer now. */
+
+ }
+}
+
+/*
+ * Prepare an SKB to be transmitted to the frontend.
+ *
+ * This function is responsible for allocating grant operations, meta
+ * structures, etc.
+ *
+ * It returns the number of meta structures consumed. The number of
+ * ring slots used is always equal to the number of meta slots used
+ * plus the number of GSO descriptors used. Currently, we use either
+ * zero GSO descriptors (for non-GSO packets) or one descriptor (for
+ * frontend-side LRO).
+ */
+static int netbk_gop_skb(struct sk_buff *skb,
+ struct netrx_pending_operations *npo)
+{
+ struct xenvif *vif = netdev_priv(skb->dev);
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+ int i;
+ struct xen_netif_rx_request *req;
+ struct netbk_rx_meta *meta;
+ unsigned char *data;
+ int head = 1;
+ int old_meta_prod;
+
+ old_meta_prod = npo->meta_prod;
+
+ /* Set up a GSO prefix descriptor, if necessary */
+ if (skb_shinfo(skb)->gso_size && vif->gso_prefix) {
+ req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+ meta = npo->meta + npo->meta_prod++;
+ meta->gso_size = skb_shinfo(skb)->gso_size;
+ meta->size = 0;
+ meta->id = req->id;
+ }
+
+ req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+ meta = npo->meta + npo->meta_prod++;
+
+ if (!vif->gso_prefix)
+ meta->gso_size = skb_shinfo(skb)->gso_size;
+ else
+ meta->gso_size = 0;
+
+ meta->size = 0;
+ meta->id = req->id;
+ npo->copy_off = 0;
+ npo->copy_gref = req->gref;
+
+ data = skb->data;
+ while (data < skb_tail_pointer(skb)) {
+ unsigned int offset = offset_in_page(data);
+ unsigned int len = PAGE_SIZE - offset;
+
+ if (data + len > skb_tail_pointer(skb))
+ len = skb_tail_pointer(skb) - data;
+
+ netbk_gop_frag_copy(vif, skb, npo,
+ virt_to_page(data), len, offset, &head);
+ data += len;
+ }
+
+ for (i = 0; i < nr_frags; i++) {
+ netbk_gop_frag_copy(vif, skb, npo,
+ skb_shinfo(skb)->frags[i].page,
+ skb_shinfo(skb)->frags[i].size,
+ skb_shinfo(skb)->frags[i].page_offset,
+ &head);
+ }
+
+ return npo->meta_prod - old_meta_prod;
+}
+
+/*
+ * This is a twin to netbk_gop_skb. Assume that netbk_gop_skb was
+ * used to set up the operations on the top of
+ * netrx_pending_operations, which have since been done. Check that
+ * they didn't give any errors and advance over them.
+ */
+static int netbk_check_gop(struct xenvif *vif, int nr_meta_slots,
+ struct netrx_pending_operations *npo)
+{
+ struct gnttab_copy *copy_op;
+ int status = XEN_NETIF_RSP_OKAY;
+ int i;
+
+ for (i = 0; i < nr_meta_slots; i++) {
+ copy_op = npo->copy + npo->copy_cons++;
+ if (copy_op->status != GNTST_okay) {
+ netdev_dbg(vif->dev,
+ "Bad status %d from copy to DOM%d.\n",
+ copy_op->status, vif->domid);
+ status = XEN_NETIF_RSP_ERROR;
+ }
+ }
+
+ return status;
+}
+
+static void netbk_add_frag_responses(struct xenvif *vif, int status,
+ struct netbk_rx_meta *meta,
+ int nr_meta_slots)
+{
+ int i;
+ unsigned long offset;
+
+ /* No fragments used */
+ if (nr_meta_slots <= 1)
+ return;
+
+ nr_meta_slots--;
+
+ for (i = 0; i < nr_meta_slots; i++) {
+ int flags;
+ if (i == nr_meta_slots - 1)
+ flags = 0;
+ else
+ flags = XEN_NETRXF_more_data;
+
+ offset = 0;
+ make_rx_response(vif, meta[i].id, status, offset,
+ meta[i].size, flags);
+ }
+}
+
+struct skb_cb_overlay {
+ int meta_slots_used;
+};
+
+static void xen_netbk_rx_action(struct xen_netbk *netbk)
+{
+ struct xenvif *vif = NULL, *tmp;
+ s8 status;
+ u16 irq, flags;
+ struct xen_netif_rx_response *resp;
+ struct sk_buff_head rxq;
+ struct sk_buff *skb;
+ LIST_HEAD(notify);
+ int ret;
+ int nr_frags;
+ int count;
+ unsigned long offset;
+ struct skb_cb_overlay *sco;
+
+ struct netrx_pending_operations npo = {
+ .copy = netbk->grant_copy_op,
+ .meta = netbk->meta,
+ };
+
+ skb_queue_head_init(&rxq);
+
+ count = 0;
+
+ while ((skb = skb_dequeue(&netbk->rx_queue)) != NULL) {
+ vif = netdev_priv(skb->dev);
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ sco = (struct skb_cb_overlay *)skb->cb;
+ sco->meta_slots_used = netbk_gop_skb(skb, &npo);
+
+ count += nr_frags + 1;
+
+ __skb_queue_tail(&rxq, skb);
+
+ /* Filled the batch queue? */
+ if (count + MAX_SKB_FRAGS >= XEN_NETIF_RX_RING_SIZE)
+ break;
+ }
+
+ BUG_ON(npo.meta_prod > ARRAY_SIZE(netbk->meta));
+
+ if (!npo.copy_prod)
+ return;
+
+ BUG_ON(npo.copy_prod > ARRAY_SIZE(netbk->grant_copy_op));
+ ret = HYPERVISOR_grant_table_op(GNTTABOP_copy, &netbk->grant_copy_op,
+ npo.copy_prod);
+ BUG_ON(ret != 0);
+
+ while ((skb = __skb_dequeue(&rxq)) != NULL) {
+ sco = (struct skb_cb_overlay *)skb->cb;
+
+ vif = netdev_priv(skb->dev);
+
+ if (netbk->meta[npo.meta_cons].gso_size && vif->gso_prefix) {
+ resp = RING_GET_RESPONSE(&vif->rx,
+ vif->rx.rsp_prod_pvt++);
+
+ resp->flags = XEN_NETRXF_gso_prefix | XEN_NETRXF_more_data;
+
+ resp->offset = netbk->meta[npo.meta_cons].gso_size;
+ resp->id = netbk->meta[npo.meta_cons].id;
+ resp->status = sco->meta_slots_used;
+
+ npo.meta_cons++;
+ sco->meta_slots_used--;
+ }
+
+
+ vif->dev->stats.tx_bytes += skb->len;
+ vif->dev->stats.tx_packets++;
+
+ status = netbk_check_gop(vif, sco->meta_slots_used, &npo);
+
+ if (sco->meta_slots_used == 1)
+ flags = 0;
+ else
+ flags = XEN_NETRXF_more_data;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) /* local packet? */
+ flags |= XEN_NETRXF_csum_blank | XEN_NETRXF_data_validated;
+ else if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+ /* remote but checksummed. */
+ flags |= XEN_NETRXF_data_validated;
+
+ offset = 0;
+ resp = make_rx_response(vif, netbk->meta[npo.meta_cons].id,
+ status, offset,
+ netbk->meta[npo.meta_cons].size,
+ flags);
+
+ if (netbk->meta[npo.meta_cons].gso_size && !vif->gso_prefix) {
+ struct xen_netif_extra_info *gso =
+ (struct xen_netif_extra_info *)
+ RING_GET_RESPONSE(&vif->rx,
+ vif->rx.rsp_prod_pvt++);
+
+ resp->flags |= XEN_NETRXF_extra_info;
+
+ gso->u.gso.size = netbk->meta[npo.meta_cons].gso_size;
+ gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4;
+ gso->u.gso.pad = 0;
+ gso->u.gso.features = 0;
+
+ gso->type = XEN_NETIF_EXTRA_TYPE_GSO;
+ gso->flags = 0;
+ }
+
+ netbk_add_frag_responses(vif, status,
+ netbk->meta + npo.meta_cons + 1,
+ sco->meta_slots_used);
+
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->rx, ret);
+ irq = vif->irq;
+ if (ret && list_empty(&vif->notify_list))
+ list_add_tail(&vif->notify_list, ¬ify);
+
+ xenvif_notify_tx_completion(vif);
+
+ xenvif_put(vif);
+ npo.meta_cons += sco->meta_slots_used;
+ dev_kfree_skb(skb);
+ }
+
+ list_for_each_entry_safe(vif, tmp, ¬ify, notify_list) {
+ notify_remote_via_irq(vif->irq);
+ list_del_init(&vif->notify_list);
+ }
+
+ /* More work to do? */
+ if (!skb_queue_empty(&netbk->rx_queue) &&
+ !timer_pending(&netbk->net_timer))
+ xen_netbk_kick_thread(netbk);
+}
+
+void xen_netbk_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb)
+{
+ struct xen_netbk *netbk = vif->netbk;
+
+ skb_queue_tail(&netbk->rx_queue, skb);
+
+ xen_netbk_kick_thread(netbk);
+}
+
+static void xen_netbk_alarm(unsigned long data)
+{
+ struct xen_netbk *netbk = (struct xen_netbk *)data;
+ xen_netbk_kick_thread(netbk);
+}
+
+static int __on_net_schedule_list(struct xenvif *vif)
+{
+ return !list_empty(&vif->schedule_list);
+}
+
+/* Must be called with net_schedule_list_lock held */
+static void remove_from_net_schedule_list(struct xenvif *vif)
+{
+ if (likely(__on_net_schedule_list(vif))) {
+ list_del_init(&vif->schedule_list);
+ xenvif_put(vif);
+ }
+}
+
+static struct xenvif *poll_net_schedule_list(struct xen_netbk *netbk)
+{
+ struct xenvif *vif = NULL;
+
+ spin_lock_irq(&netbk->net_schedule_list_lock);
+ if (list_empty(&netbk->net_schedule_list))
+ goto out;
+
+ vif = list_first_entry(&netbk->net_schedule_list,
+ struct xenvif, schedule_list);
+ if (!vif)
+ goto out;
+
+ xenvif_get(vif);
+
+ remove_from_net_schedule_list(vif);
+out:
+ spin_unlock_irq(&netbk->net_schedule_list_lock);
+ return vif;
+}
+
+void xen_netbk_schedule_xenvif(struct xenvif *vif)
+{
+ unsigned long flags;
+ struct xen_netbk *netbk = vif->netbk;
+
+ if (__on_net_schedule_list(vif))
+ goto kick;
+
+ spin_lock_irqsave(&netbk->net_schedule_list_lock, flags);
+ if (!__on_net_schedule_list(vif) &&
+ likely(xenvif_schedulable(vif))) {
+ list_add_tail(&vif->schedule_list, &netbk->net_schedule_list);
+ xenvif_get(vif);
+ }
+ spin_unlock_irqrestore(&netbk->net_schedule_list_lock, flags);
+
+kick:
+ smp_mb();
+ if ((nr_pending_reqs(netbk) < (MAX_PENDING_REQS/2)) &&
+ !list_empty(&netbk->net_schedule_list))
+ xen_netbk_kick_thread(netbk);
+}
+
+void xen_netbk_deschedule_xenvif(struct xenvif *vif)
+{
+ struct xen_netbk *netbk = vif->netbk;
+ spin_lock_irq(&netbk->net_schedule_list_lock);
+ remove_from_net_schedule_list(vif);
+ spin_unlock_irq(&netbk->net_schedule_list_lock);
+}
+
+void xen_netbk_check_rx_xenvif(struct xenvif *vif)
+{
+ int more_to_do;
+
+ RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, more_to_do);
+
+ if (more_to_do)
+ xen_netbk_schedule_xenvif(vif);
+}
+
+static void tx_add_credit(struct xenvif *vif)
+{
+ unsigned long max_burst, max_credit;
+
+ /*
+ * Allow a burst big enough to transmit a jumbo packet of up to 128kB.
+ * Otherwise the interface can seize up due to insufficient credit.
+ */
+ max_burst = RING_GET_REQUEST(&vif->tx, vif->tx.req_cons)->size;
+ max_burst = min(max_burst, 131072UL);
+ max_burst = max(max_burst, vif->credit_bytes);
+
+ /* Take care that adding a new chunk of credit doesn't wrap to zero. */
+ max_credit = vif->remaining_credit + vif->credit_bytes;
+ if (max_credit < vif->remaining_credit)
+ max_credit = ULONG_MAX; /* wrapped: clamp to ULONG_MAX */
+
+ vif->remaining_credit = min(max_credit, max_burst);
+}
+
+static void tx_credit_callback(unsigned long data)
+{
+ struct xenvif *vif = (struct xenvif *)data;
+ tx_add_credit(vif);
+ xen_netbk_check_rx_xenvif(vif);
+}
+
+static void netbk_tx_err(struct xenvif *vif,
+ struct xen_netif_tx_request *txp, RING_IDX end)
+{
+ RING_IDX cons = vif->tx.req_cons;
+
+ do {
+ make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
+ if (cons >= end)
+ break;
+ txp = RING_GET_REQUEST(&vif->tx, cons++);
+ } while (1);
+ vif->tx.req_cons = cons;
+ xen_netbk_check_rx_xenvif(vif);
+ xenvif_put(vif);
+}
+
+static int netbk_count_requests(struct xenvif *vif,
+ struct xen_netif_tx_request *first,
+ struct xen_netif_tx_request *txp,
+ int work_to_do)
+{
+ RING_IDX cons = vif->tx.req_cons;
+ int frags = 0;
+
+ if (!(first->flags & XEN_NETTXF_more_data))
+ return 0;
+
+ do {
+ if (frags >= work_to_do) {
+ netdev_dbg(vif->dev, "Need more frags\n");
+ return -frags;
+ }
+
+ if (unlikely(frags >= MAX_SKB_FRAGS)) {
+ netdev_dbg(vif->dev, "Too many frags\n");
+ return -frags;
+ }
+
+ memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + frags),
+ sizeof(*txp));
+ if (txp->size > first->size) {
+ netdev_dbg(vif->dev, "Frags galore\n");
+ return -frags;
+ }
+
+ first->size -= txp->size;
+ frags++;
+
+ if (unlikely((txp->offset + txp->size) > PAGE_SIZE)) {
+ netdev_dbg(vif->dev, "txp->offset: %x, size: %u\n",
+ txp->offset, txp->size);
+ return -frags;
+ }
+ } while ((txp++)->flags & XEN_NETTXF_more_data);
+ return frags;
+}
+
+static struct page *xen_netbk_alloc_page(struct xen_netbk *netbk,
+ struct sk_buff *skb,
+ unsigned long pending_idx)
+{
+ struct page *page;
+ page = alloc_page(GFP_KERNEL|__GFP_COLD);
+ if (!page)
+ return NULL;
+ set_page_ext(page, netbk, pending_idx);
+ netbk->mmap_pages[pending_idx] = page;
+ return page;
+}
+
+static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
+ struct xenvif *vif,
+ struct sk_buff *skb,
+ struct xen_netif_tx_request *txp,
+ struct gnttab_copy *gop)
+{
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+ skb_frag_t *frags = shinfo->frags;
+ unsigned long pending_idx = *((u16 *)skb->data);
+ int i, start;
+
+ /* Skip first skb fragment if it is on same page as header fragment. */
+ start = ((unsigned long)shinfo->frags[0].page == pending_idx);
+
+ for (i = start; i < shinfo->nr_frags; i++, txp++) {
+ struct page *page;
+ pending_ring_idx_t index;
+ struct pending_tx_info *pending_tx_info =
+ netbk->pending_tx_info;
+
+ index = pending_index(netbk->pending_cons++);
+ pending_idx = netbk->pending_ring[index];
+ page = xen_netbk_alloc_page(netbk, skb, pending_idx);
+ if (!page)
+ return NULL;
+
+ netbk->mmap_pages[pending_idx] = page;
+
+ gop->source.u.ref = txp->gref;
+ gop->source.domid = vif->domid;
+ gop->source.offset = txp->offset;
+
+ gop->dest.u.gmfn = virt_to_mfn(page_address(page));
+ gop->dest.domid = DOMID_SELF;
+ gop->dest.offset = txp->offset;
+
+ gop->len = txp->size;
+ gop->flags = GNTCOPY_source_gref;
+
+ gop++;
+
+ memcpy(&pending_tx_info[pending_idx].req, txp, sizeof(*txp));
+ xenvif_get(vif);
+ pending_tx_info[pending_idx].vif = vif;
+ frags[i].page = (void *)pending_idx;
+ }
+
+ return gop;
+}
+
+static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
+ struct sk_buff *skb,
+ struct gnttab_copy **gopp)
+{
+ struct gnttab_copy *gop = *gopp;
+ int pending_idx = *((u16 *)skb->data);
+ struct pending_tx_info *pending_tx_info = netbk->pending_tx_info;
+ struct xenvif *vif = pending_tx_info[pending_idx].vif;
+ struct xen_netif_tx_request *txp;
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+ int nr_frags = shinfo->nr_frags;
+ int i, err, start;
+
+ /* Check status of header. */
+ err = gop->status;
+ if (unlikely(err)) {
+ pending_ring_idx_t index;
+ index = pending_index(netbk->pending_prod++);
+ txp = &pending_tx_info[pending_idx].req;
+ make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
+ netbk->pending_ring[index] = pending_idx;
+ xenvif_put(vif);
+ }
+
+ /* Skip first skb fragment if it is on same page as header fragment. */
+ start = ((unsigned long)shinfo->frags[0].page == pending_idx);
+
+ for (i = start; i < nr_frags; i++) {
+ int j, newerr;
+ pending_ring_idx_t index;
+
+ pending_idx = (unsigned long)shinfo->frags[i].page;
+
+ /* Check error status: if okay then remember grant handle. */
+ newerr = (++gop)->status;
+ if (likely(!newerr)) {
+ /* Had a previous error? Invalidate this fragment. */
+ if (unlikely(err))
+ xen_netbk_idx_release(netbk, pending_idx);
+ continue;
+ }
+
+ /* Error on this fragment: respond to client with an error. */
+ txp = &netbk->pending_tx_info[pending_idx].req;
+ make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
+ index = pending_index(netbk->pending_prod++);
+ netbk->pending_ring[index] = pending_idx;
+ xenvif_put(vif);
+
+ /* Not the first error? Preceding frags already invalidated. */
+ if (err)
+ continue;
+
+ /* First error: invalidate header and preceding fragments. */
+ pending_idx = *((u16 *)skb->data);
+ xen_netbk_idx_release(netbk, pending_idx);
+ for (j = start; j < i; j++) {
+ pending_idx = (unsigned long)shinfo->frags[i].page;
+ xen_netbk_idx_release(netbk, pending_idx);
+ }
+
+ /* Remember the error: invalidate all subsequent fragments. */
+ err = newerr;
+ }
+
+ *gopp = gop + 1;
+ return err;
+}
+
+static void xen_netbk_fill_frags(struct xen_netbk *netbk, struct sk_buff *skb)
+{
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+ int nr_frags = shinfo->nr_frags;
+ int i;
+
+ for (i = 0; i < nr_frags; i++) {
+ skb_frag_t *frag = shinfo->frags + i;
+ struct xen_netif_tx_request *txp;
+ unsigned long pending_idx;
+
+ pending_idx = (unsigned long)frag->page;
+
+ txp = &netbk->pending_tx_info[pending_idx].req;
+ frag->page = virt_to_page(idx_to_kaddr(netbk, pending_idx));
+ frag->size = txp->size;
+ frag->page_offset = txp->offset;
+
+ skb->len += txp->size;
+ skb->data_len += txp->size;
+ skb->truesize += txp->size;
+
+ /* Take an extra reference to offset xen_netbk_idx_release */
+ get_page(netbk->mmap_pages[pending_idx]);
+ xen_netbk_idx_release(netbk, pending_idx);
+ }
+}
+
+static int xen_netbk_get_extras(struct xenvif *vif,
+ struct xen_netif_extra_info *extras,
+ int work_to_do)
+{
+ struct xen_netif_extra_info extra;
+ RING_IDX cons = vif->tx.req_cons;
+
+ do {
+ if (unlikely(work_to_do-- <= 0)) {
+ netdev_dbg(vif->dev, "Missing extra info\n");
+ return -EBADR;
+ }
+
+ memcpy(&extra, RING_GET_REQUEST(&vif->tx, cons),
+ sizeof(extra));
+ if (unlikely(!extra.type ||
+ extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
+ vif->tx.req_cons = ++cons;
+ netdev_dbg(vif->dev,
+ "Invalid extra type: %d\n", extra.type);
+ return -EINVAL;
+ }
+
+ memcpy(&extras[extra.type - 1], &extra, sizeof(extra));
+ vif->tx.req_cons = ++cons;
+ } while (extra.flags & XEN_NETIF_EXTRA_FLAG_MORE);
+
+ return work_to_do;
+}
+
+static int netbk_set_skb_gso(struct xenvif *vif,
+ struct sk_buff *skb,
+ struct xen_netif_extra_info *gso)
+{
+ if (!gso->u.gso.size) {
+ netdev_dbg(vif->dev, "GSO size must not be zero.\n");
+ return -EINVAL;
+ }
+
+ /* Currently only TCPv4 S.O. is supported. */
+ if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) {
+ netdev_dbg(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type);
+ return -EINVAL;
+ }
+
+ skb_shinfo(skb)->gso_size = gso->u.gso.size;
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+
+ /* Header must be checked, and gso_segs computed. */
+ skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
+ skb_shinfo(skb)->gso_segs = 0;
+
+ return 0;
+}
+
+static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
+{
+ struct iphdr *iph;
+ unsigned char *th;
+ int err = -EPROTO;
+ int recalculate_partial_csum = 0;
+
+ /*
+ * A GSO SKB must be CHECKSUM_PARTIAL. However some buggy
+ * peers can fail to set NETRXF_csum_blank when sending a GSO
+ * frame. In this case force the SKB to CHECKSUM_PARTIAL and
+ * recalculate the partial checksum.
+ */
+ if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) {
+ vif->rx_gso_checksum_fixup++;
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ recalculate_partial_csum = 1;
+ }
+
+ /* A non-CHECKSUM_PARTIAL SKB does not require setup. */
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
+ if (skb->protocol != htons(ETH_P_IP))
+ goto out;
+
+ iph = (void *)skb->data;
+ th = skb->data + 4 * iph->ihl;
+ if (th >= skb_tail_pointer(skb))
+ goto out;
+
+ skb->csum_start = th - skb->head;
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ skb->csum_offset = offsetof(struct tcphdr, check);
+
+ if (recalculate_partial_csum) {
+ struct tcphdr *tcph = (struct tcphdr *)th;
+ tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+ skb->len - iph->ihl*4,
+ IPPROTO_TCP, 0);
+ }
+ break;
+ case IPPROTO_UDP:
+ skb->csum_offset = offsetof(struct udphdr, check);
+
+ if (recalculate_partial_csum) {
+ struct udphdr *udph = (struct udphdr *)th;
+ udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+ skb->len - iph->ihl*4,
+ IPPROTO_UDP, 0);
+ }
+ break;
+ default:
+ if (net_ratelimit())
+ netdev_err(vif->dev,
+ "Attempting to checksum a non-TCP/UDP packet, dropping a protocol %d packet\n",
+ iph->protocol);
+ goto out;
+ }
+
+ if ((th + skb->csum_offset + 2) > skb_tail_pointer(skb))
+ goto out;
+
+ err = 0;
+
+out:
+ return err;
+}
+
+static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
+{
+ unsigned long now = jiffies;
+ unsigned long next_credit =
+ vif->credit_timeout.expires +
+ msecs_to_jiffies(vif->credit_usec / 1000);
+
+ /* Timer could already be pending in rare cases. */
+ if (timer_pending(&vif->credit_timeout))
+ return true;
+
+ /* Passed the point where we can replenish credit? */
+ if (time_after_eq(now, next_credit)) {
+ vif->credit_timeout.expires = now;
+ tx_add_credit(vif);
+ }
+
+ /* Still too big to send right now? Set a callback. */
+ if (size > vif->remaining_credit) {
+ vif->credit_timeout.data =
+ (unsigned long)vif;
+ vif->credit_timeout.function =
+ tx_credit_callback;
+ mod_timer(&vif->credit_timeout,
+ next_credit);
+
+ return true;
+ }
+
+ return false;
+}
+
+static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
+{
+ struct gnttab_copy *gop = netbk->tx_copy_ops, *request_gop;
+ struct sk_buff *skb;
+ int ret;
+
+ while (((nr_pending_reqs(netbk) + MAX_SKB_FRAGS) < MAX_PENDING_REQS) &&
+ !list_empty(&netbk->net_schedule_list)) {
+ struct xenvif *vif;
+ struct xen_netif_tx_request txreq;
+ struct xen_netif_tx_request txfrags[MAX_SKB_FRAGS];
+ struct page *page;
+ struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1];
+ u16 pending_idx;
+ RING_IDX idx;
+ int work_to_do;
+ unsigned int data_len;
+ pending_ring_idx_t index;
+
+ /* Get a netif from the list with work to do. */
+ vif = poll_net_schedule_list(netbk);
+ if (!vif)
+ continue;
+
+ RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, work_to_do);
+ if (!work_to_do) {
+ xenvif_put(vif);
+ continue;
+ }
+
+ idx = vif->tx.req_cons;
+ rmb(); /* Ensure that we see the request before we copy it. */
+ memcpy(&txreq, RING_GET_REQUEST(&vif->tx, idx), sizeof(txreq));
+
+ /* Credit-based scheduling. */
+ if (txreq.size > vif->remaining_credit &&
+ tx_credit_exceeded(vif, txreq.size)) {
+ xenvif_put(vif);
+ continue;
+ }
+
+ vif->remaining_credit -= txreq.size;
+
+ work_to_do--;
+ vif->tx.req_cons = ++idx;
+
+ memset(extras, 0, sizeof(extras));
+ if (txreq.flags & XEN_NETTXF_extra_info) {
+ work_to_do = xen_netbk_get_extras(vif, extras,
+ work_to_do);
+ idx = vif->tx.req_cons;
+ if (unlikely(work_to_do < 0)) {
+ netbk_tx_err(vif, &txreq, idx);
+ continue;
+ }
+ }
+
+ ret = netbk_count_requests(vif, &txreq, txfrags, work_to_do);
+ if (unlikely(ret < 0)) {
+ netbk_tx_err(vif, &txreq, idx - ret);
+ continue;
+ }
+ idx += ret;
+
+ if (unlikely(txreq.size < ETH_HLEN)) {
+ netdev_dbg(vif->dev,
+ "Bad packet size: %d\n", txreq.size);
+ netbk_tx_err(vif, &txreq, idx);
+ continue;
+ }
+
+ /* No crossing a page as the payload mustn't fragment. */
+ if (unlikely((txreq.offset + txreq.size) > PAGE_SIZE)) {
+ netdev_dbg(vif->dev,
+ "txreq.offset: %x, size: %u, end: %lu\n",
+ txreq.offset, txreq.size,
+ (txreq.offset&~PAGE_MASK) + txreq.size);
+ netbk_tx_err(vif, &txreq, idx);
+ continue;
+ }
+
+ index = pending_index(netbk->pending_cons);
+ pending_idx = netbk->pending_ring[index];
+
+ data_len = (txreq.size > PKT_PROT_LEN &&
+ ret < MAX_SKB_FRAGS) ?
+ PKT_PROT_LEN : txreq.size;
+
+ skb = alloc_skb(data_len + NET_SKB_PAD + NET_IP_ALIGN,
+ GFP_ATOMIC | __GFP_NOWARN);
+ if (unlikely(skb == NULL)) {
+ netdev_dbg(vif->dev,
+ "Can't allocate a skb in start_xmit.\n");
+ netbk_tx_err(vif, &txreq, idx);
+ break;
+ }
+
+ /* Packets passed to netif_rx() must have some headroom. */
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+
+ if (extras[XEN_NETIF_EXTRA_TYPE_GSO - 1].type) {
+ struct xen_netif_extra_info *gso;
+ gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1];
+
+ if (netbk_set_skb_gso(vif, skb, gso)) {
+ kfree_skb(skb);
+ netbk_tx_err(vif, &txreq, idx);
+ continue;
+ }
+ }
+
+ /* XXX could copy straight to head */
+ page = xen_netbk_alloc_page(netbk, skb, pending_idx);
+ if (!page) {
+ kfree_skb(skb);
+ netbk_tx_err(vif, &txreq, idx);
+ continue;
+ }
+
+ netbk->mmap_pages[pending_idx] = page;
+
+ gop->source.u.ref = txreq.gref;
+ gop->source.domid = vif->domid;
+ gop->source.offset = txreq.offset;
+
+ gop->dest.u.gmfn = virt_to_mfn(page_address(page));
+ gop->dest.domid = DOMID_SELF;
+ gop->dest.offset = txreq.offset;
+
+ gop->len = txreq.size;
+ gop->flags = GNTCOPY_source_gref;
+
+ gop++;
+
+ memcpy(&netbk->pending_tx_info[pending_idx].req,
+ &txreq, sizeof(txreq));
+ netbk->pending_tx_info[pending_idx].vif = vif;
+ *((u16 *)skb->data) = pending_idx;
+
+ __skb_put(skb, data_len);
+
+ skb_shinfo(skb)->nr_frags = ret;
+ if (data_len < txreq.size) {
+ skb_shinfo(skb)->nr_frags++;
+ skb_shinfo(skb)->frags[0].page =
+ (void *)(unsigned long)pending_idx;
+ } else {
+ /* Discriminate from any valid pending_idx value. */
+ skb_shinfo(skb)->frags[0].page = (void *)~0UL;
+ }
+
+ __skb_queue_tail(&netbk->tx_queue, skb);
+
+ netbk->pending_cons++;
+
+ request_gop = xen_netbk_get_requests(netbk, vif,
+ skb, txfrags, gop);
+ if (request_gop == NULL) {
+ kfree_skb(skb);
+ netbk_tx_err(vif, &txreq, idx);
+ continue;
+ }
+ gop = request_gop;
+
+ vif->tx.req_cons = idx;
+ xen_netbk_check_rx_xenvif(vif);
+
+ if ((gop-netbk->tx_copy_ops) >= ARRAY_SIZE(netbk->tx_copy_ops))
+ break;
+ }
+
+ return gop - netbk->tx_copy_ops;
+}
+
+static void xen_netbk_tx_submit(struct xen_netbk *netbk)
+{
+ struct gnttab_copy *gop = netbk->tx_copy_ops;
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&netbk->tx_queue)) != NULL) {
+ struct xen_netif_tx_request *txp;
+ struct xenvif *vif;
+ u16 pending_idx;
+ unsigned data_len;
+
+ pending_idx = *((u16 *)skb->data);
+ vif = netbk->pending_tx_info[pending_idx].vif;
+ txp = &netbk->pending_tx_info[pending_idx].req;
+
+ /* Check the remap error code. */
+ if (unlikely(xen_netbk_tx_check_gop(netbk, skb, &gop))) {
+ netdev_dbg(vif->dev, "netback grant failed.\n");
+ skb_shinfo(skb)->nr_frags = 0;
+ kfree_skb(skb);
+ continue;
+ }
+
+ data_len = skb->len;
+ memcpy(skb->data,
+ (void *)(idx_to_kaddr(netbk, pending_idx)|txp->offset),
+ data_len);
+ if (data_len < txp->size) {
+ /* Append the packet payload as a fragment. */
+ txp->offset += data_len;
+ txp->size -= data_len;
+ } else {
+ /* Schedule a response immediately. */
+ xen_netbk_idx_release(netbk, pending_idx);
+ }
+
+ if (txp->flags & XEN_NETTXF_csum_blank)
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ else if (txp->flags & XEN_NETTXF_data_validated)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ xen_netbk_fill_frags(netbk, skb);
+
+ /*
+ * If the initial fragment was < PKT_PROT_LEN then
+ * pull through some bytes from the other fragments to
+ * increase the linear region to PKT_PROT_LEN bytes.
+ */
+ if (skb_headlen(skb) < PKT_PROT_LEN && skb_is_nonlinear(skb)) {
+ int target = min_t(int, skb->len, PKT_PROT_LEN);
+ __pskb_pull_tail(skb, target - skb_headlen(skb));
+ }
+
+ skb->dev = vif->dev;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ if (checksum_setup(vif, skb)) {
+ netdev_dbg(vif->dev,
+ "Can't setup checksum in net_tx_action\n");
+ kfree_skb(skb);
+ continue;
+ }
+
+ vif->dev->stats.rx_bytes += skb->len;
+ vif->dev->stats.rx_packets++;
+
+ xenvif_receive_skb(vif, skb);
+ }
+}
+
+/* Called after netfront has transmitted */
+static void xen_netbk_tx_action(struct xen_netbk *netbk)
+{
+ unsigned nr_gops;
+ int ret;
+
+ nr_gops = xen_netbk_tx_build_gops(netbk);
+
+ if (nr_gops == 0)
+ return;
+ ret = HYPERVISOR_grant_table_op(GNTTABOP_copy,
+ netbk->tx_copy_ops, nr_gops);
+ BUG_ON(ret);
+
+ xen_netbk_tx_submit(netbk);
+
+}
+
+static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx)
+{
+ struct xenvif *vif;
+ struct pending_tx_info *pending_tx_info;
+ pending_ring_idx_t index;
+
+ /* Already complete? */
+ if (netbk->mmap_pages[pending_idx] == NULL)
+ return;
+
+ pending_tx_info = &netbk->pending_tx_info[pending_idx];
+
+ vif = pending_tx_info->vif;
+
+ make_tx_response(vif, &pending_tx_info->req, XEN_NETIF_RSP_OKAY);
+
+ index = pending_index(netbk->pending_prod++);
+ netbk->pending_ring[index] = pending_idx;
+
+ xenvif_put(vif);
+
+ netbk->mmap_pages[pending_idx]->mapping = 0;
+ put_page(netbk->mmap_pages[pending_idx]);
+ netbk->mmap_pages[pending_idx] = NULL;
+}
+
+static void make_tx_response(struct xenvif *vif,
+ struct xen_netif_tx_request *txp,
+ s8 st)
+{
+ RING_IDX i = vif->tx.rsp_prod_pvt;
+ struct xen_netif_tx_response *resp;
+ int notify;
+
+ resp = RING_GET_RESPONSE(&vif->tx, i);
+ resp->id = txp->id;
+ resp->status = st;
+
+ if (txp->flags & XEN_NETTXF_extra_info)
+ RING_GET_RESPONSE(&vif->tx, ++i)->status = XEN_NETIF_RSP_NULL;
+
+ vif->tx.rsp_prod_pvt = ++i;
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->tx, notify);
+ if (notify)
+ notify_remote_via_irq(vif->irq);
+}
+
+static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
+ u16 id,
+ s8 st,
+ u16 offset,
+ u16 size,
+ u16 flags)
+{
+ RING_IDX i = vif->rx.rsp_prod_pvt;
+ struct xen_netif_rx_response *resp;
+
+ resp = RING_GET_RESPONSE(&vif->rx, i);
+ resp->offset = offset;
+ resp->flags = flags;
+ resp->id = id;
+ resp->status = (s16)size;
+ if (st < 0)
+ resp->status = (s16)st;
+
+ vif->rx.rsp_prod_pvt = ++i;
+
+ return resp;
+}
+
+static inline int rx_work_todo(struct xen_netbk *netbk)
+{
+ return !skb_queue_empty(&netbk->rx_queue);
+}
+
+static inline int tx_work_todo(struct xen_netbk *netbk)
+{
+
+ if (((nr_pending_reqs(netbk) + MAX_SKB_FRAGS) < MAX_PENDING_REQS) &&
+ !list_empty(&netbk->net_schedule_list))
+ return 1;
+
+ return 0;
+}
+
+static int xen_netbk_kthread(void *data)
+{
+ struct xen_netbk *netbk = data;
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(netbk->wq,
+ rx_work_todo(netbk) ||
+ tx_work_todo(netbk) ||
+ kthread_should_stop());
+ cond_resched();
+
+ if (kthread_should_stop())
+ break;
+
+ if (rx_work_todo(netbk))
+ xen_netbk_rx_action(netbk);
+
+ if (tx_work_todo(netbk))
+ xen_netbk_tx_action(netbk);
+ }
+
+ return 0;
+}
+
+void xen_netbk_unmap_frontend_rings(struct xenvif *vif)
+{
+ struct gnttab_unmap_grant_ref op;
+
+ if (vif->tx.sring) {
+ gnttab_set_unmap_op(&op, (unsigned long)vif->tx_comms_area->addr,
+ GNTMAP_host_map, vif->tx_shmem_handle);
+
+ if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
+ BUG();
+ }
+
+ if (vif->rx.sring) {
+ gnttab_set_unmap_op(&op, (unsigned long)vif->rx_comms_area->addr,
+ GNTMAP_host_map, vif->rx_shmem_handle);
+
+ if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
+ BUG();
+ }
+ if (vif->rx_comms_area)
+ free_vm_area(vif->rx_comms_area);
+ if (vif->tx_comms_area)
+ free_vm_area(vif->tx_comms_area);
+}
+
+int xen_netbk_map_frontend_rings(struct xenvif *vif,
+ grant_ref_t tx_ring_ref,
+ grant_ref_t rx_ring_ref)
+{
+ struct gnttab_map_grant_ref op;
+ struct xen_netif_tx_sring *txs;
+ struct xen_netif_rx_sring *rxs;
+
+ int err = -ENOMEM;
+
+ vif->tx_comms_area = alloc_vm_area(PAGE_SIZE);
+ if (vif->tx_comms_area == NULL)
+ goto err;
+
+ vif->rx_comms_area = alloc_vm_area(PAGE_SIZE);
+ if (vif->rx_comms_area == NULL)
+ goto err;
+
+ gnttab_set_map_op(&op, (unsigned long)vif->tx_comms_area->addr,
+ GNTMAP_host_map, tx_ring_ref, vif->domid);
+
+ if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
+ BUG();
+
+ if (op.status) {
+ netdev_warn(vif->dev,
+ "failed to map tx ring. err=%d status=%d\n",
+ err, op.status);
+ err = op.status;
+ goto err;
+ }
+
+ vif->tx_shmem_ref = tx_ring_ref;
+ vif->tx_shmem_handle = op.handle;
+
+ txs = (struct xen_netif_tx_sring *)vif->tx_comms_area->addr;
+ BACK_RING_INIT(&vif->tx, txs, PAGE_SIZE);
+
+ gnttab_set_map_op(&op, (unsigned long)vif->rx_comms_area->addr,
+ GNTMAP_host_map, rx_ring_ref, vif->domid);
+
+ if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
+ BUG();
+
+ if (op.status) {
+ netdev_warn(vif->dev,
+ "failed to map rx ring. err=%d status=%d\n",
+ err, op.status);
+ err = op.status;
+ goto err;
+ }
+
+ vif->rx_shmem_ref = rx_ring_ref;
+ vif->rx_shmem_handle = op.handle;
+ vif->rx_req_cons_peek = 0;
+
+ rxs = (struct xen_netif_rx_sring *)vif->rx_comms_area->addr;
+ BACK_RING_INIT(&vif->rx, rxs, PAGE_SIZE);
+
+ return 0;
+
+err:
+ xen_netbk_unmap_frontend_rings(vif);
+ return err;
+}
+
+static int __init netback_init(void)
+{
+ int i;
+ int rc = 0;
+ int group;
+
+ if (!xen_pv_domain())
+ return -ENODEV;
+
+ xen_netbk_group_nr = num_online_cpus();
+ xen_netbk = vzalloc(sizeof(struct xen_netbk) * xen_netbk_group_nr);
+ if (!xen_netbk) {
+ printk(KERN_ALERT "%s: out of memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (group = 0; group < xen_netbk_group_nr; group++) {
+ struct xen_netbk *netbk = &xen_netbk[group];
+ skb_queue_head_init(&netbk->rx_queue);
+ skb_queue_head_init(&netbk->tx_queue);
+
+ init_timer(&netbk->net_timer);
+ netbk->net_timer.data = (unsigned long)netbk;
+ netbk->net_timer.function = xen_netbk_alarm;
+
+ netbk->pending_cons = 0;
+ netbk->pending_prod = MAX_PENDING_REQS;
+ for (i = 0; i < MAX_PENDING_REQS; i++)
+ netbk->pending_ring[i] = i;
+
+ init_waitqueue_head(&netbk->wq);
+ netbk->task = kthread_create(xen_netbk_kthread,
+ (void *)netbk,
+ "netback/%u", group);
+
+ if (IS_ERR(netbk->task)) {
+ printk(KERN_ALERT "kthread_run() fails at netback\n");
+ del_timer(&netbk->net_timer);
+ rc = PTR_ERR(netbk->task);
+ goto failed_init;
+ }
+
+ kthread_bind(netbk->task, group);
+
+ INIT_LIST_HEAD(&netbk->net_schedule_list);
+
+ spin_lock_init(&netbk->net_schedule_list_lock);
+
+ atomic_set(&netbk->netfront_count, 0);
+
+ wake_up_process(netbk->task);
+ }
+
+ rc = xenvif_xenbus_init();
+ if (rc)
+ goto failed_init;
+
+ return 0;
+
+failed_init:
+ while (--group >= 0) {
+ struct xen_netbk *netbk = &xen_netbk[group];
+ for (i = 0; i < MAX_PENDING_REQS; i++) {
+ if (netbk->mmap_pages[i])
+ __free_page(netbk->mmap_pages[i]);
+ }
+ del_timer(&netbk->net_timer);
+ kthread_stop(netbk->task);
+ }
+ vfree(xen_netbk);
+ return rc;
+
+}
+
+module_init(netback_init);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
new file mode 100644
index 0000000..22b8c35
--- /dev/null
+++ b/drivers/net/xen-netback/xenbus.c
@@ -0,0 +1,490 @@
+/*
+ * Xenbus code for netif backend
+ *
+ * Copyright (C) 2005 Rusty Russell <rusty@rustcorp.com.au>
+ * Copyright (C) 2005 XenSource Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "common.h"
+
+struct backend_info {
+ struct xenbus_device *dev;
+ struct xenvif *vif;
+ enum xenbus_state frontend_state;
+ struct xenbus_watch hotplug_status_watch;
+ int have_hotplug_status_watch:1;
+};
+
+static int connect_rings(struct backend_info *);
+static void connect(struct backend_info *);
+static void backend_create_xenvif(struct backend_info *be);
+static void unregister_hotplug_status_watch(struct backend_info *be);
+
+static int netback_remove(struct xenbus_device *dev)
+{
+ struct backend_info *be = dev_get_drvdata(&dev->dev);
+
+ unregister_hotplug_status_watch(be);
+ if (be->vif) {
+ kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
+ xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status");
+ xenvif_disconnect(be->vif);
+ be->vif = NULL;
+ }
+ kfree(be);
+ dev_set_drvdata(&dev->dev, NULL);
+ return 0;
+}
+
+
+/**
+ * Entry point to this code when a new device is created. Allocate the basic
+ * structures and switch to InitWait.
+ */
+static int netback_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ const char *message;
+ struct xenbus_transaction xbt;
+ int err;
+ int sg;
+ struct backend_info *be = kzalloc(sizeof(struct backend_info),
+ GFP_KERNEL);
+ if (!be) {
+ xenbus_dev_fatal(dev, -ENOMEM,
+ "allocating backend structure");
+ return -ENOMEM;
+ }
+
+ be->dev = dev;
+ dev_set_drvdata(&dev->dev, be);
+
+ sg = 1;
+
+ do {
+ err = xenbus_transaction_start(&xbt);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "starting transaction");
+ goto fail;
+ }
+
+ err = xenbus_printf(xbt, dev->nodename, "feature-sg", "%d", sg);
+ if (err) {
+ message = "writing feature-sg";
+ goto abort_transaction;
+ }
+
+ err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv4",
+ "%d", sg);
+ if (err) {
+ message = "writing feature-gso-tcpv4";
+ goto abort_transaction;
+ }
+
+ /* We support rx-copy path. */
+ err = xenbus_printf(xbt, dev->nodename,
+ "feature-rx-copy", "%d", 1);
+ if (err) {
+ message = "writing feature-rx-copy";
+ goto abort_transaction;
+ }
+
+ /*
+ * We don't support rx-flip path (except old guests who don't
+ * grok this feature flag).
+ */
+ err = xenbus_printf(xbt, dev->nodename,
+ "feature-rx-flip", "%d", 0);
+ if (err) {
+ message = "writing feature-rx-flip";
+ goto abort_transaction;
+ }
+
+ err = xenbus_transaction_end(xbt, 0);
+ } while (err == -EAGAIN);
+
+ if (err) {
+ xenbus_dev_fatal(dev, err, "completing transaction");
+ goto fail;
+ }
+
+ err = xenbus_switch_state(dev, XenbusStateInitWait);
+ if (err)
+ goto fail;
+
+ /* This kicks hotplug scripts, so do it immediately. */
+ backend_create_xenvif(be);
+
+ return 0;
+
+abort_transaction:
+ xenbus_transaction_end(xbt, 1);
+ xenbus_dev_fatal(dev, err, "%s", message);
+fail:
+ pr_debug("failed");
+ netback_remove(dev);
+ return err;
+}
+
+
+/*
+ * Handle the creation of the hotplug script environment. We add the script
+ * and vif variables to the environment, for the benefit of the vif-* hotplug
+ * scripts.
+ */
+static int netback_uevent(struct xenbus_device *xdev,
+ struct kobj_uevent_env *env)
+{
+ struct backend_info *be = dev_get_drvdata(&xdev->dev);
+ char *val;
+
+ val = xenbus_read(XBT_NIL, xdev->nodename, "script", NULL);
+ if (IS_ERR(val)) {
+ int err = PTR_ERR(val);
+ xenbus_dev_fatal(xdev, err, "reading script");
+ return err;
+ } else {
+ if (add_uevent_var(env, "script=%s", val)) {
+ kfree(val);
+ return -ENOMEM;
+ }
+ kfree(val);
+ }
+
+ if (!be || !be->vif)
+ return 0;
+
+ return add_uevent_var(env, "vif=%s", be->vif->dev->name);
+}
+
+
+static void backend_create_xenvif(struct backend_info *be)
+{
+ int err;
+ long handle;
+ struct xenbus_device *dev = be->dev;
+
+ if (be->vif != NULL)
+ return;
+
+ err = xenbus_scanf(XBT_NIL, dev->nodename, "handle", "%li", &handle);
+ if (err != 1) {
+ xenbus_dev_fatal(dev, err, "reading handle");
+ return;
+ }
+
+ be->vif = xenvif_alloc(&dev->dev, dev->otherend_id, handle);
+ if (IS_ERR(be->vif)) {
+ err = PTR_ERR(be->vif);
+ be->vif = NULL;
+ xenbus_dev_fatal(dev, err, "creating interface");
+ return;
+ }
+
+ kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE);
+}
+
+
+static void disconnect_backend(struct xenbus_device *dev)
+{
+ struct backend_info *be = dev_get_drvdata(&dev->dev);
+
+ if (be->vif) {
+ xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status");
+ xenvif_disconnect(be->vif);
+ be->vif = NULL;
+ }
+}
+
+/**
+ * Callback received when the frontend's state changes.
+ */
+static void frontend_changed(struct xenbus_device *dev,
+ enum xenbus_state frontend_state)
+{
+ struct backend_info *be = dev_get_drvdata(&dev->dev);
+
+ pr_debug("frontend state %s", xenbus_strstate(frontend_state));
+
+ be->frontend_state = frontend_state;
+
+ switch (frontend_state) {
+ case XenbusStateInitialising:
+ if (dev->state == XenbusStateClosed) {
+ printk(KERN_INFO "%s: %s: prepare for reconnect\n",
+ __func__, dev->nodename);
+ xenbus_switch_state(dev, XenbusStateInitWait);
+ }
+ break;
+
+ case XenbusStateInitialised:
+ break;
+
+ case XenbusStateConnected:
+ if (dev->state == XenbusStateConnected)
+ break;
+ backend_create_xenvif(be);
+ if (be->vif)
+ connect(be);
+ break;
+
+ case XenbusStateClosing:
+ if (be->vif)
+ kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
+ disconnect_backend(dev);
+ xenbus_switch_state(dev, XenbusStateClosing);
+ break;
+
+ case XenbusStateClosed:
+ xenbus_switch_state(dev, XenbusStateClosed);
+ if (xenbus_dev_is_online(dev))
+ break;
+ /* fall through if not online */
+ case XenbusStateUnknown:
+ device_unregister(&dev->dev);
+ break;
+
+ default:
+ xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+ frontend_state);
+ break;
+ }
+}
+
+
+static void xen_net_read_rate(struct xenbus_device *dev,
+ unsigned long *bytes, unsigned long *usec)
+{
+ char *s, *e;
+ unsigned long b, u;
+ char *ratestr;
+
+ /* Default to unlimited bandwidth. */
+ *bytes = ~0UL;
+ *usec = 0;
+
+ ratestr = xenbus_read(XBT_NIL, dev->nodename, "rate", NULL);
+ if (IS_ERR(ratestr))
+ return;
+
+ s = ratestr;
+ b = simple_strtoul(s, &e, 10);
+ if ((s == e) || (*e != ','))
+ goto fail;
+
+ s = e + 1;
+ u = simple_strtoul(s, &e, 10);
+ if ((s == e) || (*e != '\0'))
+ goto fail;
+
+ *bytes = b;
+ *usec = u;
+
+ kfree(ratestr);
+ return;
+
+ fail:
+ pr_warn("Failed to parse network rate limit. Traffic unlimited.\n");
+ kfree(ratestr);
+}
+
+static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[])
+{
+ char *s, *e, *macstr;
+ int i;
+
+ macstr = s = xenbus_read(XBT_NIL, dev->nodename, "mac", NULL);
+ if (IS_ERR(macstr))
+ return PTR_ERR(macstr);
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ mac[i] = simple_strtoul(s, &e, 16);
+ if ((s == e) || (*e != ((i == ETH_ALEN-1) ? '\0' : ':'))) {
+ kfree(macstr);
+ return -ENOENT;
+ }
+ s = e+1;
+ }
+
+ kfree(macstr);
+ return 0;
+}
+
+static void unregister_hotplug_status_watch(struct backend_info *be)
+{
+ if (be->have_hotplug_status_watch) {
+ unregister_xenbus_watch(&be->hotplug_status_watch);
+ kfree(be->hotplug_status_watch.node);
+ }
+ be->have_hotplug_status_watch = 0;
+}
+
+static void hotplug_status_changed(struct xenbus_watch *watch,
+ const char **vec,
+ unsigned int vec_size)
+{
+ struct backend_info *be = container_of(watch,
+ struct backend_info,
+ hotplug_status_watch);
+ char *str;
+ unsigned int len;
+
+ str = xenbus_read(XBT_NIL, be->dev->nodename, "hotplug-status", &len);
+ if (IS_ERR(str))
+ return;
+ if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) {
+ xenbus_switch_state(be->dev, XenbusStateConnected);
+ /* Not interested in this watch anymore. */
+ unregister_hotplug_status_watch(be);
+ }
+ kfree(str);
+}
+
+static void connect(struct backend_info *be)
+{
+ int err;
+ struct xenbus_device *dev = be->dev;
+
+ err = connect_rings(be);
+ if (err)
+ return;
+
+ err = xen_net_read_mac(dev, be->vif->fe_dev_addr);
+ if (err) {
+ xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
+ return;
+ }
+
+ xen_net_read_rate(dev, &be->vif->credit_bytes,
+ &be->vif->credit_usec);
+ be->vif->remaining_credit = be->vif->credit_bytes;
+
+ unregister_hotplug_status_watch(be);
+ err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch,
+ hotplug_status_changed,
+ "%s/%s", dev->nodename, "hotplug-status");
+ if (err) {
+ /* Switch now, since we can't do a watch. */
+ xenbus_switch_state(dev, XenbusStateConnected);
+ } else {
+ be->have_hotplug_status_watch = 1;
+ }
+
+ netif_wake_queue(be->vif->dev);
+}
+
+
+static int connect_rings(struct backend_info *be)
+{
+ struct xenvif *vif = be->vif;
+ struct xenbus_device *dev = be->dev;
+ unsigned long tx_ring_ref, rx_ring_ref;
+ unsigned int evtchn, rx_copy;
+ int err;
+ int val;
+
+ err = xenbus_gather(XBT_NIL, dev->otherend,
+ "tx-ring-ref", "%lu", &tx_ring_ref,
+ "rx-ring-ref", "%lu", &rx_ring_ref,
+ "event-channel", "%u", &evtchn, NULL);
+ if (err) {
+ xenbus_dev_fatal(dev, err,
+ "reading %s/ring-ref and event-channel",
+ dev->otherend);
+ return err;
+ }
+
+ err = xenbus_scanf(XBT_NIL, dev->otherend, "request-rx-copy", "%u",
+ &rx_copy);
+ if (err == -ENOENT) {
+ err = 0;
+ rx_copy = 0;
+ }
+ if (err < 0) {
+ xenbus_dev_fatal(dev, err, "reading %s/request-rx-copy",
+ dev->otherend);
+ return err;
+ }
+ if (!rx_copy)
+ return -EOPNOTSUPP;
+
+ if (vif->dev->tx_queue_len != 0) {
+ if (xenbus_scanf(XBT_NIL, dev->otherend,
+ "feature-rx-notify", "%d", &val) < 0)
+ val = 0;
+ if (val)
+ vif->can_queue = 1;
+ else
+ /* Must be non-zero for pfifo_fast to work. */
+ vif->dev->tx_queue_len = 1;
+ }
+
+ if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-sg",
+ "%d", &val) < 0)
+ val = 0;
+ vif->can_sg = !!val;
+
+ if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv4",
+ "%d", &val) < 0)
+ val = 0;
+ vif->gso = !!val;
+
+ if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv4-prefix",
+ "%d", &val) < 0)
+ val = 0;
+ vif->gso_prefix = !!val;
+
+ if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-no-csum-offload",
+ "%d", &val) < 0)
+ val = 0;
+ vif->csum = !val;
+
+ /* Map the shared frame, irq etc. */
+ err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref, evtchn);
+ if (err) {
+ xenbus_dev_fatal(dev, err,
+ "mapping shared-frames %lu/%lu port %u",
+ tx_ring_ref, rx_ring_ref, evtchn);
+ return err;
+ }
+ return 0;
+}
+
+
+/* ** Driver Registration ** */
+
+
+static const struct xenbus_device_id netback_ids[] = {
+ { "vif" },
+ { "" }
+};
+
+
+static struct xenbus_driver netback = {
+ .name = "vif",
+ .owner = THIS_MODULE,
+ .ids = netback_ids,
+ .probe = netback_probe,
+ .remove = netback_remove,
+ .uevent = netback_uevent,
+ .otherend_changed = frontend_changed,
+};
+
+int xenvif_xenbus_init(void)
+{
+ return xenbus_register_backend(&netback);
+}
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 546de57..31c1e78 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -356,7 +356,7 @@ static void xennet_tx_buf_gc(struct net_device *dev)
struct xen_netif_tx_response *txrsp;
txrsp = RING_GET_RESPONSE(&np->tx, cons);
- if (txrsp->status == NETIF_RSP_NULL)
+ if (txrsp->status == XEN_NETIF_RSP_NULL)
continue;
id = txrsp->id;
@@ -413,7 +413,7 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
larger than a page), split it it into page-sized chunks. */
while (len > PAGE_SIZE - offset) {
tx->size = PAGE_SIZE - offset;
- tx->flags |= NETTXF_more_data;
+ tx->flags |= XEN_NETTXF_more_data;
len -= tx->size;
data += tx->size;
offset = 0;
@@ -439,7 +439,7 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
for (i = 0; i < frags; i++) {
skb_frag_t *frag = skb_shinfo(skb)->frags + i;
- tx->flags |= NETTXF_more_data;
+ tx->flags |= XEN_NETTXF_more_data;
id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs);
np->tx_skbs[id].skb = skb_get(skb);
@@ -514,10 +514,10 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx->flags = 0;
if (skb->ip_summed == CHECKSUM_PARTIAL)
/* local packet? */
- tx->flags |= NETTXF_csum_blank | NETTXF_data_validated;
+ tx->flags |= XEN_NETTXF_csum_blank | XEN_NETTXF_data_validated;
else if (skb->ip_summed == CHECKSUM_UNNECESSARY)
/* remote but checksummed. */
- tx->flags |= NETTXF_data_validated;
+ tx->flags |= XEN_NETTXF_data_validated;
if (skb_shinfo(skb)->gso_size) {
struct xen_netif_extra_info *gso;
@@ -528,7 +528,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (extra)
extra->flags |= XEN_NETIF_EXTRA_FLAG_MORE;
else
- tx->flags |= NETTXF_extra_info;
+ tx->flags |= XEN_NETTXF_extra_info;
gso->u.gso.size = skb_shinfo(skb)->gso_size;
gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4;
@@ -648,7 +648,7 @@ static int xennet_get_responses(struct netfront_info *np,
int err = 0;
unsigned long ret;
- if (rx->flags & NETRXF_extra_info) {
+ if (rx->flags & XEN_NETRXF_extra_info) {
err = xennet_get_extras(np, extras, rp);
cons = np->rx.rsp_cons;
}
@@ -685,7 +685,7 @@ static int xennet_get_responses(struct netfront_info *np,
__skb_queue_tail(list, skb);
next:
- if (!(rx->flags & NETRXF_more_data))
+ if (!(rx->flags & XEN_NETRXF_more_data))
break;
if (cons + frags == rp) {
@@ -950,9 +950,9 @@ err:
skb->truesize += skb->data_len - (RX_COPY_THRESHOLD - len);
skb->len += skb->data_len;
- if (rx->flags & NETRXF_csum_blank)
+ if (rx->flags & XEN_NETRXF_csum_blank)
skb->ip_summed = CHECKSUM_PARTIAL;
- else if (rx->flags & NETRXF_data_validated)
+ else if (rx->flags & XEN_NETRXF_data_validated)
skb->ip_summed = CHECKSUM_UNNECESSARY;
__skb_queue_tail(&rxq, skb);
diff --git a/include/xen/interface/io/netif.h b/include/xen/interface/io/netif.h
index 518481c..cb94668 100644
--- a/include/xen/interface/io/netif.h
+++ b/include/xen/interface/io/netif.h
@@ -22,50 +22,50 @@
/*
* This is the 'wire' format for packets:
- * Request 1: netif_tx_request -- NETTXF_* (any flags)
- * [Request 2: netif_tx_extra] (only if request 1 has NETTXF_extra_info)
- * [Request 3: netif_tx_extra] (only if request 2 has XEN_NETIF_EXTRA_MORE)
- * Request 4: netif_tx_request -- NETTXF_more_data
- * Request 5: netif_tx_request -- NETTXF_more_data
+ * Request 1: xen_netif_tx_request -- XEN_NETTXF_* (any flags)
+ * [Request 2: xen_netif_extra_info] (only if request 1 has XEN_NETTXF_extra_info)
+ * [Request 3: xen_netif_extra_info] (only if request 2 has XEN_NETIF_EXTRA_MORE)
+ * Request 4: xen_netif_tx_request -- XEN_NETTXF_more_data
+ * Request 5: xen_netif_tx_request -- XEN_NETTXF_more_data
* ...
- * Request N: netif_tx_request -- 0
+ * Request N: xen_netif_tx_request -- 0
*/
/* Protocol checksum field is blank in the packet (hardware offload)? */
-#define _NETTXF_csum_blank (0)
-#define NETTXF_csum_blank (1U<<_NETTXF_csum_blank)
+#define _XEN_NETTXF_csum_blank (0)
+#define XEN_NETTXF_csum_blank (1U<<_XEN_NETTXF_csum_blank)
/* Packet data has been validated against protocol checksum. */
-#define _NETTXF_data_validated (1)
-#define NETTXF_data_validated (1U<<_NETTXF_data_validated)
+#define _XEN_NETTXF_data_validated (1)
+#define XEN_NETTXF_data_validated (1U<<_XEN_NETTXF_data_validated)
/* Packet continues in the next request descriptor. */
-#define _NETTXF_more_data (2)
-#define NETTXF_more_data (1U<<_NETTXF_more_data)
+#define _XEN_NETTXF_more_data (2)
+#define XEN_NETTXF_more_data (1U<<_XEN_NETTXF_more_data)
/* Packet to be followed by extra descriptor(s). */
-#define _NETTXF_extra_info (3)
-#define NETTXF_extra_info (1U<<_NETTXF_extra_info)
+#define _XEN_NETTXF_extra_info (3)
+#define XEN_NETTXF_extra_info (1U<<_XEN_NETTXF_extra_info)
struct xen_netif_tx_request {
grant_ref_t gref; /* Reference to buffer page */
uint16_t offset; /* Offset within buffer page */
- uint16_t flags; /* NETTXF_* */
+ uint16_t flags; /* XEN_NETTXF_* */
uint16_t id; /* Echoed in response message. */
uint16_t size; /* Packet size in bytes. */
};
-/* Types of netif_extra_info descriptors. */
-#define XEN_NETIF_EXTRA_TYPE_NONE (0) /* Never used - invalid */
-#define XEN_NETIF_EXTRA_TYPE_GSO (1) /* u.gso */
-#define XEN_NETIF_EXTRA_TYPE_MAX (2)
+/* Types of xen_netif_extra_info descriptors. */
+#define XEN_NETIF_EXTRA_TYPE_NONE (0) /* Never used - invalid */
+#define XEN_NETIF_EXTRA_TYPE_GSO (1) /* u.gso */
+#define XEN_NETIF_EXTRA_TYPE_MAX (2)
-/* netif_extra_info flags. */
-#define _XEN_NETIF_EXTRA_FLAG_MORE (0)
-#define XEN_NETIF_EXTRA_FLAG_MORE (1U<<_XEN_NETIF_EXTRA_FLAG_MORE)
+/* xen_netif_extra_info flags. */
+#define _XEN_NETIF_EXTRA_FLAG_MORE (0)
+#define XEN_NETIF_EXTRA_FLAG_MORE (1U<<_XEN_NETIF_EXTRA_FLAG_MORE)
/* GSO types - only TCPv4 currently supported. */
-#define XEN_NETIF_GSO_TYPE_TCPV4 (1)
+#define XEN_NETIF_GSO_TYPE_TCPV4 (1)
/*
* This structure needs to fit within both netif_tx_request and
@@ -107,7 +107,7 @@ struct xen_netif_extra_info {
struct xen_netif_tx_response {
uint16_t id;
- int16_t status; /* NETIF_RSP_* */
+ int16_t status; /* XEN_NETIF_RSP_* */
};
struct xen_netif_rx_request {
@@ -116,25 +116,29 @@ struct xen_netif_rx_request {
};
/* Packet data has been validated against protocol checksum. */
-#define _NETRXF_data_validated (0)
-#define NETRXF_data_validated (1U<<_NETRXF_data_validated)
+#define _XEN_NETRXF_data_validated (0)
+#define XEN_NETRXF_data_validated (1U<<_XEN_NETRXF_data_validated)
/* Protocol checksum field is blank in the packet (hardware offload)? */
-#define _NETRXF_csum_blank (1)
-#define NETRXF_csum_blank (1U<<_NETRXF_csum_blank)
+#define _XEN_NETRXF_csum_blank (1)
+#define XEN_NETRXF_csum_blank (1U<<_XEN_NETRXF_csum_blank)
/* Packet continues in the next request descriptor. */
-#define _NETRXF_more_data (2)
-#define NETRXF_more_data (1U<<_NETRXF_more_data)
+#define _XEN_NETRXF_more_data (2)
+#define XEN_NETRXF_more_data (1U<<_XEN_NETRXF_more_data)
/* Packet to be followed by extra descriptor(s). */
-#define _NETRXF_extra_info (3)
-#define NETRXF_extra_info (1U<<_NETRXF_extra_info)
+#define _XEN_NETRXF_extra_info (3)
+#define XEN_NETRXF_extra_info (1U<<_XEN_NETRXF_extra_info)
+
+/* GSO Prefix descriptor. */
+#define _XEN_NETRXF_gso_prefix (4)
+#define XEN_NETRXF_gso_prefix (1U<<_XEN_NETRXF_gso_prefix)
struct xen_netif_rx_response {
uint16_t id;
uint16_t offset; /* Offset in page of start of received packet */
- uint16_t flags; /* NETRXF_* */
+ uint16_t flags; /* XEN_NETRXF_* */
int16_t status; /* -ve: BLKIF_RSP_* ; +ve: Rx'ed pkt size. */
};
@@ -149,10 +153,10 @@ DEFINE_RING_TYPES(xen_netif_rx,
struct xen_netif_rx_request,
struct xen_netif_rx_response);
-#define NETIF_RSP_DROPPED -2
-#define NETIF_RSP_ERROR -1
-#define NETIF_RSP_OKAY 0
-/* No response: used for auxiliary requests (e.g., netif_tx_extra). */
-#define NETIF_RSP_NULL 1
+#define XEN_NETIF_RSP_DROPPED -2
+#define XEN_NETIF_RSP_ERROR -1
+#define XEN_NETIF_RSP_OKAY 0
+/* No response: used for auxiliary requests (e.g., xen_netif_extra_info). */
+#define XEN_NETIF_RSP_NULL 1
#endif
^ permalink raw reply related
* [PATCH net-next-2.6] pktgen: bug fix in transmission headers with frags=0
From: Daniel Turull @ 2011-03-14 14:31 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev, Robert Olsson, Jens Laas, Voravit Tanyingyong
In-Reply-To: <1300112338.3423.27.camel@edumazet-laptop>
(bug introduced by commit 26ad787962ef84677a48c560
(pktgen: speedup fragmented skbs)
The headers of pktgen were incorrectly added in a pktgen packet
without frags (frags=0). There was an offset in the pktgen headers.
The cause was in reusing the pgh variable as a return variable in skb_put
when adding the payload to the skb.
Signed-off-by: Daniel Turull <daniel.turull@gmail.com>
---
The PKTGEN magic (be9b e955) now starts in the correct offset.
Before the patch, it was starting with an offset of +16 bytes
(sizeof(struct pktgen_hdr)) (be9b)
Capture from tcpdump:
before patch:
14:57:37.854812 IP 10.0.0.2.discard > 10.254.254.84.discard: UDP, length 18
0x0000: 001b 2157 ed84 001b 215d 01d0 0800 4500
0x0010: 002e 0004 0000 2011 8767 0a00 0002 0afe
0x0020: fe54 0009 0009 001a 0000 0000 0000 b072
0x0030: 9102 00ea ffff 0010 0000 be9b
after patch:
14:44:32.896048 IP 10.0.0.2.discard > 10.217.234.56.discard: UDP, length 18
0x0000: 001b 2157 ed84 001b 215d 01d0 0800 4500
0x0010: 002e 0000 0000 2011 9bac 0a00 0002 0ad9
0x0020: ea38 0009 0009 001a 0000 be9b e955 0000
0x0030: 0001 4d7e 1b09 0005 5f23 af00
---
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index f0aec6c..0c55eaa 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2620,8 +2620,7 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
datalen -= sizeof(*pgh);
if (pkt_dev->nfrags <= 0) {
- pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
- memset(pgh + 1, 0, datalen);
+ memset(skb_put(skb, datalen), 0, datalen);
} else {
int frags = pkt_dev->nfrags;
int i, len;
^ permalink raw reply related
* Re: [PATCH net-next-2.6] pktgen: bug fix in transmission headers with frags=0
From: Eric Dumazet @ 2011-03-14 14:35 UTC (permalink / raw)
To: Daniel Turull; +Cc: netdev, Robert Olsson, Jens Laas, Voravit Tanyingyong
In-Reply-To: <4D7E26A4.2070802@gmail.com>
Le lundi 14 mars 2011 à 15:31 +0100, Daniel Turull a écrit :
> (bug introduced by commit 26ad787962ef84677a48c560
> (pktgen: speedup fragmented skbs)
>
> The headers of pktgen were incorrectly added in a pktgen packet
> without frags (frags=0). There was an offset in the pktgen headers.
>
> The cause was in reusing the pgh variable as a return variable in skb_put
> when adding the payload to the skb.
>
> Signed-off-by: Daniel Turull <daniel.turull@gmail.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Thanks !
^ permalink raw reply
* Re: [RFC v3 1/6] can: extend sockaddr_can to include j1939 members
From: Kurt Van Dijck @ 2011-03-14 14:53 UTC (permalink / raw)
To: Eric Dumazet
Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1300112100.3423.24.camel@edumazet-laptop>
On Mon, Mar 14, 2011 at 03:15:00PM +0100, Eric Dumazet wrote:
> > +/*
> > + * required_size
> > + * macro to find the minimum size of a struct
> > + * that includes a requested member
> > + */
> > +#define required_size(member, struct_type) \
> > + (offsetof(typeof(struct_type), member) + \
> > + sizeof(((typeof(struct_type) *)(0))->member))
> > +
>
> Hmm, most similar macros reverse arguments.
>
> (For example : offsetof())
I had 'list_entry' in mind.
>
> What about
>
> #define required_size(TYPE, MEMBER) ...
I'm not against that ...
If I receive no complaint for that, I can alter this in the next series.
>
>
Thanks,
Kurt
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox