#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/core.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 "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$");
static __initdata const char banner[] = KERN_INFO
module_param(stats_timer, int, S_IRUGO);
MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
-#ifdef CONFIG_CAN_DEBUG_CORE
-#define DBG_PREFIX "can"
-#define DBG_VAR can_debug
-static int can_debug __read_mostly;
-module_param_named(debug, can_debug, int, S_IRUGO);
-MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
-#endif
-
-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] __read_mostly;
-static DEFINE_SPINLOCK(proto_tab_lock);
+static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_MUTEX(proto_tab_lock);
-struct timer_list stattimer; /* timer for statistics update */
-struct s_stats stats; /* packet statistics */
-struct s_pstats pstats; /* receive list statistics */
-
-#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 int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
#endif
}
}
+EXPORT_SYMBOL(can_ioctl);
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)
#endif
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+static const struct can_proto *can_try_module_get(int protocol)
+{
+ const struct can_proto *cp;
+
+ rcu_read_lock();
+ cp = rcu_dereference(proto_tab[protocol]);
+#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
+ rcu_read_unlock();
+
+ return cp;
+}
+
+#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[sizeof("can-proto-000")];
- int ret = 0;
-
- DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+ const struct can_proto *cp;
+ int err = 0;
sock->state = SS_UNCONNECTED;
return -EAFNOSUPPORT;
#endif
- DBG("looking up proto %d in proto_tab[]\n", protocol);
+ cp = can_try_module_get(protocol);
+
+#ifdef CONFIG_MODULES
+ if (!cp) {
+ /* try to load protocol module if kernel is modular */
- /* try to load protocol module, when CONFIG_KMOD is defined */
- if (!proto_tab[protocol]) {
- sprintf(module_name, "can-proto-%d", protocol);
- ret = request_module(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 (ret == -ENOSYS) {
- if (printk_ratelimit())
- printk(KERN_INFO "can: request_module(%s)"
- " not implemented.\n", module_name);
- } else if (ret) {
- if (printk_ratelimit())
- printk(KERN_ERR "can: request_module(%s)"
- " failed.\n", module_name);
- }
- }
+ if (err && printk_ratelimit())
+ printk(KERN_ERR "can: request_module "
+ "(can-proto-%d) failed.\n", protocol);
- spin_lock(&proto_tab_lock);
- cp = proto_tab[protocol];
-#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;
+ cp = can_try_module_get(protocol);
+ }
#endif
- spin_unlock(&proto_tab_lock);
/* check for available protocol and correct usage */
return -EPROTONOSUPPORT;
if (cp->type != sock->type) {
- ret = -EPROTONOSUPPORT;
+ err = -EPROTOTYPE;
goto errout;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
if (cp->capability >= 0 && !capable(cp->capability)) {
- ret = -EPERM;
+ err = -EPERM;
goto errout;
}
-
+#endif
sock->ops = cp->ops;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
- sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
+ 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);
#else
sk = sk_alloc(PF_CAN, GFP_KERNEL, 1, 0);
#endif
if (!sk) {
- ret = -ENOMEM;
+ err = -ENOMEM;
goto errout;
}
sk->sk_protinfo = kmalloc(cp->obj_size, GFP_KERNEL);
if (!sk->sk_protinfo) {
sk_free(sk);
- ret = -ENOMEM;
+ err = -ENOMEM;
goto errout;
}
}
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);
#else
module_put(cp->owner);
#endif
- return ret;
+ 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;
}
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 */
* If the interface is not capable to do loopback
* itself, we do it here.
*/
- struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
-
+ newskb = skb_clone(skb, GFP_ATOMIC);
if (!newskb) {
kfree_skb(skb);
return -ENOMEM;
newskb->sk = skb->sk;
newskb->ip_summed = CHECKSUM_UNNECESSARY;
newskb->pkt_type = PACKET_BROADCAST;
- netif_rx(newskb);
}
} else {
/* indication for the CAN driver: no loopback required */
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_rcu(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[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;
if (!(*mask))
return &d->rx[RX_ALL];
- /* 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[RX_EFF];
+ /* 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)) {
+
+ 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 */
* 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 (%s), id %03X, mask %03X, callback %p, data %p, "
- "ident %s\n", dev, DNAME(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(&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(&rcv_lists_lock);
+ spin_unlock(&can_rcvlists_lock);
- return ret;
+ return err;
}
EXPORT_SYMBOL(can_rx_register);
{
struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
- DBG("removing dev_rcv_list at %p\n", d);
kfree(d);
}
{
struct receiver *r = container_of(rp, struct receiver, rcu);
- DBG("removing receiver at %p\n", r);
kmem_cache_free(rcv_cache, r);
}
struct hlist_node *next;
struct dev_rcv_lists *d;
- DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
- dev, DNAME(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(&rcv_lists_lock);
+ spin_lock(&can_rcvlists_lock);
d = find_dev_rcv_lists(dev);
if (!d) {
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) {
- DBG("removing dev_rcv_list for %s on zero entries\n",
- dev->name);
+#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(&rcv_lists_lock);
+ spin_unlock(&can_rcvlists_lock);
/* schedule the receiver item for deletion */
if (r)
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) {
- clone->sk = skb->sk;
- 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)
/* check for error frame entries only */
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[RX_ALL], list) {
- DBG("match on rx_all skbuff %p\n", skb);
deliver(skb, r);
matches++;
}
/* check for can_id/mask entries */
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[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[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("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,24)
- if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+#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
- if (dev->type != ARPHRD_CAN) {
+ BUG_ON(dev->type != ARPHRD_CAN ||
+ skb->len != sizeof(struct can_frame) ||
+ cf->can_dlc > 8);
#endif
- kfree_skb(skb);
- return 0;
- }
/* 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;
+ return NET_RX_SUCCESS;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+#endif
}
/*
* -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;
return -EINVAL;
}
- spin_lock(&proto_tab_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ return err;
+#endif
+
+ mutex_lock(&proto_tab_lock);
+
if (proto_tab[proto]) {
printk(KERN_ERR "can: protocol %d already registered\n",
proto);
err = -EBUSY;
- goto errout;
- }
+ } else
+ rcu_assign_pointer(proto_tab[proto], cp);
+
+ mutex_unlock(&proto_tab_lock);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
- err = proto_register(cp->prot, 0);
if (err < 0)
- goto errout;
+ proto_unregister(cp->prot);
#endif
- proto_tab[proto] = cp;
-
- /* use generic ioctl function if the module doesn't bring its own */
- if (!cp->ops->ioctl)
- cp->ops->ioctl = can_ioctl;
-
- errout:
- spin_unlock(&proto_tab_lock);
-
return err;
}
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;
- spin_lock(&proto_tab_lock);
- if (!proto_tab[proto]) {
- printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
- proto);
- }
+ mutex_lock(&proto_tab_lock);
+ BUG_ON(proto_tab[proto] != cp);
+ rcu_assign_pointer(proto_tab[proto], NULL);
+ mutex_unlock(&proto_tab_lock);
+
+ synchronize_rcu();
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
proto_unregister(cp->prot);
#endif
- proto_tab[proto] = NULL;
- spin_unlock(&proto_tab_lock);
}
EXPORT_SYMBOL(can_proto_unregister);
struct net_device *dev = (struct net_device *)data;
struct dev_rcv_lists *d;
- DBG("msg %ld for dev %p (%s idx %d)\n",
- msg, dev, dev->name, dev->ifindex);
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#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
* explicit initialization.
*/
- DBG("creating new dev_rcv_lists for %s\n", dev->name);
-
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d) {
printk(KERN_ERR
}
d->dev = dev;
- spin_lock(&rcv_lists_lock);
- hlist_add_head_rcu(&d->list, &rx_dev_list);
- spin_unlock(&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(&rcv_lists_lock);
+ spin_lock(&can_rcvlists_lock);
d = find_dev_rcv_lists(dev);
if (d) {
- DBG("remove dev_rcv_list for %s (%d entries)\n",
- dev->name, d->entries);
-
if (d->entries) {
d->remove_on_zero_entries = 1;
d = NULL;
- } else
+ } 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(&rcv_lists_lock);
+ spin_unlock(&can_rcvlists_lock);
if (d)
call_rcu(&d->rcu, can_rx_delete_device);
return NOTIFY_DONE;
}
-/*
- * af_can debugging stuff
- */
-
-#ifdef CONFIG_CAN_DEBUG_CORE
-
-/**
- * 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)
-{
- char idbuf[12];
- char hexbuf[28];
- int dlc;
-
- dlc = cf->can_dlc;
- if (dlc > 8)
- dlc = 8;
-
- if (cf->can_id & CAN_EFF_FLAG)
- sprintf(idbuf, "<%08X>", cf->can_id & CAN_EFF_MASK);
- else
- sprintf(idbuf, "<%03X>", cf->can_id & CAN_SFF_MASK);
-
- if (cf->can_id & CAN_RTR_FLAG)
- sprintf(hexbuf, "(RTR)");
- else
- hex_dump_to_buffer(cf->data, dlc, 16, 1, hexbuf, 28, 0);
-
- printk(KERN_DEBUG "%s: %s [%d] %s\n", msg, idbuf, dlc, hexbuf);
-}
-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)
-{
- printk(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\n",
- 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);
-
- print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE,
- 16, 1, skb->head, skb->end - skb->head, 0);
-}
-EXPORT_SYMBOL(can_debug_skb);
-
-#endif
-
/*
* af_can module init/exit functions
*/
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,
};
return -ENOMEM;
/*
- * Insert rx_alldev_list for reception on all devices.
+ * 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(&rcv_lists_lock);
- hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
- spin_unlock(&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 = round_jiffies(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
- stattimer.function = NULL;
+ can_stattimer.function = NULL;
can_init_proc();
struct hlist_node *n, *next;
if (stats_timer)
- del_timer(&stattimer);
+ del_timer(&can_stattimer);
can_remove_proc();
unregister_netdevice_notifier(&can_netdev_notifier);
sock_unregister(PF_CAN);
- /* remove rx_dev_list */
- spin_lock(&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(&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);
}