All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
To: Urs Thuermann <urs@isnogud.escape.de>
Cc: netdev@vger.kernel.org, Thomas Gleixner <tglx@linutronix.de>,
	Oliver Hartkopp <oliver.hartkopp@volkswagen.de>,
	Urs Thuermann <urs.thuermann@volkswagen.de>
Subject: Re: [patch 2/7] CAN: Add PF_CAN core module
Date: Thu, 17 May 2007 17:59:48 -0700	[thread overview]
Message-ID: <20070518005948.GA11737@linux.vnet.ibm.com> (raw)
In-Reply-To: <20070516145121.29877.2@janus.isnogud.escape.de>

On Wed, May 16, 2007 at 04:51:02PM +0200, Urs Thuermann 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.

Interesting!  One question called out below -- why do call_rcu() on each
piece of the struct dev_rcv_lists, instead of doing call_rcu() on the
whole thing and having the RCU callback free up the pieces?  Given that
all the pieces are call_rcu()ed separately, there had better not be
persistent pointers to the pieces, right?

Doing it in one chunk would make the code a bit simpler and also reduce
the RCU overhead a bit.

Or am I missing something subtle here?

						Thanx, Paul

> Signed-Off-By: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-Off-By: Urs Thuermann <urs.thuermann@volkswagen.de>
> 
> ---
>  include/linux/can.h      |   98 ++++
>  include/linux/can/core.h |   88 +++
>  net/Kconfig              |    1 
>  net/Makefile             |    1 
>  net/can/Kconfig          |   25 +
>  net/can/Makefile         |    6 
>  net/can/af_can.c         | 1063 +++++++++++++++++++++++++++++++++++++++++++++++
>  net/can/af_can.h         |  122 +++++
>  net/can/proc.c           |  660 +++++++++++++++++++++++++++++
>  9 files changed, 2064 insertions(+)
> 
> Index: linux-2.6.22-rc1-git4/include/linux/can.h
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/include/linux/can.h	2007-05-16 10:02:06.000000000 +0200
> @@ -0,0 +1,98 @@
> +/*
> + * linux/can.h
> + *
> + * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + *          Urs Thuermann   <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_H
> +#define CAN_H
> +
> +#include <linux/types.h>
> +#include <linux/socket.h>
> +
> +/* controller area network (CAN) kernel definitions */
> +
> +/* special address description flags for the CAN_ID */
> +#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
> +#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
> +#define CAN_ERR_FLAG 0x20000000U /* error frame */
> +
> +/* valid bits in CAN ID for frame formats */
> +#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
> +#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
> +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
> +
> +typedef __u32 canid_t;
> +typedef __u32 can_err_mask_t;
> +
> +/**
> + * struct can_frame - basic CAN frame structure
> + * @can_id:  the CAN ID of the frame and CAN_*_FLAG flags, see above.
> + * @can_dlc: the data length field of the CAN frame
> + * @data:    the CAN frame payload.
> + */
> +struct can_frame {
> +	canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
> +	__u8    can_dlc; /* data length code: 0 .. 8 */
> +	__u8    data[8] __attribute__((aligned(8)));
> +};
> +
> +/* particular protocols of the protocol family PF_CAN */
> +#define CAN_RAW		1 /* RAW sockets */
> +#define CAN_BCM		2 /* Broadcast Manager */
> +#define CAN_TP16	3 /* VAG Transport Protocol v1.6 */
> +#define CAN_TP20	4 /* VAG Transport Protocol v2.0 */
> +#define CAN_MCNET	5 /* Bosch MCNet */
> +#define CAN_ISOTP	6 /* ISO 15765-2 Transport Protocol */
> +#define CAN_BAP		7 /* VAG Bedien- und Anzeigeprotokoll */
> +#define CAN_NPROTO	8
> +
> +#define SOL_CAN_BASE 100
> +
> +/**
> + * struct sockaddr_can - the sockaddr structure for CAN sockets
> + * @can_family:  address family number AF_CAN.
> + * @can_ifindex: CAN network interface index.
> + * @can_addr:    transport protocol specific address, mostly CAN IDs.
> + */
> +struct sockaddr_can {
> +	sa_family_t can_family;
> +	int         can_ifindex;
> +	union {
> +		struct { canid_t rx_id, tx_id; } tp16;
> +		struct { canid_t rx_id, tx_id; } tp20;
> +		struct { canid_t rx_id, tx_id; } mcnet;
> +		struct { canid_t rx_id, tx_id; } isotp;
> +		struct { int     lcu,   type;  } bap;
> +	} can_addr;
> +};
> +
> +/**
> + * struct can_filter - CAN ID based filter in can_register().
> + * @can_id:   relevant bits of CAN ID which are not masked out.
> + * @can_mask: CAN mask (see description)
> + *
> + * Description:
> + * A filter matches, when
> + *
> + *          <received_can_id> & mask == can_id & mask
> + *
> + * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
> + * filter for error frames (CAN_ERR_FLAG bit set in mask).
> + */
> +struct can_filter {
> +	canid_t can_id;
> +	canid_t can_mask;
> +};
> +
> +#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
> +
> +#endif /* CAN_H */
> Index: linux-2.6.22-rc1-git4/include/linux/can/core.h
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/include/linux/can/core.h	2007-05-16 09:45:00.000000000 +0200
> @@ -0,0 +1,88 @@
> +/*
> + * linux/can/core.h
> + *
> + * Protoypes and definitions for CAN protocol modules using the PF_CAN core
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + *          Urs Thuermann   <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_CORE_H
> +#define CAN_CORE_H
> +
> +#include <linux/can.h>
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>
> +
> +#define CAN_VERSION "20070508"
> +
> +/* 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")
> +
> +#define CAN_PROC_DIR "net/can" /* /proc/... */
> +
> +/**
> + * 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 int 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 int 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_dev_register(struct net_device *dev,
> +			    void (*func)(unsigned long msg, void *),
> +			    void *data);
> +
> +extern int can_dev_unregister(struct net_device *dev,
> +			      void (*func)(unsigned long msg, 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(args...)       (debug & 1 ? \
> +			       (printk(KERN_DEBUG "can-%s %s: ", \
> +				IDENT, __func__), printk(args)) : 0)
> +#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
> +#define DBG_SKB(skb)       (debug & 4 ? can_debug_skb(skb) : 0)
> +#else
> +#define DBG(args...)
> +#define DBG_FRAME(args...)
> +#define DBG_SKB(skb)
> +#endif
> +
> +#endif /* CAN_CORE_H */
> Index: linux-2.6.22-rc1-git4/net/Kconfig
> ===================================================================
> --- linux-2.6.22-rc1-git4.orig/net/Kconfig	2007-05-16 09:44:54.000000000 +0200
> +++ linux-2.6.22-rc1-git4/net/Kconfig	2007-05-16 09:45:00.000000000 +0200
> @@ -210,6 +210,7 @@
>  endmenu
> 
>  source "net/ax25/Kconfig"
> +source "net/can/Kconfig"
>  source "net/irda/Kconfig"
>  source "net/bluetooth/Kconfig"
>  source "net/rxrpc/Kconfig"
> Index: linux-2.6.22-rc1-git4/net/Makefile
> ===================================================================
> --- linux-2.6.22-rc1-git4.orig/net/Makefile	2007-05-16 09:44:54.000000000 +0200
> +++ linux-2.6.22-rc1-git4/net/Makefile	2007-05-16 09:45:00.000000000 +0200
> @@ -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: linux-2.6.22-rc1-git4/net/can/Kconfig
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/Kconfig	2007-05-16 10:01:14.000000000 +0200
> @@ -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 at
> +	  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 to the system log.  Select this if you are having a
> +	  problem with CAN support and want to see more of what is going on.
> Index: linux-2.6.22-rc1-git4/net/can/Makefile
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/Makefile	2007-05-16 10:01:14.000000000 +0200
> @@ -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: linux-2.6.22-rc1-git4/net/can/af_can.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/af_can.c	2007-05-16 10:02:06.000000000 +0200
> @@ -0,0 +1,1063 @@
> +/*
> + * 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, the following disclaimer and
> + *    the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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 <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/kmod.h>
> +#include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/spinlock.h>
> +#include <linux/rcupdate.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 <linux/can.h>
> +#include <linux/can/core.h>
> +#include <net/sock.h>
> +#include <asm/uaccess.h>
> +
> +#include "af_can.h"
> +
> +#define IDENT "core"
> +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 <urs.thuermann@volkswagen.de>, "
> +	      "Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
> +
> +MODULE_ALIAS_NETPROTO(PF_CAN);
> +
> +int stats_timer = 1; /* default: on */
> +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);
> +
> +static struct kmem_cache *rcv_cache __read_mostly;
> +
> +/* 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 */
> +
> +/*
> + * 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);
> +	if (sk->sk_protinfo)
> +		kfree(sk->sk_protinfo);
> +}
> +
> +static int can_create(struct socket *sock, int protocol)
> +{
> +	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);
> +
> +	sock->state = SS_UNCONNECTED;
> +
> +	if (protocol < 0 || protocol >= CAN_NPROTO)
> +		return -EINVAL;
> +
> +	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);
> +		ret = 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 (ret == -ENOSYS)
> +			printk(KERN_INFO "can: request_module(%s) not"
> +			       " implemented.\n", module_name);
> +		else if (ret)
> +			printk(KERN_ERR "can: request_module(%s) failed\n",
> +			       module_name);
> +	}
> +
> +	/* check for success and correct type */
> +	cp = proto_tab[protocol];
> +	if (!cp || cp->type != sock->type)
> +		return -EPROTONOSUPPORT;
> +
> +	if (cp->capability >= 0 && !capable(cp->capability))
> +		return -EPERM;
> +
> +	sock->ops = cp->ops;
> +
> +	sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
> +	if (!sk)
> +		return -ENOMEM;
> +
> +	sock_init_data(sock, sk);
> +	sk->sk_destruct = can_sock_destruct;
> +
> +	DBG("created sock: %p\n", sk);
> +
> +	if (sk->sk_prot->init)
> +		ret = sk->sk_prot->init(sk);
> +
> +	if (ret) {
> +		/* release sk on errors */
> +		sock_orphan(sk);
> +		sock_put(sk);
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * 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())
> + */
> +int can_send(struct sk_buff *skb, int loop)
> +{
> +	struct sock **tx_sk = (struct sock **)skb->cb;
> +	int err;
> +
> +	if (skb->dev->type != ARPHRD_CAN) {
> +		kfree_skb(skb);
> +		return -EPERM;
> +	}
> +
> +	if (loop) {
> +		/* local loopback of sent CAN frames (default) */
> +
> +		/* indication for the CAN driver: do loopback */
> +		*tx_sk = skb->sk;
> +
> +		/*
> +		 * 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
> +		 */
> +
> +		/* interface not capabable to do the loopback itself? */
> +		if (!(skb->dev->flags & IFF_LOOPBACK)) {
> +			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
> +
> +			/* perform the local loopback here */
> +			newskb->protocol  = htons(ETH_P_CAN);
> +			newskb->ip_summed = CHECKSUM_UNNECESSARY;
> +			netif_rx(newskb);
> +		}
> +	} else {
> +		/* indication for the CAN driver: no loopback required */
> +		*tx_sk = NULL;
> +	}
> +
> +	if (!(skb->dev->flags & IFF_UP))
> +		return -ENETDOWN;
> +
> +	/* 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;
> +	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(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_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_inv;
> +
> +	/* mask == 0 => no condition testing at receive time */
> +	if (!(*mask))
> +		return &d->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_eff;
> +		}
> +	} else {
> +		if (*mask == CAN_SFF_MASK)
> +			return &d->rx_sff[*can_id];
> +	}
> +
> +	/* default: filter via can_id/can_mask */
> +	return &d->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 ret = 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);
> +
> +	r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
> +	if (!r)
> +		return -ENOMEM;
> +
> +	spin_lock_bh(&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);
> +		ret = -ENODEV;
> +	}
> +
> +	spin_unlock_bh(&rcv_lists_lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(can_rx_register);
> +
> +static void can_rcv_lists_delete(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)
> +{
> +	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)
> + * @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.
> + *
> + * 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)
> +{
> +	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);
> +
> +	spin_lock_bh(&rcv_lists_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;
> +		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(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:
> +	 * 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;
> +		r = NULL;
> +		goto out;
> +	}
> +
> +	hlist_del_rcu(&r->list);
> +	d->entries--;
> +
> +	if (pstats.rcv_entries > 0)
> +		pstats.rcv_entries--;
> +
> + out:
> +	spin_unlock_bh(&rcv_lists_lock);
> +
> +	/* schedule the receiver item for deletion */
> +	if (r)
> +		call_rcu(&r->rcu, can_rx_delete);
> +
> +	return ret;
> +}
> +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++;
> +	}
> +}
> +
> +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_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_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_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) {
> +		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_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("af_can: can_rcv: received CAN frame",
> +		  (struct can_frame *)skb->data);
> +
> +	if (dev->type != ARPHRD_CAN) {
> +		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 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);
> +
> +#endif
> +
> +/*
> + * 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;
> +	}
> +	if (proto_tab[proto]) {
> +		printk(KERN_ERR "can: protocol %d already "
> +		       "registered\n", proto);
> +		return -EBUSY;
> +	}
> +
> +	err = proto_register(cp->prot, 0);
> +	if (err < 0)
> +		return err;
> +
> +	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;
> +
> +	return err;
> +}
> +EXPORT_SYMBOL(can_proto_register);
> +
> +/**
> + * 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)
> +{
> +	int proto = cp->protocol;
> +
> +	if (!proto_tab[proto]) {
> +		printk(KERN_ERR "can: protocol %d is not registered\n", proto);
> +		return -ESRCH;
> +	}
> +	proto_unregister(cp->prot);
> +	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(&notifier_lock);
> +	list_add(&n->list, &notifier_list);
> +	write_unlock(&notifier_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
> + */
> +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(&notifier_lock);
> +	list_for_each_entry_safe(n, next, &notifier_list, list) {
> +		if (n->dev == dev && n->func == func && n->data == data) {
> +			list_del(&n->list);
> +			kfree(n);
> +			ret = 0;
> +			break;
> +		}
> +	}
> +	write_unlock(&notifier_lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(can_dev_unregister);
> +
> +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 (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),
> +			    in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
> +		if (!d) {
> +			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);
> +
> +		break;
> +
> +	case NETDEV_UNREGISTER:
> +		spin_lock_bh(&rcv_lists_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]);

Urk!!!  That could be one heaping pile of callbacks!!!  Why not just
do the single call_rcu() below, and take the struct rcu_dev_lists
apart in the RCU callback?  Unless I am missing something, the
can_rx_delete_all() function feeds all the pieces to call_rcu() anyway,
so there should not be other CPUs digging through this stuff.

Or am I missing something subtle here?

> +		} else
> +			printk(KERN_ERR "can: notifier: receive list not "
> +			       "found for dev %s\n", dev->name);
> +
> +		spin_unlock_bh(&rcv_lists_lock);
> +
> +		if (d)
> +			call_rcu(&d->rcu, can_rcv_lists_delete);
> +
> +		break;
> +	}
> +
> +	read_lock(&notifier_lock);
> +	list_for_each_entry(n, &notifier_list, list) {
> +		if (n->dev == dev)
> +			n->func(msg, n->data);
> +	}
> +	read_unlock(&notifier_lock);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +/*
> + * af_can module init/exit functions
> + */
> +
> +static struct packet_type can_packet = {
> +	.type = __constant_htons(ETH_P_CAN),
> +	.dev  = NULL,
> +	.func = can_rcv,
> +};
> +
> +static struct net_proto_family can_family_ops = {
> +	.family = PF_CAN,
> +	.create = can_create,
> +	.owner  = THIS_MODULE,
> +};
> +
> +/* notifier block for netdevice event */
> +static struct notifier_block can_netdev_notifier = {
> +	.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, NULL);
> +	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
> +	 * 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);
> +
> +	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);
> +	} else
> +		stattimer.function = NULL;
> +
> +	/* procfs init */
> +	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);
> +
> +	/* procfs remove */
> +	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_bh(&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_bh(&rcv_lists_lock);
> +
> +	kmem_cache_destroy(rcv_cache);
> +}
> +
> +module_init(can_init);
> +module_exit(can_exit);
> Index: linux-2.6.22-rc1-git4/net/can/af_can.h
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/af_can.h	2007-05-16 10:02:06.000000000 +0200
> @@ -0,0 +1,122 @@
> +/*
> + * 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, the following disclaimer and
> + *    the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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 <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#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;
> +};
> +
> +struct dev_rcv_lists {
> +	struct hlist_node list;
> +	struct rcu_head rcu;
> +	struct net_device *dev;
> +	struct hlist_head rx_err;
> +	struct hlist_head rx_all;
> +	struct hlist_head rx_fil;
> +	struct hlist_head rx_inv;
> +	struct hlist_head rx_sff[0x800];
> +	struct hlist_head rx_eff;
> +	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: linux-2.6.22-rc1-git4/net/can/proc.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/proc.c	2007-05-16 10:02:06.000000000 +0200
> @@ -0,0 +1,660 @@
> +/*
> + * 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, the following disclaimer and
> + *    the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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 <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#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         = NULL;
> +static struct proc_dir_entry *pde_version     = NULL;
> +static struct proc_dir_entry *pde_stats       = NULL;
> +static struct proc_dir_entry *pde_reset_stats = NULL;
> +static struct proc_dir_entry *pde_rcvlist_all = NULL;
> +static struct proc_dir_entry *pde_rcvlist_fil = NULL;
> +static struct proc_dir_entry *pde_rcvlist_inv = NULL;
> +static struct proc_dir_entry *pde_rcvlist_sff = NULL;
> +static struct proc_dir_entry *pde_rcvlist_eff = NULL;
> +static struct proc_dir_entry *pde_rcvlist_err = NULL;
> +
> +static int user_reset = 0;
> +
> +/*
> + * 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 ret = 0;
> +
> +	if (oldjif == newjif)
> +		return 0;
> +
> +	/* see can_rcv() - this should NEVER happen! */
> +	if (count > (ULONG_MAX / HZ)) {
> +		printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
> +		       count);
> +		return 99999999;
> +	}
> +
> +	ret = (count * HZ) / (newjif - oldjif);
> +
> +	return ret;
> +}
> +
> +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();
> +
> +	/* stats.rx_frames is the definitively max. statistic value */
> +
> +	/* prevent overflow in calc_rate() */
> +	if (stats.rx_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 = 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  %08x  %08x  %8ld  %s\n";
> +
> +		len += snprintf(page + len, PAGE_SIZE - len, fmt,
> +				DNAME(dev), r->can_id, r->mask,
> +				(unsigned int)r->func, (unsigned int)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;
> +}
> +
> +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;
> +}
> +
> +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_all(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_ALL */
> +	len += snprintf(page + len, PAGE_SIZE - len,
> +			"\nreceive list 'rx_all':\n");
> +
> +	/* find receive list for this device */
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> +		if (!hlist_empty(&d->rx_all)) {
> +			len = can_print_recv_banner(page, len);
> +			len = can_print_rcvlist(page, len, &d->rx_all, 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_fil(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_FIL */
> +	len += snprintf(page + len, PAGE_SIZE - len,
> +			"\nreceive list 'rx_fil':\n");
> +
> +	/* find receive list for this device */
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> +		if (!hlist_empty(&d->rx_fil)) {
> +			len = can_print_recv_banner(page, len);
> +			len = can_print_rcvlist(page, len, &d->rx_fil, 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_inv(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_INV */
> +	len += snprintf(page + len, PAGE_SIZE - len,
> +			"\nreceive list 'rx_inv':\n");
> +
> +	/* find receive list for this device */
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> +		if (!hlist_empty(&d->rx_inv)) {
> +			len = can_print_recv_banner(page, len);
> +			len = can_print_rcvlist(page, len, &d->rx_inv, 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");
> +
> +	/* find receive list for this device */
> +	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;
> +}
> +
> +static int can_proc_read_rcvlist_eff(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_EFF */
> +	len += snprintf(page + len, PAGE_SIZE - len,
> +			"\nreceive list 'rx_eff':\n");
> +
> +	/* find receive list for this device */
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> +		if (!hlist_empty(&d->rx_eff)) {
> +			len = can_print_recv_banner(page, len);
> +			len = can_print_rcvlist(page, len, &d->rx_eff, 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_err(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_ERR */
> +	len += snprintf(page + len, PAGE_SIZE - len,
> +			"\nreceive list 'rx_err':\n");
> +
> +	/* find receive list for this device */
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> +		if (!hlist_empty(&d->rx_err)) {
> +			len = can_print_recv_banner(page, len);
> +			len = can_print_rcvlist(page, len, &d->rx_err, 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_PROC_DIR, NULL);
> +
> +	if (!can_dir) {
> +		printk(KERN_INFO "can: failed to create /proc/%s . "
> +		       "CONFIG_PROC_FS missing?\n", CAN_PROC_DIR);
> +		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_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL,
> +					0644, can_proc_read_rcvlist_all, NULL);
> +	pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL,
> +					0644, can_proc_read_rcvlist_fil, NULL);
> +	pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV,
> +					0644, can_proc_read_rcvlist_inv, NULL);
> +	pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF,
> +					0644, can_proc_read_rcvlist_sff, NULL);
> +	pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF,
> +					0644, can_proc_read_rcvlist_eff, NULL);
> +	pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR,
> +					0644, can_proc_read_rcvlist_err, 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_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_sff)
> +		can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
> +
> +	if (pde_rcvlist_eff)
> +		can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
> +
> +	if (pde_rcvlist_err)
> +		can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
> +
> +	if (can_dir)
> +		remove_proc_entry(CAN_PROC_DIR, NULL);
> +}
> 
> --
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

  parent reply	other threads:[~2007-05-18  0:59 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-05-16 14:51 [patch 0/7] CAN: Add new PF_CAN protocol family Urs Thuermann
2007-05-16 14:51 ` [patch 1/7] CAN: Allocate protocol numbers for PF_CAN Urs Thuermann
2007-05-16 14:51 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-05-16 16:35   ` Arnaldo Carvalho de Melo
2007-05-16 18:26     ` Oliver Hartkopp
2007-05-16 19:14     ` Urs Thuermann
2007-05-16 20:51       ` Arnaldo Carvalho de Melo
2007-05-18  0:59   ` Paul E. McKenney [this message]
2007-05-18  9:19     ` Oliver Hartkopp
2007-05-18 14:33       ` Paul E. McKenney
2007-05-18 15:03         ` Oliver Hartkopp
2007-05-18 21:06         ` Oliver Hartkopp
2007-05-16 14:51 ` [patch 3/7] CAN: Add raw protocol Urs Thuermann
2007-05-16 14:51 ` [patch 4/7] CAN: Add broadcast manager (bcm) protocol Urs Thuermann
2007-05-16 14:51 ` [patch 5/7] CAN: Add virtual CAN netdevice driver Urs Thuermann
2007-05-16 14:51 ` [patch 6/7] CAN: Add maintainer entries Urs Thuermann
2007-05-16 14:51 ` [patch 7/7] CAN: Add documentation Urs Thuermann
  -- strict thread matches above, loose matches on Subject: below --
2007-05-30 13:11 [patch 0/7] CAN: Add new PF_CAN protocol family, update Urs Thuermann
2007-05-30 13:11 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-06-22  3:44 [patch 0/7] CAN: Add new PF_CAN protocol family, try #3 Urs Thuermann
2007-06-22  3:44 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-08-04  2:06 [patch 0/7] CAN: Add new PF_CAN protocol family, try #5 Urs Thuermann
2007-08-04  2:06 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-17 10:03 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #6 Urs Thuermann
2007-09-17 10:03 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-17 15:50   ` Paul E. McKenney
2007-09-18 13:31   ` Patrick McHardy
2007-09-18 14:54     ` Urs Thuermann
2007-09-18 15:07       ` Patrick McHardy
2007-09-18 21:20     ` Urs Thuermann
2007-09-19  8:27       ` Patrick McHardy
2007-09-20  8:53         ` Urs Thuermann
2007-09-20 10:33           ` Patrick McHardy
2007-09-20 11:30             ` Urs Thuermann
2007-09-20 11:43               ` Patrick McHardy
2007-09-20 18:43 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #7 Urs Thuermann
2007-09-20 18:43 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-20 20:06   ` Joe Perches
2007-09-20 20:27     ` Thomas Gleixner
2007-09-21 10:35     ` Urs Thuermann
2007-09-21 16:58       ` Joe Perches
2007-09-24 19:23     ` Urs Thuermann
2007-09-21 12:47   ` Patrick McHardy
2007-09-21 18:01     ` Urs Thuermann
2007-09-22 10:53       ` Patrick McHardy
2007-09-25 12:20 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #8 Urs Thuermann
2007-09-25 12:20 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-25 12:41   ` Arnaldo Carvalho de Melo
2007-09-25 13:24     ` Urs Thuermann
2007-09-25 15:33       ` Stephen Hemminger
2007-09-25 21:00         ` Urs Thuermann
2007-09-25 21:07           ` Stephen Hemminger
2007-09-25 21:09           ` David Miller
2007-09-28 16:27             ` Thomas Gleixner
2007-09-28 20:20               ` David Miller
2007-09-28 20:28                 ` Thomas Gleixner
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
2007-10-02 13:10 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-10-02 14:38   ` Arnaldo Carvalho de Melo
2007-10-02 16:09     ` Oliver Hartkopp
2007-10-04 11:51     ` Urs Thuermann
2007-10-04 13:40       ` Arnaldo Carvalho de Melo
2007-10-05 10:49 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #10 Urs Thuermann
2007-10-05 10:49 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-11-14 12:13 [PATCH 0/7] CAN: New PF_CAN protocol family for 2.6.25 Urs Thuermann
2007-11-14 12:13 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-11-14 21:38   ` Stephen Hemminger
2007-11-15  7:40     ` Oliver Hartkopp
2007-11-15  8:04       ` Joe Perches
2007-11-15 11:51         ` Urs Thuermann
2007-11-15 12:05           ` David Miller
2007-11-15 15:11             ` Sam Ravnborg
2007-11-16 14:33             ` Urs Thuermann
2007-11-16 23:42               ` David Miller
2007-11-15 11:36     ` Urs Thuermann
2007-11-15 15:09       ` Sam Ravnborg
2007-11-16 15:02 [PATCH 0/7] CAN: New PF_CAN protocol family for 2.6.25, update Urs Thuermann
2007-11-16 15:02 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070518005948.GA11737@linux.vnet.ibm.com \
    --to=paulmck@linux.vnet.ibm.com \
    --cc=netdev@vger.kernel.org \
    --cc=oliver.hartkopp@volkswagen.de \
    --cc=tglx@linutronix.de \
    --cc=urs.thuermann@volkswagen.de \
    --cc=urs@isnogud.escape.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.