* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, the following disclaimer and
- * the referenced file 'COPYING'.
+ * notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Alternatively, provided that this notice is retained in full, this
* software may be distributed under the terms of the GNU General
- * Public License ("GPL") version 2 as distributed in the 'COPYING'
- * file from the main directory of the linux kernel source.
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
*
* The provided data structures and external interfaces from this code
* are not restricted to be used by modules with a GPL compatible license.
*
*/
-#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/version.h>
-#include <linux/slab.h>
-#include <linux/kmod.h>
#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
+#include <linux/uaccess.h>
+#else
+#include <asm/uaccess.h>
+#endif
+#include <linux/net.h>
+#include <linux/netdevice.h>
#include <linux/socket.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
+#include <socketcan/can.h>
+#include <socketcan/can/core.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#include <net/net_namespace.h>
+#endif
#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <linux/can.h>
-#include <linux/can/core.h>
#include "af_can.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#include "compat.h"
+#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
-#define IDENT "core"
-static __initdata const char banner[] =
- KERN_INFO "can: controller area network core # "
- CAN_VERSION_STRING "\n";
+static __initdata const char banner[] = KERN_INFO
+ "can: controller area network core (" CAN_VERSION_STRING ")\n";
MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS_NETPROTO(PF_CAN);
-int stats_timer = 1; /* default: on */
+static int stats_timer __read_mostly = 1;
module_param(stats_timer, int, S_IRUGO);
MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
-#ifdef CONFIG_CAN_DEBUG_CORE
-static int debug = 0;
-module_param(debug, int, S_IRUGO);
-MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
-#endif
-
-struct notifier {
- struct list_head list;
- struct net_device *dev;
- void (*func)(unsigned long msg, void *data);
- void *data;
-};
-
-static LIST_HEAD(notifier_list);
-static DEFINE_RWLOCK(notifier_lock);
-
-HLIST_HEAD(rx_dev_list);
-static struct dev_rcv_lists rx_alldev_list;
-static DEFINE_SPINLOCK(rcv_lists_lock);
+HLIST_HEAD(can_rx_dev_list);
+static struct dev_rcv_lists can_rx_alldev_list;
+static DEFINE_SPINLOCK(can_rcvlists_lock);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static struct kmem_cache *rcv_cache __read_mostly;
#endif
/* table of registered CAN protocols */
-static struct can_proto *proto_tab[CAN_NPROTO];
-
-struct timer_list stattimer; /* timer for statistics update */
-struct s_stats stats; /* packet statistics */
-struct s_pstats pstats; /* receive list statistics */
+static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_SPINLOCK(proto_tab_lock);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
-static void *kzalloc(size_t size, unsigned int __nocast flags)
-{
- void *ret = kmalloc(size, flags);
- if (ret)
- memset(ret, 0, size);
- return ret;
-}
-#endif
+struct timer_list can_stattimer; /* timer for statistics update */
+struct s_stats can_stats; /* packet statistics */
+struct s_pstats can_pstats; /* receive list statistics */
/*
* af_can socket functions
static void can_sock_destruct(struct sock *sk)
{
- DBG("called for sock %p\n", sk);
-
skb_queue_purge(&sk->sk_receive_queue);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
if (sk->sk_protinfo)
kfree(sk->sk_protinfo);
+#endif
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)
+static int can_create(struct net *net, struct socket *sock, int protocol, int kern)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+static int can_create(struct net *net, struct socket *sock, int protocol)
+#else
static int can_create(struct socket *sock, int protocol)
+#endif
{
struct sock *sk;
struct can_proto *cp;
- char module_name[30];
- int ret = 0;
-
- DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+ int err = 0;
sock->state = SS_UNCONNECTED;
if (protocol < 0 || protocol >= CAN_NPROTO)
return -EINVAL;
- DBG("looking up proto %d in proto_tab[]\n", protocol);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+#endif
- /* try to load protocol module, when CONFIG_KMOD is defined */
+#ifdef CONFIG_MODULES
+ /* try to load protocol module kernel is modular */
if (!proto_tab[protocol]) {
- sprintf(module_name, "can-proto-%d", protocol);
- if (request_module(module_name) == -ENOSYS)
- printk(KERN_INFO "can: request_module(%s) not"
- " implemented.\n", module_name);
+ 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
- /* check for success and correct type */
+ spin_lock(&proto_tab_lock);
cp = proto_tab[protocol];
- if (!cp || cp->type != sock->type)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = NULL;
+#else
+ if (cp && !try_module_get(cp->owner))
+ cp = NULL;
+#endif
+ spin_unlock(&proto_tab_lock);
+
+ /* check for available protocol and correct usage */
+
+ if (!cp)
return -EPROTONOSUPPORT;
- if (cp->capability >= 0 && !capable(cp->capability))
- return -EPERM;
+ if (cp->type != sock->type) {
+ err = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+ if (cp->capability >= 0 && !capable(cp->capability)) {
+ err = -EPERM;
+ goto errout;
+ }
sock->ops = cp->ops;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
- if (!sk)
- return -ENOMEM;
#else
sk = sk_alloc(PF_CAN, GFP_KERNEL, 1, 0);
- if (!sk)
- return -ENOMEM;
+#endif
+ if (!sk) {
+ err = -ENOMEM;
+ goto errout;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
if (cp->obj_size) {
sk->sk_protinfo = kmalloc(cp->obj_size, GFP_KERNEL);
if (!sk->sk_protinfo) {
sk_free(sk);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto errout;
}
}
sk_set_owner(sk, proto_tab[protocol]->owner);
sock_init_data(sock, sk);
sk->sk_destruct = can_sock_destruct;
- DBG("created sock: %p\n", sk);
-
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
if (sk->sk_prot->init)
- ret = sk->sk_prot->init(sk);
+ err = sk->sk_prot->init(sk);
#else
if (cp->init)
- ret = cp->init(sk);
+ err = cp->init(sk);
#endif
- if (ret) {
+ if (err) {
/* release sk on errors */
sock_orphan(sk);
sock_put(sk);
}
- return ret;
+ errout:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ module_put(cp->prot->owner);
+#else
+ module_put(cp->owner);
+#endif
+ return err;
}
/*
* @skb: pointer to socket buffer with CAN frame in data section
* @loop: loopback for listeners on local CAN sockets (recommended default!)
*
+ * Due to the loopback this routine must not be called from hardirq context.
+ *
* Return:
* 0 on success
* -ENETDOWN when the selected interface is down
* -ENOBUFS on full driver queue (see net_xmit_errno())
+ * -ENOMEM when local loopback failed at calling skb_clone()
+ * -EPERM when trying to send on a non-CAN interface
+ * -EINVAL when the skb->data does not contain a valid CAN frame
*/
int can_send(struct sk_buff *skb, int loop)
{
+ struct sk_buff *newskb = NULL;
+ struct can_frame *cf = (struct can_frame *)skb->data;
int err;
+ if (skb->len != sizeof(struct can_frame) || cf->can_dlc > 8) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+#else
+ skb->nh.raw = skb->data;
+ skb->h.raw = skb->data;
+#endif
+
if (loop) {
- /* local loopback of sent CAN frames (default) */
+ /* local loopback of sent CAN frames */
/* indication for the CAN driver: do loopback */
- *(struct sock **)skb->cb = skb->sk;
+ skb->pkt_type = PACKET_LOOPBACK;
/*
- * The reference to the originating sock may be also required
- * by the receiving socket to indicate (and ignore) his own
- * sent data. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
+ * The reference to the originating sock may be required
+ * by the receiving socket to check whether the frame is
+ * its own. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
+ * Therefore we have to ensure that skb->sk remains the
+ * reference to the originating sock by restoring skb->sk
+ * after each skb_clone() or skb_orphan() usage.
*/
- /* interface not capabable to do the loopback itself? */
- if (!(skb->dev->flags & IFF_LOOPBACK)) {
- struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#define IFF_ECHO IFF_LOOPBACK
+#endif
+ if (!(skb->dev->flags & IFF_ECHO)) {
+ /*
+ * If the interface is not capable to do loopback
+ * itself, we do it here.
+ */
+ newskb = skb_clone(skb, GFP_ATOMIC);
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
- /* perform the local loopback here */
- newskb->protocol = htons(ETH_P_CAN);
+ newskb->sk = skb->sk;
newskb->ip_summed = CHECKSUM_UNNECESSARY;
- netif_rx(newskb);
+ newskb->pkt_type = PACKET_BROADCAST;
}
} else {
/* indication for the CAN driver: no loopback required */
- *(struct sock **)skb->cb = NULL;
+ skb->pkt_type = PACKET_HOST;
}
- if (!(skb->dev->flags & IFF_UP))
- return -ENETDOWN;
-
/* send to netdevice */
err = dev_queue_xmit(skb);
if (err > 0)
err = net_xmit_errno(err);
+ if (err) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
+ /* kfree_skb() does not check for !NULL on older kernels */
+ if (newskb)
+ kfree_skb(newskb);
+#else
+ kfree_skb(newskb);
+#endif
+ return err;
+ }
+
+ if (newskb)
+ netif_rx_ni(newskb);
+
/* update statistics */
- stats.tx_frames++;
- stats.tx_frames_delta++;
+ can_stats.tx_frames++;
+ can_stats.tx_frames_delta++;
- return err;
+ return 0;
}
EXPORT_SYMBOL(can_send);
* af_can rx path
*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
{
- struct dev_rcv_lists *d;
+ /*
+ * find receive list for this device
+ *
+ * Since 2.6.26 a new "midlevel private" ml_priv pointer has been
+ * introduced in struct net_device. We use this pointer to omit the
+ * linear walk through the can_rx_dev_list. A similar speedup has been
+ * queued for 2.6.34 mainline but using the new netdev_rcu lists.
+ * Therefore the can_rx_dev_list is still needed (e.g. in proc.c)
+ */
+
+ /* dev == NULL is the indicator for the 'all' filterlist */
+ if (!dev)
+ return &can_rx_alldev_list;
+ else
+ return (struct dev_rcv_lists *)dev->ml_priv;
+}
+#else
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d = NULL;
struct hlist_node *n;
/*
* cursor variable n to decide if a match was found.
*/
- hlist_for_each_entry(d, n, &rx_dev_list, list) {
+ hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) {
if (d->dev == dev)
break;
}
return n ? d : NULL;
}
+#endif
+/**
+ * find_rcv_list - determine optimal filterlist inside device filter struct
+ * @can_id: pointer to CAN identifier of a given can_filter
+ * @mask: pointer to CAN mask of a given can_filter
+ * @d: pointer to the device filter struct
+ *
+ * Description:
+ * Returns the optimal filterlist to reduce the filter handling in the
+ * receive path. This function is called by service functions that need
+ * to register or unregister a can_filter in the filter lists.
+ *
+ * A filter matches in general, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * so every bit set in the mask (even CAN_EFF_FLAG, CAN_RTR_FLAG) describe
+ * relevant bits for the filter.
+ *
+ * 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). For error frames
+ * there is a special filterlist and a special rx path filter handling.
+ *
+ * Return:
+ * Pointer to optimal filterlist for the given can_id/mask pair.
+ * Constistency checked mask.
+ * Reduced can_id to have a preprocessed filter compare value.
+ */
static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
struct dev_rcv_lists *d)
{
canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
- /* filter error frames */
+ /* filter for error frames in extra filterlist */
if (*mask & CAN_ERR_FLAG) {
- /* clear CAN_ERR_FLAG in list entry */
+ /* clear CAN_ERR_FLAG in filter entry */
*mask &= CAN_ERR_MASK;
- return &d->rx_err;
+ return &d->rx[RX_ERR];
}
- /* ensure valid values in can_mask */
- if (*mask & CAN_EFF_FLAG)
- *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
- else
- *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+ /* with cleared CAN_ERR_FLAG we have a simple mask/value filterpair */
+
+#define CAN_EFF_RTR_FLAGS (CAN_EFF_FLAG | CAN_RTR_FLAG)
+
+ /* ensure valid values in can_mask for 'SFF only' frame filtering */
+ if ((*mask & CAN_EFF_FLAG) && !(*can_id & CAN_EFF_FLAG))
+ *mask &= (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS);
/* reduce condition testing at receive time */
*can_id &= *mask;
/* inverse can_id/can_mask filter */
if (inv)
- return &d->rx_inv;
+ return &d->rx[RX_INV];
/* mask == 0 => no condition testing at receive time */
if (!(*mask))
- return &d->rx_all;
+ return &d->rx[RX_ALL];
+
+ /* extra filterlists for the subscription of a single non-RTR can_id */
+ if (((*mask & CAN_EFF_RTR_FLAGS) == CAN_EFF_RTR_FLAGS)
+ && !(*can_id & CAN_RTR_FLAG)) {
- /* use extra filterset for the subscription of exactly *ONE* can_id */
- if (*can_id & CAN_EFF_FLAG) {
- if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
- /* RFC: a use-case for hash-tables in the future? */
- return &d->rx_eff;
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) {
+ /* RFC: a future use-case for hash-tables? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))
+ return &d->rx_sff[*can_id];
}
- } else {
- if (*mask == CAN_SFF_MASK)
- return &d->rx_sff[*can_id];
}
/* default: filter via can_id/can_mask */
- return &d->rx_fil;
+ return &d->rx[RX_FIL];
}
/**
* 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).
*
+ * The provided pointer to the sk_buff is guaranteed to be valid as long as
+ * the callback function is running. The callback function must *not* free
+ * the given sk_buff while processing it's task. When the given sk_buff is
+ * needed after the end of the callback function it must be cloned inside
+ * the callback function with skb_clone().
+ *
* Return:
* 0 on success
* -ENOMEM on missing cache mem to create subscription entry
struct receiver *r;
struct hlist_head *rl;
struct dev_rcv_lists *d;
- int ret = 0;
+ int err = 0;
/* insert new receiver (dev,canid,mask) -> (func,data) */
- DBG("dev %p, id %03X, mask %03X, callback %p, data %p, ident %s\n",
- dev, can_id, mask, func, data, ident);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (dev && dev->type != ARPHRD_CAN)
+ return -ENODEV;
+#endif
r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
if (!r)
return -ENOMEM;
- spin_lock_bh(&rcv_lists_lock);
+ spin_lock(&can_rcvlists_lock);
d = find_dev_rcv_lists(dev);
if (d) {
hlist_add_head_rcu(&r->list, rl);
d->entries++;
- pstats.rcv_entries++;
- if (pstats.rcv_entries_max < pstats.rcv_entries)
- pstats.rcv_entries_max = pstats.rcv_entries;
+ can_pstats.rcv_entries++;
+ if (can_pstats.rcv_entries_max < can_pstats.rcv_entries)
+ can_pstats.rcv_entries_max = can_pstats.rcv_entries;
} else {
- DBG("receive list not found for dev %s, id %03X, mask %03X\n",
- DNAME(dev), can_id, mask);
kmem_cache_free(rcv_cache, r);
- ret = -ENODEV;
+ err = -ENODEV;
}
- spin_unlock_bh(&rcv_lists_lock);
+ spin_unlock(&can_rcvlists_lock);
- return ret;
+ return err;
}
EXPORT_SYMBOL(can_rx_register);
-static void can_rcv_lists_delete(struct rcu_head *rp)
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
{
struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
kfree(d);
}
-static void can_rx_delete(struct rcu_head *rp)
+/*
+ * can_rx_delete_receiver - rcu callback for single receiver entry removal
+ */
+static void can_rx_delete_receiver(struct rcu_head *rp)
{
struct receiver *r = container_of(rp, struct receiver, rcu);
kmem_cache_free(rcv_cache, r);
}
-static void can_rx_delete_all(struct hlist_head *rl)
-{
- struct receiver *r;
- struct hlist_node *n;
-
- hlist_for_each_entry_rcu(r, n, rl, list) {
- hlist_del_rcu(&r->list);
- call_rcu(&r->rcu, can_rx_delete);
- }
-}
-
/**
* can_rx_unregister - unsubscribe CAN frames from a specific interface
* @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
*
* Description:
* Removes subscription entry depending on given (subscription) values.
- *
- * Return:
- * 0 on success
- * -EINVAL on missing subscription entry
- * -ENODEV unknown device
*/
-int can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
- void (*func)(struct sk_buff *, void *), void *data)
+void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
+ void (*func)(struct sk_buff *, void *), void *data)
{
struct receiver *r = NULL;
struct hlist_head *rl;
struct hlist_node *next;
struct dev_rcv_lists *d;
- int ret = 0;
- DBG("dev %p, id %03X, mask %03X, callback %p, data %p\n",
- dev, can_id, mask, func, data);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (dev && dev->type != ARPHRD_CAN)
+ return;
+#endif
- spin_lock_bh(&rcv_lists_lock);
+ spin_lock(&can_rcvlists_lock);
d = find_dev_rcv_lists(dev);
if (!d) {
- DBG("receive list not found for dev %s, id %03X, mask %03X\n",
- DNAME(dev), can_id, mask);
- ret = -ENODEV;
+ printk(KERN_ERR "BUG: receive list not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
goto out;
}
* been registered before.
*/
- hlist_for_each_entry(r, next, rl, list) {
+ hlist_for_each_entry_rcu(r, next, rl, list) {
if (r->can_id == can_id && r->mask == mask
&& r->func == func && r->data == data)
break;
}
/*
- * Check for bug in CAN protocol implementations:
+ * Check for bugs in CAN protocol implementations:
* If no matching list item was found, the list cursor variable next
* will be NULL, while r will point to the last item of the list.
*/
if (!next) {
- DBG("receive list entry not found for "
- "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
- ret = -EINVAL;
+ printk(KERN_ERR "BUG: receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
r = NULL;
+ d = NULL;
goto out;
}
hlist_del_rcu(&r->list);
d->entries--;
- if (pstats.rcv_entries > 0)
- pstats.rcv_entries--;
+ if (can_pstats.rcv_entries > 0)
+ can_pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ dev->ml_priv = NULL;
+#endif
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
out:
- spin_unlock_bh(&rcv_lists_lock);
+ spin_unlock(&can_rcvlists_lock);
/* schedule the receiver item for deletion */
if (r)
- call_rcu(&r->rcu, can_rx_delete);
+ call_rcu(&r->rcu, can_rx_delete_receiver);
- return ret;
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
}
EXPORT_SYMBOL(can_rx_unregister);
static inline void deliver(struct sk_buff *skb, struct receiver *r)
{
- struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
-
- DBG("skbuff %p cloned to %p\n", skb, clone);
- if (clone) {
- r->func(clone, r->data);
- r->matches++;
- }
+ r->func(skb, r->data);
+ r->matches++;
}
static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
struct receiver *r;
struct hlist_node *n;
int matches = 0;
- struct can_frame *cf = (struct can_frame*)skb->data;
+ struct can_frame *cf = (struct can_frame *)skb->data;
canid_t can_id = cf->can_id;
if (d->entries == 0)
if (can_id & CAN_ERR_FLAG) {
/* check for error frame entries only */
- hlist_for_each_entry_rcu(r, n, &d->rx_err, list) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
if (can_id & r->mask) {
- DBG("match on rx_err skbuff %p\n", skb);
deliver(skb, r);
matches++;
}
}
/* check for unfiltered entries */
- hlist_for_each_entry_rcu(r, n, &d->rx_all, list) {
- DBG("match on rx_all skbuff %p\n", skb);
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
deliver(skb, r);
matches++;
}
/* check for can_id/mask entries */
- hlist_for_each_entry_rcu(r, n, &d->rx_fil, list) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
if ((can_id & r->mask) == r->can_id) {
- DBG("match on rx_fil skbuff %p\n", skb);
deliver(skb, r);
matches++;
}
}
/* check for inverted can_id/mask entries */
- hlist_for_each_entry_rcu(r, n, &d->rx_inv, list) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
if ((can_id & r->mask) != r->can_id) {
- DBG("match on rx_inv skbuff %p\n", skb);
deliver(skb, r);
matches++;
}
}
- /* check CAN_ID specific entries */
+ /* check filterlists for single non-RTR can_ids */
+ if (can_id & CAN_RTR_FLAG)
+ return matches;
+
if (can_id & CAN_EFF_FLAG) {
- hlist_for_each_entry_rcu(r, n, &d->rx_eff, list) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
if (r->can_id == can_id) {
- DBG("match on rx_eff skbuff %p\n", skb);
deliver(skb, r);
matches++;
}
} else {
can_id &= CAN_SFF_MASK;
hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
- DBG("match on rx_sff skbuff %p\n", skb);
deliver(skb, r);
matches++;
}
#endif
{
struct dev_rcv_lists *d;
+ struct can_frame *cf = (struct can_frame *)skb->data;
int matches;
- DBG("received skbuff on device %s, ptype %04x\n",
- dev->name, ntohs(pt->type));
- DBG_SKB(skb);
- DBG_FRAME("af_can: can_rcv: received CAN frame",
- (struct can_frame *)skb->data);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (!net_eq(dev_net(dev), &init_net))
+ goto drop;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (dev->nd_net != &init_net)
+ goto drop;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+ if (WARN_ONCE(dev->type != ARPHRD_CAN ||
+ skb->len != sizeof(struct can_frame) ||
+ cf->can_dlc > 8,
+ "PF_CAN: dropped non conform skbuf: "
+ "dev type %d, len %d, can_dlc %d\n",
+ dev->type, skb->len, cf->can_dlc))
+ goto drop;
+#else
+ BUG_ON(dev->type != ARPHRD_CAN ||
+ skb->len != sizeof(struct can_frame) ||
+ cf->can_dlc > 8);
+#endif
/* update statistics */
- stats.rx_frames++;
- stats.rx_frames_delta++;
+ can_stats.rx_frames++;
+ can_stats.rx_frames_delta++;
rcu_read_lock();
/* deliver the packet to sockets listening on all devices */
- matches = can_rcv_filter(&rx_alldev_list, skb);
+ matches = can_rcv_filter(&can_rx_alldev_list, skb);
/* find receive list for this device */
d = find_dev_rcv_lists(dev);
rcu_read_unlock();
- /* free the skbuff allocated by the netdevice driver */
- DBG("freeing skbuff %p\n", skb);
+ /* consume the skbuff allocated by the netdevice driver */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+ consume_skb(skb);
+#else
kfree_skb(skb);
+#endif
if (matches > 0) {
- stats.matches++;
- stats.matches_delta++;
+ can_stats.matches++;
+ can_stats.matches_delta++;
}
- return 0;
-}
-
-/*
- * af_can debugging stuff
- */
-
-#ifdef CONFIG_CAN_DEBUG_CORE
-
-#define DBG_BSIZE 1024
-
-/**
- * can_debug_cframe - print CAN frame
- * @msg: pointer to message printed before the given CAN frame
- * @cf: pointer to CAN frame
- */
-void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
-{
- va_list ap;
- int len;
- int dlc, i;
- char *buf;
-
- buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
- if (!buf)
- return;
-
- len = sprintf(buf, KERN_DEBUG);
- va_start(ap, cf);
- len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
- buf[len++] = ':';
- buf[len++] = ' ';
- va_end(ap);
-
- dlc = cf->can_dlc;
- if (dlc > 8)
- dlc = 8;
-
- if (cf->can_id & CAN_EFF_FLAG)
- len += sprintf(buf + len, "<%08X> [%X] ",
- cf->can_id & CAN_EFF_MASK, dlc);
- else
- len += sprintf(buf + len, "<%03X> [%X] ",
- cf->can_id & CAN_SFF_MASK, dlc);
-
- for (i = 0; i < dlc; i++)
- len += sprintf(buf + len, "%02X ", cf->data[i]);
-
- if (cf->can_id & CAN_RTR_FLAG)
- len += sprintf(buf + len, "(RTR)");
-
- buf[len++] = '\n';
- buf[len] = '\0';
- printk(buf);
- kfree(buf);
-}
-EXPORT_SYMBOL(can_debug_cframe);
-
-/**
- * can_debug_skb - print socket buffer content to kernel log
- * @skb: pointer to socket buffer
- */
-void can_debug_skb(struct sk_buff *skb)
-{
- int len, nbytes, i;
- char *buf;
-
- buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
- if (!buf)
- return;
-
- len = sprintf(buf,
- KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
- KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
- "h,d,t,e,l: %p %+d %+d %+d, %d",
- skb, skb->dev ? skb->dev->ifindex : -1,
- ntohs(skb->protocol),
- atomic_read(&skb->users),
- atomic_read(&(skb_shinfo(skb)->dataref)),
- skb_shinfo(skb)->nr_frags,
- skb->head, skb->data - skb->head,
- skb->tail - skb->head, skb->end - skb->head, skb->len);
- nbytes = skb->end - skb->head;
- for (i = 0; i < nbytes; i++) {
- if (i % 16 == 0)
- len += sprintf(buf + len, "\n" KERN_DEBUG " ");
- if (len < DBG_BSIZE - 16) {
- len += sprintf(buf + len, " %02x", skb->head[i]);
- } else {
- len += sprintf(buf + len, "...");
- break;
- }
- }
- buf[len++] = '\n';
- buf[len] = '\0';
- printk(buf);
- kfree(buf);
-}
-EXPORT_SYMBOL(can_debug_skb);
+ return NET_RX_SUCCESS;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
#endif
+}
/*
* af_can protocol functions
int err = 0;
if (proto < 0 || proto >= CAN_NPROTO) {
- printk(KERN_ERR "can: protocol number %d out "
- "of range\n", proto);
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
return -EINVAL;
}
- if (proto_tab[proto]) {
- printk(KERN_ERR "can: protocol %d already "
- "registered\n", proto);
- return -EBUSY;
- }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
err = proto_register(cp->prot, 0);
return err;
#endif
- proto_tab[proto] = cp;
+ spin_lock(&proto_tab_lock);
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ 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);
- /* use generic ioctl function if the module doesn't bring its own */
- if (!cp->ops->ioctl)
- cp->ops->ioctl = can_ioctl;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ if (err < 0)
+ proto_unregister(cp->prot);
+#endif
return err;
}
/**
* can_proto_unregister - unregister CAN transport protocol
* @cp: pointer to CAN protocol structure
- *
- * Return:
- * 0 on success
- * -ESRCH protocol number was not registered
*/
-int can_proto_unregister(struct can_proto *cp)
+void can_proto_unregister(struct can_proto *cp)
{
int proto = cp->protocol;
+ spin_lock(&proto_tab_lock);
if (!proto_tab[proto]) {
- printk(KERN_ERR "can: protocol %d is not registered\n", proto);
- return -ESRCH;
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
}
+ proto_tab[proto] = NULL;
+ spin_unlock(&proto_tab_lock);
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
proto_unregister(cp->prot);
#endif
- proto_tab[proto] = NULL;
-
- return 0;
}
EXPORT_SYMBOL(can_proto_unregister);
-/**
- * can_dev_register - subscribe notifier for CAN device status changes
- * @dev: pointer to netdevice
- * @func: callback function on status change
- * @data: returned parameter for callback function
- *
- * Description:
- * Invokes the callback function with the status 'msg' and the given
- * parameter 'data' on a status change of the given CAN network device.
- *
- * Return:
- * 0 on success
- * -ENOMEM on missing mem to create subscription entry
- * -ENODEV unknown device
- */
-int can_dev_register(struct net_device *dev,
- void (*func)(unsigned long msg, void *), void *data)
-{
- struct notifier *n;
-
- DBG("called for %s\n", dev->name);
-
- if (!dev || dev->type != ARPHRD_CAN)
- return -ENODEV;
-
- n = kmalloc(sizeof(*n), GFP_KERNEL);
- if (!n)
- return -ENOMEM;
-
- n->dev = dev;
- n->func = func;
- n->data = data;
-
- write_lock(¬ifier_lock);
- list_add(&n->list, ¬ifier_list);
- write_unlock(¬ifier_lock);
-
- return 0;
-}
-EXPORT_SYMBOL(can_dev_register);
-
-/**
- * can_dev_unregister - unsubscribe notifier for CAN device status changes
- * @dev: pointer to netdevice
- * @func: callback function on filter match
- * @data: returned parameter for callback function
- *
- * Description:
- * Removes subscription entry depending on given (subscription) values.
- *
- * Return:
- * 0 on success
- * -EINVAL on missing subscription entry
+/*
+ * af_can notifier to create/remove CAN netdevice specific structs
*/
-int can_dev_unregister(struct net_device *dev,
- void (*func)(unsigned long msg, void *), void *data)
-{
- struct notifier *n, *next;
- int ret = -EINVAL;
-
- DBG("called for %s\n", dev->name);
-
- write_lock(¬ifier_lock);
- list_for_each_entry_safe(n, next, ¬ifier_list, list) {
- if (n->dev == dev && n->func == func && n->data == data) {
- list_del(&n->list);
- kfree(n);
- ret = 0;
- break;
- }
- }
- write_unlock(¬ifier_lock);
-
- return ret;
-}
-EXPORT_SYMBOL(can_dev_unregister);
-
-static int can_notifier(struct notifier_block *nb,
- unsigned long msg, void *data)
+static int can_notifier(struct notifier_block *nb, unsigned long msg,
+ void *data)
{
struct net_device *dev = (struct net_device *)data;
- struct notifier *n;
struct dev_rcv_lists *d;
- int i;
- DBG("called for %s, msg = %lu\n", dev->name, msg);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (!net_eq(dev_net(dev), &init_net))
+ return NOTIFY_DONE;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (dev->nd_net != &init_net)
+ return NOTIFY_DONE;
+#endif
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
* explicit initialization.
*/
- DBG("creating new dev_rcv_lists for %s\n", dev->name);
-
- d = kzalloc(sizeof(*d),
- in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d) {
- printk(KERN_ERR "can: allocation of receive "
- "list failed\n");
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
return NOTIFY_DONE;
}
d->dev = dev;
- spin_lock_bh(&rcv_lists_lock);
- hlist_add_head_rcu(&d->list, &rx_dev_list);
- spin_unlock_bh(&rcv_lists_lock);
+ spin_lock(&can_rcvlists_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ BUG_ON(dev->ml_priv);
+ dev->ml_priv = d;
+#endif
+ hlist_add_head_rcu(&d->list, &can_rx_dev_list);
+ spin_unlock(&can_rcvlists_lock);
break;
case NETDEV_UNREGISTER:
- spin_lock_bh(&rcv_lists_lock);
+ spin_lock(&can_rcvlists_lock);
d = find_dev_rcv_lists(dev);
if (d) {
- hlist_del_rcu(&d->list);
-
- /* remove all receivers hooked at this netdevice */
- can_rx_delete_all(&d->rx_err);
- can_rx_delete_all(&d->rx_all);
- can_rx_delete_all(&d->rx_fil);
- can_rx_delete_all(&d->rx_inv);
- can_rx_delete_all(&d->rx_eff);
- for (i = 0; i < 2048; i++)
- can_rx_delete_all(&d->rx_sff[i]);
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ dev->ml_priv = NULL;
+#endif
+ hlist_del_rcu(&d->list);
+ }
} else
printk(KERN_ERR "can: notifier: receive list not "
"found for dev %s\n", dev->name);
- spin_unlock_bh(&rcv_lists_lock);
+ spin_unlock(&can_rcvlists_lock);
if (d)
- call_rcu(&d->rcu, can_rcv_lists_delete);
+ call_rcu(&d->rcu, can_rx_delete_device);
break;
}
- read_lock(¬ifier_lock);
- list_for_each_entry(n, ¬ifier_list, list) {
- if (n->dev == dev)
- n->func(msg, n->data);
- }
- read_unlock(¬ifier_lock);
-
return NOTIFY_DONE;
}
* af_can module init/exit functions
*/
-static struct packet_type can_packet = {
+static struct packet_type can_packet __read_mostly = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+ .type = cpu_to_be16(ETH_P_CAN),
+#else
.type = __constant_htons(ETH_P_CAN),
+#endif
.dev = NULL,
.func = can_rcv,
};
-static struct net_proto_family can_family_ops = {
+static struct net_proto_family can_family_ops __read_mostly = {
.family = PF_CAN,
.create = can_create,
.owner = THIS_MODULE,
};
/* notifier block for netdevice event */
-static struct notifier_block can_netdev_notifier = {
+static struct notifier_block can_netdev_notifier __read_mostly = {
.notifier_call = can_notifier,
};
{
printk(banner);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+#else
rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
0, 0, NULL, NULL);
+#endif
if (!rcv_cache)
return -ENOMEM;
/*
- * Insert struct dev_rcv_lists for reception on all devices.
- * This struct is zero initialized which is correct for the
+ * Insert can_rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
* embedded hlist heads, the dev pointer, and the entries counter.
*/
- spin_lock_bh(&rcv_lists_lock);
- hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
- spin_unlock_bh(&rcv_lists_lock);
+ spin_lock(&can_rcvlists_lock);
+ hlist_add_head_rcu(&can_rx_alldev_list.list, &can_rx_dev_list);
+ spin_unlock(&can_rcvlists_lock);
if (stats_timer) {
/* the statistics are updated every second (timer triggered) */
- init_timer(&stattimer);
- stattimer.function = can_stat_update;
- stattimer.data = 0;
- /* update every second */
- stattimer.expires = jiffies + HZ;
- /* start statistics timer */
- add_timer(&stattimer);
- }
+ setup_timer(&can_stattimer, can_stat_update, 0);
+ mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
+ } else
+ can_stattimer.function = NULL;
- /* procfs init */
can_init_proc();
/* protocol register */
struct hlist_node *n, *next;
if (stats_timer)
- del_timer(&stattimer);
+ del_timer(&can_stattimer);
- /* procfs remove */
can_remove_proc();
/* protocol unregister */
unregister_netdevice_notifier(&can_netdev_notifier);
sock_unregister(PF_CAN);
- /* remove rx_dev_list */
- spin_lock_bh(&rcv_lists_lock);
- hlist_del(&rx_alldev_list.list);
- hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ /* remove can_rx_dev_list */
+ spin_lock(&can_rcvlists_lock);
+ hlist_del(&can_rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &can_rx_dev_list, list) {
hlist_del(&d->list);
+ BUG_ON(d->entries);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ d->dev->ml_priv = NULL;
+#endif
kfree(d);
}
- spin_unlock_bh(&rcv_lists_lock);
+ spin_unlock(&can_rcvlists_lock);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ rcu_barrier(); /* Wait for completion of call_rcu()'s */
+#endif
kmem_cache_destroy(rcv_cache);
}