On Wed, 14 Nov 2007 13:13:41 +0100 Urs Thuermann <[EMAIL PROTECTED]> wrote:
> This patch adds the CAN core functionality but no protocols or drivers. > No protocol implementations are included here. They come as separate > patches. Protocol numbers are already in include/linux/can.h. > > Signed-off-by: Oliver Hartkopp <[EMAIL PROTECTED]> > Signed-off-by: Urs Thuermann <[EMAIL PROTECTED]> > > --- > include/linux/can.h | 111 +++++ > include/linux/can/core.h | 78 +++ > include/linux/can/error.h | 93 ++++ > net/Kconfig | 1 > net/Makefile | 1 > net/can/Kconfig | 25 + > net/can/Makefile | 6 > net/can/af_can.c | 975 > ++++++++++++++++++++++++++++++++++++++++++++++ > net/can/af_can.h | 120 +++++ > net/can/proc.c | 532 +++++++++++++++++++++++++ > 10 files changed, 1942 insertions(+) > Index: net-2.6.25/include/linux/can/core.h > =================================================================== > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ net-2.6.25/include/linux/can/core.h 2007-11-14 13:04:49.000000000 > +0100 > @@ -0,0 +1,78 @@ > +/* > + * linux/can/core.h > + * > + * Protoypes and definitions for CAN protocol modules using the PF_CAN core > + * > + * Authors: Oliver Hartkopp <[EMAIL PROTECTED]> > + * Urs Thuermann <[EMAIL PROTECTED]> > + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research > + * All rights reserved. > + * > + * Send feedback to <[EMAIL PROTECTED]> > + * > + */ > + > +#ifndef CAN_CORE_H > +#define CAN_CORE_H > + > +#include <linux/can.h> > +#include <linux/skbuff.h> > +#include <linux/netdevice.h> > + > +#define CAN_VERSION "20071027" > + > +/* increment this number each time you change some user-space interface */ > +#define CAN_ABI_VERSION "8" > + > +#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION > + > +#define DNAME(dev) ((dev) ? (dev)->name : "any") > + > +/** > + * struct can_proto - CAN protocol structure > + * @type: type argument in socket() syscall, e.g. SOCK_DGRAM. > + * @protocol: protocol number in socket() syscall. > + * @capability: capability needed to open the socket, or -1 for no > restriction. > + * @ops: pointer to struct proto_ops for sock->ops. > + * @prot: pointer to struct proto structure. > + */ > +struct can_proto { > + int type; > + int protocol; > + int capability; > + struct proto_ops *ops; > + struct proto *prot; > +}; > + > +/* 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_rx_register(struct net_device *dev, canid_t can_id, > + canid_t mask, > + void (*func)(struct sk_buff *, void *), > + void *data, char *ident); > + > +extern void can_rx_unregister(struct net_device *dev, canid_t can_id, > + canid_t mask, > + void (*func)(struct sk_buff *, void *), > + void *data); > + > +extern int can_send(struct sk_buff *skb, int loop); > + > +#ifdef CONFIG_CAN_DEBUG_CORE > +extern void can_debug_skb(struct sk_buff *skb); > +extern void can_debug_cframe(const char *msg, struct can_frame *cframe); > +#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \ > + KERN_DEBUG DBG_PREFIX ": %s: " fmt, \ > + __func__, ##args) : 0) > +#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0) > +#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0) > +#else > +#define DBG(fmt, args...) > +#define DBG_FRAME(fmt, cf) > +#define DBG_SKB(skb) > +#endif This non-standard debugging seems like it needs a better interface. Also, need paren's around (DBG_VAR & 1) and don't use UPPERCASE for variable names. > + > +#endif /* CAN_CORE_H */ > Index: net-2.6.25/net/Kconfig > =================================================================== > --- net-2.6.25.orig/net/Kconfig 2007-11-14 13:04:26.000000000 +0100 > +++ net-2.6.25/net/Kconfig 2007-11-14 13:04:49.000000000 +0100 > @@ -218,6 +218,7 @@ > endmenu > > source "net/ax25/Kconfig" > +source "net/can/Kconfig" > source "net/irda/Kconfig" > source "net/bluetooth/Kconfig" > source "net/rxrpc/Kconfig" > Index: net-2.6.25/net/Makefile > =================================================================== > --- net-2.6.25.orig/net/Makefile 2007-11-14 13:04:26.000000000 +0100 > +++ net-2.6.25/net/Makefile 2007-11-14 13:04:49.000000000 +0100 > @@ -34,6 +34,7 @@ > obj-$(CONFIG_NETROM) += netrom/ > obj-$(CONFIG_ROSE) += rose/ > obj-$(CONFIG_AX25) += ax25/ > +obj-$(CONFIG_CAN) += can/ > obj-$(CONFIG_IRDA) += irda/ > obj-$(CONFIG_BT) += bluetooth/ > obj-$(CONFIG_SUNRPC) += sunrpc/ > Index: net-2.6.25/net/can/Kconfig > =================================================================== > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ net-2.6.25/net/can/Kconfig 2007-11-14 13:04:49.000000000 +0100 > @@ -0,0 +1,25 @@ > +# > +# Controller Area Network (CAN) network layer core configuration > +# > + > +menuconfig CAN > + depends on NET > + tristate "CAN bus subsystem support" > + ---help--- > + Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial > + communications protocol which was developed by Bosch in > + 1991, mainly for automotive, but now widely used in marine > + (NMEA2000), industrial, and medical applications. > + More information on the CAN network protocol family PF_CAN > + is contained in <Documentation/networking/can.txt>. > + > + If you want CAN support you should say Y here and also to the > + specific driver for your controller(s) below. > + > +config CAN_DEBUG_CORE > + bool "CAN Core debugging messages" > + depends on CAN > + ---help--- > + Say Y here if you want the CAN core to produce a bunch of debug > + messages. Select this if you are having a problem with CAN > + support and want to see more of what is going on. > Index: net-2.6.25/net/can/Makefile > =================================================================== > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ net-2.6.25/net/can/Makefile 2007-11-14 13:04:49.000000000 +0100 > @@ -0,0 +1,6 @@ > +# > +# Makefile for the Linux Controller Area Network core. > +# > + > +obj-$(CONFIG_CAN) += can.o > +can-objs := af_can.o proc.o > Index: net-2.6.25/net/can/af_can.c > =================================================================== > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ net-2.6.25/net/can/af_can.c 2007-11-14 13:04:49.000000000 +0100 > @@ -0,0 +1,975 @@ > +/* > + * af_can.c - Protocol family CAN core module > + * (used by different CAN protocol modules) > + * > + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * 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 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. > + * 3. Neither the name of Volkswagen nor the names of its contributors > + * may be used to endorse or promote products derived from this software > + * without specific prior written permission. > + * > + * 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, 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. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > + * DAMAGE. > + * > + * Send feedback to <[EMAIL PROTECTED]> > + * > + */ > + > +#include <linux/module.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> > +#include <linux/uaccess.h> > +#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/can.h> > +#include <linux/can/core.h> > +#include <net/net_namespace.h> > +#include <net/sock.h> > + > +#include "af_can.h" > + > +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_AUTHOR("Urs Thuermann <[EMAIL PROTECTED]>, " > + "Oliver Hartkopp <[EMAIL PROTECTED]>"); > + > +MODULE_ALIAS_NETPROTO(PF_CAN); > + > +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 > +#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); Please either make rx_dev_list static or call it can_rx_dev_list to avoid name conflices. > +static struct dev_rcv_lists rx_alldev_list; > +static DEFINE_SPINLOCK(rcv_lists_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 DEFINE_SPINLOCK(proto_tab_lock); > + > +struct timer_list stattimer; /* timer for statistics update */ > +struct s_stats stats; /* packet statistics */ > +struct s_pstats pstats; /* receive list statistics */ More global variables without prefix. > +/* > + * af_can socket functions > + */ > + > +static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long > arg) > +{ > + struct sock *sk = sock->sk; > + > + switch (cmd) { > + > + case SIOCGSTAMP: > + return sock_get_timestamp(sk, (struct timeval __user *)arg); > + > + default: > + return -ENOIOCTLCMD; > + } > +} > + > +static void can_sock_destruct(struct sock *sk) > +{ > + DBG("called for sock %p\n", sk); > + > + skb_queue_purge(&sk->sk_receive_queue); > +} > + > +static int can_create(struct net *net, struct socket *sock, int protocol) > +{ > + struct sock *sk; > + struct can_proto *cp; > + char module_name[sizeof("can-proto-000")]; > + int err = 0; > + > + DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol); > + > + sock->state = SS_UNCONNECTED; > + > + if (protocol < 0 || protocol >= CAN_NPROTO) > + return -EINVAL; > + > + if (net != &init_net) > + return -EAFNOSUPPORT; > + > + DBG("looking up proto %d in proto_tab[]\n", protocol); > + > + /* try to load protocol module, when CONFIG_KMOD is defined */ > + if (!proto_tab[protocol]) { > + sprintf(module_name, "can-proto-%d", protocol); > + err = request_module(module_name); > + > + /* > + * In case of error we only print a message but don't > + * return the error code immediately. Below we will > + * return -EPROTONOSUPPORT > + */ > + if (err == -ENOSYS) { > + if (printk_ratelimit()) > + printk(KERN_INFO "can: request_module(%s)" > + " not implemented.\n", module_name); > + } else if (err) { > + if (printk_ratelimit()) > + printk(KERN_ERR "can: request_module(%s)" > + " failed.\n", module_name); > + } > + } > + > + spin_lock(&proto_tab_lock); > + cp = proto_tab[protocol]; > + if (cp && !try_module_get(cp->prot->owner)) > + cp = NULL; > + spin_unlock(&proto_tab_lock); > + > + /* check for available protocol and correct usage */ > + > + if (!cp) > + return -EPROTONOSUPPORT; > + > + if (cp->type != sock->type) { > + err = -EPROTONOSUPPORT; > + goto errout; > + } > + > + if (cp->capability >= 0 && !capable(cp->capability)) { > + err = -EPERM; > + goto errout; > + } > + > + sock->ops = cp->ops; > + > + sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot); > + if (!sk) { > + err = -ENOMEM; > + goto errout; > + } > + > + sock_init_data(sock, sk); > + sk->sk_destruct = can_sock_destruct; > + > + DBG("created sock: %p\n", sk); > + > + if (sk->sk_prot->init) > + err = sk->sk_prot->init(sk); > + > + if (err) { > + /* release sk on errors */ > + sock_orphan(sk); > + sock_put(sk); > + } > + > + errout: > + module_put(cp->prot->owner); > + return err; > +} > + > +/* > + * af_can tx path > + */ > + > +/** > + * can_send - transmit a CAN frame (optional with local loopback) > + * @skb: pointer to socket buffer with CAN frame in data section > + * @loop: loopback for listeners on local CAN sockets (recommended default!) > + * > + * 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 > + */ > +int can_send(struct sk_buff *skb, int loop) > +{ > + int err; > + > + 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); > + skb_reset_network_header(skb); > + skb_reset_transport_header(skb); > + > + if (loop) { > + /* local loopback of sent CAN frames */ > + > + /* indication for the CAN driver: do loopback */ > + skb->pkt_type = PACKET_LOOPBACK; > + > + /* > + * 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. > + */ > + > + if (!(skb->dev->flags & IFF_ECHO)) { > + /* > + * If the interface is not capable to do loopback > + * itself, we do it here. > + */ > + struct sk_buff *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 */ > + skb->pkt_type = PACKET_HOST; > + } > + > + /* send to netdevice */ > + err = dev_queue_xmit(skb); > + if (err > 0) > + err = net_xmit_errno(err); > + > + /* update statistics */ > + stats.tx_frames++; > + stats.tx_frames_delta++; > + > + return err; > +} > +EXPORT_SYMBOL(can_send); > + > +/* > + * af_can rx path > + */ > + > +static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev) > +{ > + struct dev_rcv_lists *d = NULL; > + struct hlist_node *n; > + > + /* > + * find receive list for this device > + * > + * The hlist_for_each_entry*() macros curse through the list > + * using the pointer variable n and set d to the containing > + * struct in each list iteration. Therefore, after list > + * iteration, d is unmodified when the list is empty, and it > + * points to last list element, when the list is non-empty > + * but no match in the loop body is found. I.e. d is *not* > + * NULL when no match is found. We can, however, use the > + * cursor variable n to decide if a match was found. > + */ > + > + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) { > + if (d->dev == dev) > + break; > + } > + > + return n ? d : NULL; > +} > + > +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 */ > + if (*mask & CAN_ERR_FLAG) { > + /* clear CAN_ERR_FLAG in list 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); > + > + /* reduce condition testing at receive time */ > + *can_id &= *mask; > + > + /* inverse can_id/can_mask filter */ > + if (inv) > + return &d->rx[RX_INV]; > + > + /* mask == 0 => no condition testing at receive time */ > + 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]; > + } > + } else { > + if (*mask == CAN_SFF_MASK) > + return &d->rx_sff[*can_id]; > + } > + > + /* default: filter via can_id/can_mask */ > + return &d->rx[RX_FIL]; > +} > + > +/** > + * can_rx_register - subscribe CAN frames from a specific interface > + * @dev: pointer to netdevice (NULL => subcribe from 'all' CAN devices list) > + * @can_id: CAN identifier (see description) > + * @mask: CAN mask (see description) > + * @func: callback function on filter match > + * @data: returned parameter for callback function > + * @ident: string for calling module indentification > + * > + * Description: > + * Invokes the callback function with the received sk_buff and the given > + * parameter 'data' on a matching receive filter. 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). > + * > + * Return: > + * 0 on success > + * -ENOMEM on missing cache mem to create subscription entry > + * -ENODEV unknown device > + */ > +int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask, > + void (*func)(struct sk_buff *, void *), void *data, > + char *ident) > +{ > + struct receiver *r; > + struct hlist_head *rl; > + struct dev_rcv_lists *d; > + 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); > + > + r = kmem_cache_alloc(rcv_cache, GFP_KERNEL); > + if (!r) > + return -ENOMEM; > + > + spin_lock(&rcv_lists_lock); > + > + d = find_dev_rcv_lists(dev); > + if (d) { > + rl = find_rcv_list(&can_id, &mask, d); > + > + r->can_id = can_id; > + r->mask = mask; > + r->matches = 0; > + r->func = func; > + r->data = data; > + r->ident = ident; > + > + 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; > + } 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); > + err = -ENODEV; > + } > + > + spin_unlock(&rcv_lists_lock); > + > + return err; > +} > +EXPORT_SYMBOL(can_rx_register); > + > +/* > + * 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); > + > + DBG("removing dev_rcv_list at %p\n", d); > + kfree(d); > +} > + > +/* > + * 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); > + > + DBG("removing receiver at %p\n", r); > + kmem_cache_free(rcv_cache, r); > +} > + > +/** > + * can_rx_unregister - unsubscribe CAN frames from a specific interface > + * @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices > list) > + * @can_id: CAN identifier > + * @mask: CAN mask > + * @func: callback function on filter match > + * @data: returned parameter for callback function > + * > + * Description: > + * Removes subscription entry depending on given (subscription) values. > + */ > +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; > + > + DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n", > + dev, DNAME(dev), can_id, mask, func, data); > + > + spin_lock(&rcv_lists_lock); > + > + d = find_dev_rcv_lists(dev); > + if (!d) { > + printk(KERN_ERR "BUG: receive list not found for " > + "dev %s, id %03X, mask %03X\n", > + DNAME(dev), can_id, mask); > + goto out; > + } > + > + rl = find_rcv_list(&can_id, &mask, d); > + > + /* > + * Search the receiver list for the item to delete. This should > + * exist, since no receiver may be unregistered that hasn't > + * been registered before. > + */ > + > + 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 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) { > + 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--; > + > + /* 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); > + hlist_del_rcu(&d->list); > + } else > + d = NULL; > + > + out: > + spin_unlock(&rcv_lists_lock); > + > + /* schedule the receiver item for deletion */ > + if (r) > + call_rcu(&r->rcu, can_rx_delete_receiver); > + > + /* 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) { > + clone->sk = skb->sk; > + r->func(clone, 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; > + canid_t can_id = cf->can_id; > + > + if (d->entries == 0) > + return 0; > + > + if (can_id & CAN_ERR_FLAG) { > + /* 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++; > + } > + } > + return 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 */ > + 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++; > + } > + } > + > + return matches; > +} > + > +static int can_rcv(struct sk_buff *skb, struct net_device *dev, > + struct packet_type *pt, struct net_device *orig_dev) > +{ > + struct dev_rcv_lists *d; > + 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 (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) { > + kfree_skb(skb); > + return 0; > + } > + > + /* update statistics */ > + stats.rx_frames++; > + stats.rx_frames_delta++; > + > + rcu_read_lock(); > + > + /* deliver the packet to sockets listening on all devices */ > + matches = can_rcv_filter(&rx_alldev_list, skb); > + > + /* find receive list for this device */ > + d = find_dev_rcv_lists(dev); > + if (d) > + matches += can_rcv_filter(d, skb); > + > + rcu_read_unlock(); > + > + /* free the skbuff allocated by the netdevice driver */ > + DBG("freeing skbuff %p\n", skb); > + kfree_skb(skb); > + > + if (matches > 0) { > + stats.matches++; > + stats.matches_delta++; > + } > + > + return 0; > +} > + > +/* > + * af_can protocol functions > + */ > + > +/** > + * can_proto_register - register CAN transport protocol > + * @cp: pointer to CAN protocol structure > + * > + * Return: > + * 0 on success > + * -EINVAL invalid (out of range) protocol number > + * -EBUSY protocol already in use > + * -ENOBUF if proto_register() fails > + */ > +int can_proto_register(struct can_proto *cp) > +{ > + int proto = cp->protocol; > + int err = 0; > + > + if (proto < 0 || proto >= CAN_NPROTO) { > + printk(KERN_ERR "can: protocol number %d out of range\n", > + proto); > + return -EINVAL; > + } > + > + spin_lock(&proto_tab_lock); > + if (proto_tab[proto]) { > + printk(KERN_ERR "can: protocol %d already registered\n", > + proto); > + err = -EBUSY; > + goto errout; > + } > + > + err = proto_register(cp->prot, 0); > + if (err < 0) > + goto errout; > + > + 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) > +{ > + 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); > + } > + proto_unregister(cp->prot); > + proto_tab[proto] = NULL; > + spin_unlock(&proto_tab_lock); > +} > +EXPORT_SYMBOL(can_proto_unregister); > + > +/* > + * af_can notifier to create/remove CAN netdevice specific structs > + */ > +static int can_notifier(struct notifier_block *nb, unsigned long msg, > + void *data) > +{ > + 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 (dev->nd_net != &init_net) > + return NOTIFY_DONE; > + > + if (dev->type != ARPHRD_CAN) > + return NOTIFY_DONE; > + > + switch (msg) { > + > + case NETDEV_REGISTER: > + > + /* > + * create new dev_rcv_lists for this device > + * > + * N.B. zeroing the struct is the correct initialization > + * for the embedded hlist_head structs. > + * Another list type, e.g. list_head, would require > + * explicit initialization. > + */ > + > + DBG("creating new dev_rcv_lists for %s\n", dev->name); > + > + d = kzalloc(sizeof(*d), GFP_KERNEL); > + if (!d) { > + printk(KERN_ERR > + "can: allocation of receive list failed\n"); > + return NOTIFY_DONE; > + } > + d->dev = dev; > + > + spin_lock(&rcv_lists_lock); > + hlist_add_head_rcu(&d->list, &rx_dev_list); > + spin_unlock(&rcv_lists_lock); > + > + break; > + > + case NETDEV_UNREGISTER: > + spin_lock(&rcv_lists_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 > + 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); > + > + if (d) > + call_rcu(&d->rcu, can_rx_delete_device); > + > + break; > + } > + > + 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 = { > + .type = __constant_htons(ETH_P_CAN), > + .dev = NULL, > + .func = can_rcv, > +}; > + > +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 __read_mostly = { > + .notifier_call = can_notifier, > +}; > + > +static __init int can_init(void) > +{ > + printk(banner); > + > + rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver), > + 0, 0, NULL); > + if (!rcv_cache) > + return -ENOMEM; > + > + /* > + * Insert 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); > + > + 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); > + } else > + stattimer.function = NULL; > + > + can_init_proc(); > + > + /* protocol register */ > + sock_register(&can_family_ops); > + register_netdevice_notifier(&can_netdev_notifier); > + dev_add_pack(&can_packet); > + > + return 0; > +} > + > +static __exit void can_exit(void) > +{ > + struct dev_rcv_lists *d; > + struct hlist_node *n, *next; > + > + if (stats_timer) > + del_timer(&stattimer); > + > + can_remove_proc(); > + > + /* protocol unregister */ > + dev_remove_pack(&can_packet); > + 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) { > + hlist_del(&d->list); > + kfree(d); > + } > + spin_unlock(&rcv_lists_lock); > + > + kmem_cache_destroy(rcv_cache); > +} > + > +module_init(can_init); > +module_exit(can_exit); > Index: net-2.6.25/net/can/af_can.h > =================================================================== > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ net-2.6.25/net/can/af_can.h 2007-11-14 13:04:49.000000000 +0100 > @@ -0,0 +1,120 @@ > +/* > + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * 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 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. > + * 3. Neither the name of Volkswagen nor the names of its contributors > + * may be used to endorse or promote products derived from this software > + * without specific prior written permission. > + * > + * 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, 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. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > + * DAMAGE. > + * > + * Send feedback to <[EMAIL PROTECTED]> > + * > + */ > + > +#ifndef AF_CAN_H > +#define AF_CAN_H > + > +#include <linux/skbuff.h> > +#include <linux/netdevice.h> > +#include <linux/list.h> > +#include <linux/rcupdate.h> > +#include <linux/can.h> > + > +/* af_can rx dispatcher structures */ > + > +struct receiver { > + struct hlist_node list; > + struct rcu_head rcu; > + canid_t can_id; > + canid_t mask; > + unsigned long matches; > + void (*func)(struct sk_buff *, void *); > + void *data; > + char *ident; > +}; > + > +enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX }; > + > +struct dev_rcv_lists { > + struct hlist_node list; > + struct rcu_head rcu; > + struct net_device *dev; > + struct hlist_head rx[RX_MAX]; > + struct hlist_head rx_sff[0x800]; > + int remove_on_zero_entries; > + int entries; > +}; > + > +/* statistic structures */ > + > +struct s_stats { > + unsigned long jiffies_init; > + > + unsigned long rx_frames; > + unsigned long tx_frames; > + unsigned long matches; > + > + unsigned long total_rx_rate; > + unsigned long total_tx_rate; > + unsigned long total_rx_match_ratio; > + > + unsigned long current_rx_rate; > + unsigned long current_tx_rate; > + unsigned long current_rx_match_ratio; > + > + unsigned long max_rx_rate; > + unsigned long max_tx_rate; > + unsigned long max_rx_match_ratio; > + > + unsigned long rx_frames_delta; > + unsigned long tx_frames_delta; > + unsigned long matches_delta; > +}; /* can be reset e.g. by can_init_stats() */ > + > +struct s_pstats { > + unsigned long stats_reset; > + unsigned long user_reset; > + unsigned long rcv_entries; > + unsigned long rcv_entries_max; > +}; /* persistent statistics */ > + > +/* function prototypes for the CAN networklayer procfs (proc.c) */ > +extern void can_init_proc(void); > +extern void can_remove_proc(void); > +extern void can_stat_update(unsigned long data); > + > +/* structures and variables from af_can.c needed in proc.c for reading */ > +extern struct timer_list stattimer; /* timer for statistics update */ > +extern struct s_stats stats; /* packet statistics */ > +extern struct s_pstats pstats; /* receive list statistics */ > +extern struct hlist_head rx_dev_list; /* rx dispatcher structures */ > + > +#endif /* AF_CAN_H */ > Index: net-2.6.25/net/can/proc.c > =================================================================== > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ net-2.6.25/net/can/proc.c 2007-11-14 13:04:49.000000000 +0100 > @@ -0,0 +1,532 @@ > +/* > + * proc.c - procfs support for Protocol family CAN core module > + * > + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * 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 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. > + * 3. Neither the name of Volkswagen nor the names of its contributors > + * may be used to endorse or promote products derived from this software > + * without specific prior written permission. > + * > + * 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, 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. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH > + * DAMAGE. > + * > + * Send feedback to <[EMAIL PROTECTED]> > + * > + */ > + > +#include <linux/module.h> > +#include <linux/proc_fs.h> > +#include <linux/list.h> > +#include <linux/rcupdate.h> > +#include <linux/can/core.h> > + > +#include "af_can.h" > + > +/* > + * proc filenames for the PF_CAN core > + */ > + > +#define CAN_PROC_VERSION "version" > +#define CAN_PROC_STATS "stats" > +#define CAN_PROC_RESET_STATS "reset_stats" > +#define CAN_PROC_RCVLIST_ALL "rcvlist_all" > +#define CAN_PROC_RCVLIST_FIL "rcvlist_fil" > +#define CAN_PROC_RCVLIST_INV "rcvlist_inv" > +#define CAN_PROC_RCVLIST_SFF "rcvlist_sff" > +#define CAN_PROC_RCVLIST_EFF "rcvlist_eff" > +#define CAN_PROC_RCVLIST_ERR "rcvlist_err" > + > +static struct proc_dir_entry *can_dir; > +static struct proc_dir_entry *pde_version; > +static struct proc_dir_entry *pde_stats; > +static struct proc_dir_entry *pde_reset_stats; > +static struct proc_dir_entry *pde_rcvlist_all; > +static struct proc_dir_entry *pde_rcvlist_fil; > +static struct proc_dir_entry *pde_rcvlist_inv; > +static struct proc_dir_entry *pde_rcvlist_sff; > +static struct proc_dir_entry *pde_rcvlist_eff; > +static struct proc_dir_entry *pde_rcvlist_err; > + > +static int user_reset; > + > +static const char rx_list_name[][8] = { > + [RX_ERR] = "rx_err", > + [RX_ALL] = "rx_all", > + [RX_FIL] = "rx_fil", > + [RX_INV] = "rx_inv", > + [RX_EFF] = "rx_eff", > +}; > + > +/* > + * af_can statistics stuff > + */ > + > +static void can_init_stats(void) > +{ > + /* > + * This memset function is called from a timer context (when > + * stattimer is active which is the default) OR in a process > + * context (reading the proc_fs when stattimer is disabled). > + */ > + memset(&stats, 0, sizeof(stats)); > + stats.jiffies_init = jiffies; > + > + pstats.stats_reset++; > + > + if (user_reset) { > + user_reset = 0; > + pstats.user_reset++; > + } > +} > + > +static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, > + unsigned long count) > +{ > + unsigned long rate; > + > + if (oldjif == newjif) > + return 0; > + > + /* see can_stat_update() - this should NEVER happen! */ > + if (count > (ULONG_MAX / HZ)) { > + printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n", > + count); > + return 99999999; > + } > + > + rate = (count * HZ) / (newjif - oldjif); > + > + return rate; > +} > + > +void can_stat_update(unsigned long data) > +{ > + unsigned long j = jiffies; /* snapshot */ > + > + /* restart counting in timer context on user request */ > + if (user_reset) > + can_init_stats(); > + > + /* restart counting on jiffies overflow */ > + if (j < stats.jiffies_init) > + can_init_stats(); > + > + /* prevent overflow in calc_rate() */ > + if (stats.rx_frames > (ULONG_MAX / HZ)) > + can_init_stats(); > + > + /* prevent overflow in calc_rate() */ > + if (stats.tx_frames > (ULONG_MAX / HZ)) > + can_init_stats(); > + > + /* matches overflow - very improbable */ > + if (stats.matches > (ULONG_MAX / 100)) > + can_init_stats(); > + > + /* calc total values */ > + if (stats.rx_frames) > + stats.total_rx_match_ratio = (stats.matches * 100) / > + stats.rx_frames; > + > + stats.total_tx_rate = calc_rate(stats.jiffies_init, j, > + stats.tx_frames); > + stats.total_rx_rate = calc_rate(stats.jiffies_init, j, > + stats.rx_frames); > + > + /* calc current values */ > + if (stats.rx_frames_delta) > + stats.current_rx_match_ratio = > + (stats.matches_delta * 100) / stats.rx_frames_delta; > + > + stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta); > + stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta); > + > + /* check / update maximum values */ > + if (stats.max_tx_rate < stats.current_tx_rate) > + stats.max_tx_rate = stats.current_tx_rate; > + > + if (stats.max_rx_rate < stats.current_rx_rate) > + stats.max_rx_rate = stats.current_rx_rate; > + > + if (stats.max_rx_match_ratio < stats.current_rx_match_ratio) > + stats.max_rx_match_ratio = stats.current_rx_match_ratio; > + > + /* clear values for 'current rate' calculation */ > + stats.tx_frames_delta = 0; > + stats.rx_frames_delta = 0; > + stats.matches_delta = 0; > + > + /* restart timer (one second) */ > + stattimer.expires = round_jiffies(jiffies + HZ); > + add_timer(&stattimer); > +} > + > +/* > + * proc read functions > + * > + * From known use-cases we expect about 10 entries in a receive list to be > + * printed in the proc_fs. So PAGE_SIZE is definitely enough space here. > + * > + */ > + > +static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list, > + struct net_device *dev) > +{ > + struct receiver *r; > + struct hlist_node *n; > + > + rcu_read_lock(); > + hlist_for_each_entry_rcu(r, n, rx_list, list) { > + char *fmt = (r->can_id & CAN_EFF_FLAG)? > + " %-5s %08X %08x %08x %08x %8ld %s\n" : > + " %-5s %03X %08x %08lx %08lx %8ld %s\n"; > + > + len += snprintf(page + len, PAGE_SIZE - len, fmt, > + DNAME(dev), r->can_id, r->mask, > + (unsigned long)r->func, (unsigned long)r->data, > + r->matches, r->ident); > + > + /* does a typical line fit into the current buffer? */ > + > + /* 100 Bytes before end of buffer */ > + if (len > PAGE_SIZE - 100) { > + /* mark output cut off */ > + len += snprintf(page + len, PAGE_SIZE - len, > + " (..)\n"); > + break; > + } > + } > + rcu_read_unlock(); > + > + return len; > +} > + > +static int can_print_recv_banner(char *page, int len) > +{ > + /* > + * can1. 00000000 00000000 00000000 > + * ....... 0 tp20 > + */ > + len += snprintf(page + len, PAGE_SIZE - len, > + " device can_id can_mask function" > + " userdata matches ident\n"); > + > + return len; > +} > + > +static int can_proc_read_stats(char *page, char **start, off_t off, > + int count, int *eof, void *data) > +{ > + int len = 0; > + > + len += snprintf(page + len, PAGE_SIZE - len, "\n"); > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld transmitted frames (TXF)\n", stats.tx_frames); > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld received frames (RXF)\n", stats.rx_frames); > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld matched frames (RXMF)\n", stats.matches); > + > + len += snprintf(page + len, PAGE_SIZE - len, "\n"); > + > + if (stattimer.function == can_stat_update) { > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld %% total match ratio (RXMR)\n", > + stats.total_rx_match_ratio); > + > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld frames/s total tx rate (TXR)\n", > + stats.total_tx_rate); > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld frames/s total rx rate (RXR)\n", > + stats.total_rx_rate); > + > + len += snprintf(page + len, PAGE_SIZE - len, "\n"); > + > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld %% current match ratio (CRXMR)\n", > + stats.current_rx_match_ratio); > + > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld frames/s current tx rate (CTXR)\n", > + stats.current_tx_rate); > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld frames/s current rx rate (CRXR)\n", > + stats.current_rx_rate); > + > + len += snprintf(page + len, PAGE_SIZE - len, "\n"); > + > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld %% max match ratio (MRXMR)\n", > + stats.max_rx_match_ratio); > + > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld frames/s max tx rate (MTXR)\n", > + stats.max_tx_rate); > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld frames/s max rx rate (MRXR)\n", > + stats.max_rx_rate); > + > + len += snprintf(page + len, PAGE_SIZE - len, "\n"); > + } > + > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld current receive list entries (CRCV)\n", > + pstats.rcv_entries); > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld maximum receive list entries (MRCV)\n", > + pstats.rcv_entries_max); > + > + if (pstats.stats_reset) > + len += snprintf(page + len, PAGE_SIZE - len, > + "\n %8ld statistic resets (STR)\n", > + pstats.stats_reset); > + > + if (pstats.user_reset) > + len += snprintf(page + len, PAGE_SIZE - len, > + " %8ld user statistic resets (USTR)\n", > + pstats.user_reset); > + > + len += snprintf(page + len, PAGE_SIZE - len, "\n"); > + > + *eof = 1; > + return len; > +} The read interface should use seq_file interface rather than formatting into page buffer. > +static int can_proc_read_reset_stats(char *page, char **start, off_t off, > + int count, int *eof, void *data) > +{ > + int len = 0; > + > + user_reset = 1; > + > + if (stattimer.function == can_stat_update) { > + len += snprintf(page + len, PAGE_SIZE - len, > + "Scheduled statistic reset #%ld.\n", > + pstats.stats_reset + 1); > + > + } else { > + if (stats.jiffies_init != jiffies) > + can_init_stats(); > + > + len += snprintf(page + len, PAGE_SIZE - len, > + "Performed statistic reset #%ld.\n", > + pstats.stats_reset); > + } > + > + *eof = 1; > + return len; > +} Why not have a write interface to do the reset? > +static int can_proc_read_version(char *page, char **start, off_t off, > + int count, int *eof, void *data) > +{ > + int len = 0; > + > + len += snprintf(page + len, PAGE_SIZE - len, "%s\n", > + CAN_VERSION_STRING); > + *eof = 1; > + return len; > +} > + > +static int can_proc_read_rcvlist(char *page, char **start, off_t off, > + int count, int *eof, void *data) > +{ > + /* double cast to prevent GCC warning */ > + int idx = (int)(long)data; > + int len = 0; > + struct dev_rcv_lists *d; > + struct hlist_node *n; > + > + len += snprintf(page + len, PAGE_SIZE - len, > + "\nreceive list '%s':\n", rx_list_name[idx]); > + > + rcu_read_lock(); > + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) { > + > + if (!hlist_empty(&d->rx[idx])) { > + len = can_print_recv_banner(page, len); > + len = can_print_rcvlist(page, len, &d->rx[idx], d->dev); > + } else > + len += snprintf(page + len, PAGE_SIZE - len, > + " (%s: no entry)\n", DNAME(d->dev)); > + > + /* exit on end of buffer? */ > + if (len > PAGE_SIZE - 100) > + break; > + } > + rcu_read_unlock(); > + > + len += snprintf(page + len, PAGE_SIZE - len, "\n"); > + > + *eof = 1; > + return len; > +} > + > +static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, > + int count, int *eof, void *data) > +{ > + int len = 0; > + struct dev_rcv_lists *d; > + struct hlist_node *n; > + > + /* RX_SFF */ > + len += snprintf(page + len, PAGE_SIZE - len, > + "\nreceive list 'rx_sff':\n"); > + > + rcu_read_lock(); > + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) { > + int i, all_empty = 1; > + /* check wether at least one list is non-empty */ > + for (i = 0; i < 0x800; i++) > + if (!hlist_empty(&d->rx_sff[i])) { > + all_empty = 0; > + break; > + } > + > + if (!all_empty) { > + len = can_print_recv_banner(page, len); > + for (i = 0; i < 0x800; i++) { > + if (!hlist_empty(&d->rx_sff[i]) && > + len < PAGE_SIZE - 100) > + len = can_print_rcvlist(page, len, > + &d->rx_sff[i], > + d->dev); > + } > + } else > + len += snprintf(page + len, PAGE_SIZE - len, > + " (%s: no entry)\n", DNAME(d->dev)); > + > + /* exit on end of buffer? */ > + if (len > PAGE_SIZE - 100) > + break; > + } > + rcu_read_unlock(); > + > + len += snprintf(page + len, PAGE_SIZE - len, "\n"); > + > + *eof = 1; > + return len; > +} > + > +/* > + * proc utility functions > + */ > + > +static struct proc_dir_entry *can_create_proc_readentry(const char *name, > + mode_t mode, > + read_proc_t *read_proc, > + void *data) > +{ > + if (can_dir) > + return create_proc_read_entry(name, mode, can_dir, read_proc, > + data); > + else > + return NULL; > +} > + > +static void can_remove_proc_readentry(const char *name) > +{ > + if (can_dir) > + remove_proc_entry(name, can_dir); > +} > + > +/* > + * can_init_proc - create main CAN proc directory and procfs entries > + */ > +void can_init_proc(void) > +{ > + /* create /proc/net/can directory */ > + can_dir = proc_mkdir("can", init_net.proc_net); > + > + if (!can_dir) { > + printk(KERN_INFO "can: failed to create /proc/net/can . " > + "CONFIG_PROC_FS missing?\n"); > + return; > + } > + > + can_dir->owner = THIS_MODULE; > + > + /* own procfs entries from the AF_CAN core */ > + pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644, > + can_proc_read_version, NULL); > + pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644, > + can_proc_read_stats, NULL); > + pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644, > + can_proc_read_reset_stats, NULL); > + pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644, > + can_proc_read_rcvlist, (void *)RX_ERR); > + pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644, > + can_proc_read_rcvlist, (void *)RX_ALL); > + pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644, > + can_proc_read_rcvlist, (void *)RX_FIL); > + pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644, > + can_proc_read_rcvlist, (void *)RX_INV); > + pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644, > + can_proc_read_rcvlist, (void *)RX_EFF); > + pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644, > + can_proc_read_rcvlist_sff, NULL); > +} > + > +/* > + * can_remove_proc - remove procfs entries and main CAN proc directory > + */ > +void can_remove_proc(void) > +{ > + if (pde_version) > + can_remove_proc_readentry(CAN_PROC_VERSION); > + > + if (pde_stats) > + can_remove_proc_readentry(CAN_PROC_STATS); > + > + if (pde_reset_stats) > + can_remove_proc_readentry(CAN_PROC_RESET_STATS); > + > + if (pde_rcvlist_err) > + can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR); > + > + if (pde_rcvlist_all) > + can_remove_proc_readentry(CAN_PROC_RCVLIST_ALL); > + > + if (pde_rcvlist_fil) > + can_remove_proc_readentry(CAN_PROC_RCVLIST_FIL); > + > + if (pde_rcvlist_inv) > + can_remove_proc_readentry(CAN_PROC_RCVLIST_INV); > + > + if (pde_rcvlist_eff) > + can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF); > + > + if (pde_rcvlist_sff) > + can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF); > + > + if (can_dir) > + proc_net_remove(&init_net, "can"); > +} > Output from checkpatch: WARNING: do not add new typedefs #116: FILE: include/linux/can.h:41: +typedef __u32 canid_t; WARNING: do not add new typedefs #124: FILE: include/linux/can.h:49: +typedef __u32 can_err_mask_t; ERROR: use tabs not spaces #498: FILE: net/can/af_can.c:159: +^I^I^I^I " not implemented.\n", module_name);$ WARNING: braces {} are not necessary for single statement blocks #1080: FILE: net/can/af_can.c:741: + if (!proto_tab[proto]) { + printk(KERN_ERR "BUG: can: protocol %d is not registered\n", + proto); + } total: 1 errors, 3 warnings, 1954 lines checked Your patch has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS. -- Stephen Hemminger <[EMAIL PROTECTED]> - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html