X-Git-Url: http://rtime.felk.cvut.cz/gitweb/socketcan-devel.git/blobdiff_plain/7793433c70606ad679c6992c5218c5813dcc04e5..05e2d5c0adda7e44b9afb6d6a17d1dbe90af4488:/kernel/2.6/net/can/bcm.c diff --git a/kernel/2.6/net/can/bcm.c b/kernel/2.6/net/can/bcm.c index c704123..11c5279 100644 --- a/kernel/2.6/net/can/bcm.c +++ b/kernel/2.6/net/can/bcm.c @@ -44,41 +44,63 @@ #include #include #include +#include +#include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#endif #include #include #include #include #include #include -#include -#include -#include +#include +#include +#include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) #include #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) +#include "compat.h" +#endif -#include /* for RCSID. Removed by mkpatch script */ +#include /* for RCSID. Removed by mkpatch script */ RCSID("$Id$"); +/* + * To send multiple CAN frame content within TX_SETUP or to filter + * CAN messages with multiplex index within RX_SETUP, the number of + * different filters is limited to 256 due to the one byte index value. + */ +#define MAX_NFRAMES 256 + /* use of last_frames[index].can_dlc */ #define RX_RECV 0x40 /* received data for this element */ #define RX_THR 0x80 /* element not been sent due to throttle feature */ #define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */ /* get best masking value for can_rx_register() for a given single can_id */ -#define REGMASK(id) ((id & CAN_RTR_FLAG) | ((id & CAN_EFF_FLAG) ? \ - (CAN_EFF_MASK | CAN_EFF_FLAG) : CAN_SFF_MASK)) +#define REGMASK(id) ((id & CAN_EFF_FLAG) ? \ + (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \ + (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG)) #define CAN_BCM_VERSION CAN_VERSION static __initdata const char banner[] = KERN_INFO - "can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n"; + "can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n"; MODULE_DESCRIPTION("PF_CAN broadcast manager protocol"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Oliver Hartkopp "); +MODULE_ALIAS("can-proto-2"); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) +#error This code only supports Kernel versions 2.6.22+ +#error For older 2.6 Kernels please use bcm-prior-2-6-22.c instead of bcm.c +#endif /* easy access to can_frame payload */ static inline u64 GET_U64(const struct can_frame *cp) @@ -90,20 +112,16 @@ struct bcm_op { struct list_head list; int ifindex; canid_t can_id; - int flags; - unsigned long j_ival1, j_ival2, j_lastmsg; + u32 flags; unsigned long frames_abs, frames_filtered; - struct timer_list timer, thrtimer; struct timeval ival1, ival2; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) - ktime_t rx_stamp; -#else - struct timeval rx_stamp; -#endif + struct hrtimer timer, thrtimer; + struct tasklet_struct tsklet, thrtsklet; + ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; int rx_ifindex; - int count; - int nframes; - int currframe; + u32 count; + u32 nframes; + u32 currframe; struct can_frame *frames; struct can_frame *last_frames; struct can_frame sframe; @@ -115,11 +133,7 @@ struct bcm_op { static struct proc_dir_entry *proc_dir; struct bcm_sock { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) struct sock sk; -#else - struct sock *sk; -#endif int bound; int ifindex; struct notifier_block notifier; @@ -127,105 +141,129 @@ struct bcm_sock { struct list_head tx_ops; unsigned long dropped_usr_msgs; struct proc_dir_entry *bcm_proc_read; - char procname [9]; /* pointer printed in ASCII with \0 */ + char procname [32]; /* inode number in decimal with \0 */ }; static inline struct bcm_sock *bcm_sk(const struct sock *sk) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) return (struct bcm_sock *)sk; -#else - return (struct bcm_sock *)sk->sk_protinfo; -#endif } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) -static void *kzalloc(size_t size, unsigned int __nocast flags) +#define CFSIZ sizeof(struct can_frame) +#define OPSIZ sizeof(struct bcm_op) +#define MHSIZ sizeof(struct bcm_msg_head) + +/* + * procfs functions + */ +static char *bcm_proc_getifname(char *result, int ifindex) { - void *ret = kmalloc(size, flags); + struct net_device *dev; - if (ret) - memset(ret, 0, size); + if (!ifindex) + return "any"; - return ret; -} + read_lock(&dev_base_lock); + dev = __dev_get_by_index(&init_net, ifindex); + if (dev) + strcpy(result, dev->name); + else + strcpy(result, "???"); + read_unlock(&dev_base_lock); -static inline void skb_get_timestamp(const struct sk_buff *skb, - struct timeval *stamp) -{ - stamp->tv_sec = skb->stamp.tv_sec; - stamp->tv_usec = skb->stamp.tv_usec; + return result; } -static inline void skb_set_timestamp(struct sk_buff *skb, - const struct timeval *stamp) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int bcm_proc_show(struct seq_file *m, void *v) { - skb->stamp.tv_sec = stamp->tv_sec; - skb->stamp.tv_usec = stamp->tv_usec; -} + char ifname[IFNAMSIZ]; + struct sock *sk = (struct sock *)m->private; + struct bcm_sock *bo = bcm_sk(sk); + struct bcm_op *op; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + seq_printf(m, ">>> socket %p", sk->sk_socket); + seq_printf(m, " / sk %p", sk); + seq_printf(m, " / bo %p", bo); +#else + seq_printf(m, ">>> socket %pK", sk->sk_socket); + seq_printf(m, " / sk %pK", sk); + seq_printf(m, " / bo %pK", bo); #endif + seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs); + seq_printf(m, " / bound %s", bcm_proc_getifname(ifname, bo->ifindex)); + seq_printf(m, " <<<\n"); -#define CFSIZ sizeof(struct can_frame) -#define OPSIZ sizeof(struct bcm_op) -#define MHSIZ sizeof(struct bcm_msg_head) + list_for_each_entry(op, &bo->rx_ops, list) { -/* - * rounded_tv2jif - calculate jiffies from timeval including optional up - * @tv: pointer to timeval - * - * Description: - * Unlike timeval_to_jiffies() provided in include/linux/jiffies.h, this - * function is intentionally more relaxed on precise timer ticks to get - * exact one jiffy for requested 1000us on a 1000HZ machine. - * This code is to be removed when upgrading to kernel hrtimer. - * - * Return: - * calculated jiffies (max: ULONG_MAX) - */ -static unsigned long rounded_tv2jif(const struct timeval *tv) -{ - unsigned long sec = tv->tv_sec; - unsigned long usec = tv->tv_usec; - unsigned long jif; + unsigned long reduction; - if (sec > ULONG_MAX / HZ) - return ULONG_MAX; + /* print only active entries & prevent division by zero */ + if (!op->frames_abs) + continue; - /* round up to get at least the requested time */ - usec += 1000000 / HZ - 1; + seq_printf(m, "rx_op: %03X %-5s ", + op->can_id, bcm_proc_getifname(ifname, op->ifindex)); + seq_printf(m, "[%u]%c ", op->nframes, + (op->flags & RX_CHECK_DLC)?'d':' '); + if (op->kt_ival1.tv64) + seq_printf(m, "timeo=%lld ", + (long long) + ktime_to_us(op->kt_ival1)); - jif = usec / (1000000 / HZ); + if (op->kt_ival2.tv64) + seq_printf(m, "thr=%lld ", + (long long) + ktime_to_us(op->kt_ival2)); - if (sec * HZ > ULONG_MAX - jif) - return ULONG_MAX; + seq_printf(m, "# recv %ld (%ld) => reduction: ", + op->frames_filtered, op->frames_abs); - return jif + sec * HZ; -} + reduction = 100 - (op->frames_filtered * 100) / op->frames_abs; -/* - * procfs functions - */ -static char *bcm_proc_getifname(int ifindex) -{ - struct net_device *dev; + seq_printf(m, "%s%ld%%\n", + (reduction == 100)?"near ":"", reduction); + } - if (!ifindex) - return "any"; + list_for_each_entry(op, &bo->tx_ops, list) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) - dev = __dev_get_by_index(&init_net, ifindex); /* no usage counting */ -#else - dev = __dev_get_by_index(ifindex); /* no usage counting */ -#endif - if (dev) - return dev->name; + seq_printf(m, "tx_op: %03X %s [%u] ", + op->can_id, + bcm_proc_getifname(ifname, op->ifindex), + op->nframes); - return "???"; + if (op->kt_ival1.tv64) + seq_printf(m, "t1=%lld ", + (long long) ktime_to_us(op->kt_ival1)); + + if (op->kt_ival2.tv64) + seq_printf(m, "t2=%lld ", + (long long) ktime_to_us(op->kt_ival2)); + + seq_printf(m, "# sent %ld\n", op->frames_abs); + } + seq_putc(m, '\n'); + return 0; } +static int bcm_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, bcm_proc_show, PDE(inode)->data); +} + +static const struct file_operations bcm_proc_fops = { + .owner = THIS_MODULE, + .open = bcm_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#else static int bcm_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { + char ifname[IFNAMSIZ]; int len = 0; struct sock *sk = (struct sock *)data; struct bcm_sock *bo = bcm_sk(sk); @@ -238,7 +276,7 @@ static int bcm_read_proc(char *page, char **start, off_t off, len += snprintf(page + len, PAGE_SIZE - len, " / dropped %lu", bo->dropped_usr_msgs); len += snprintf(page + len, PAGE_SIZE - len, " / bound %s", - bcm_proc_getifname(bo->ifindex)); + bcm_proc_getifname(ifname, bo->ifindex)); len += snprintf(page + len, PAGE_SIZE - len, " <<<\n"); list_for_each_entry(op, &bo->rx_ops, list) { @@ -251,17 +289,21 @@ static int bcm_read_proc(char *page, char **start, off_t off, len += snprintf(page + len, PAGE_SIZE - len, "rx_op: %03X %-5s ", - op->can_id, bcm_proc_getifname(op->ifindex)); + op->can_id, bcm_proc_getifname(ifname, op->ifindex)); len += snprintf(page + len, PAGE_SIZE - len, "[%d]%c ", op->nframes, (op->flags & RX_CHECK_DLC)?'d':' '); - if (op->j_ival1) + if (op->kt_ival1.tv64) len += snprintf(page + len, PAGE_SIZE - len, - "timeo=%ld ", op->j_ival1); + "timeo=%lld ", + (long long) + ktime_to_us(op->kt_ival1)); - if (op->j_ival2) + if (op->kt_ival2.tv64) len += snprintf(page + len, PAGE_SIZE - len, - "thr=%ld ", op->j_ival2); + "thr=%lld ", + (long long) + ktime_to_us(op->kt_ival2)); len += snprintf(page + len, PAGE_SIZE - len, "# recv %ld (%ld) => reduction: ", @@ -283,15 +325,17 @@ static int bcm_read_proc(char *page, char **start, off_t off, len += snprintf(page + len, PAGE_SIZE - len, "tx_op: %03X %s [%d] ", - op->can_id, bcm_proc_getifname(op->ifindex), + op->can_id, + bcm_proc_getifname(ifname, op->ifindex), op->nframes); - if (op->j_ival1) - len += snprintf(page + len, PAGE_SIZE - len, "t1=%ld ", - op->j_ival1); - if (op->j_ival2) - len += snprintf(page + len, PAGE_SIZE - len, "t2=%ld ", - op->j_ival2); + if (op->kt_ival1.tv64) + len += snprintf(page + len, PAGE_SIZE - len, "t1=%lld ", + (long long) ktime_to_us(op->kt_ival1)); + + if (op->kt_ival2.tv64) + len += snprintf(page + len, PAGE_SIZE - len, "t2=%lld ", + (long long) ktime_to_us(op->kt_ival2)); len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n", op->frames_abs); @@ -308,6 +352,7 @@ static int bcm_read_proc(char *page, char **start, off_t off, *eof = 1; return len; } +#endif /* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface @@ -323,11 +368,7 @@ static void bcm_can_tx(struct bcm_op *op) if (!op->ifindex) return; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) dev = dev_get_by_index(&init_net, op->ifindex); -#else - dev = dev_get_by_index(op->ifindex); -#endif if (!dev) { /* RFC: should this bcm_op remove itself here? */ return; @@ -366,7 +407,7 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, struct can_frame *firstframe; struct sockaddr_can *addr; struct sock *sk = op->sk; - int datalen = head->nframes * CFSIZ; + unsigned int datalen = head->nframes * CFSIZ; int err; skb = alloc_skb(sizeof(*head) + datalen, gfp_any()); @@ -377,11 +418,7 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, if (head->nframes) { /* can_frames starting here */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) - firstframe = (struct can_frame *) skb_tail_pointer(skb); -#else - firstframe = (struct can_frame *) skb->tail; -#endif + firstframe = (struct can_frame *)skb_tail_pointer(skb); memcpy(skb_put(skb, datalen), frames, datalen); @@ -397,11 +434,7 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, if (has_timestamp) { /* restore rx timestamp */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) skb->tstamp = op->rx_stamp; -#else - skb_set_timestamp(skb, &op->rx_stamp); -#endif } /* @@ -427,18 +460,15 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, } } -/* - * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions - */ -static void bcm_tx_timeout_handler(unsigned long data) +static void bcm_tx_timeout_tsklet(unsigned long data) { struct bcm_op *op = (struct bcm_op *)data; + struct bcm_msg_head msg_head; - if (op->j_ival1 && (op->count > 0)) { + if (op->kt_ival1.tv64 && (op->count > 0)) { op->count--; if (!op->count && (op->flags & TX_COUNTEVT)) { - struct bcm_msg_head msg_head; /* create notification to user */ msg_head.opcode = TX_EXPIRED; @@ -453,26 +483,36 @@ static void bcm_tx_timeout_handler(unsigned long data) } } - if (op->j_ival1 && (op->count > 0)) { - - op->timer.expires = jiffies + op->j_ival1; - add_timer(&op->timer); + if (op->kt_ival1.tv64 && (op->count > 0)) { /* send (next) frame */ bcm_can_tx(op); + hrtimer_start(&op->timer, + ktime_add(ktime_get(), op->kt_ival1), + HRTIMER_MODE_ABS); } else { - if (op->j_ival2) { - op->timer.expires = jiffies + op->j_ival2; - add_timer(&op->timer); + if (op->kt_ival2.tv64) { /* send (next) frame */ bcm_can_tx(op); - + hrtimer_start(&op->timer, + ktime_add(ktime_get(), op->kt_ival2), + HRTIMER_MODE_ABS); } } +} - return; +/* + * bcm_tx_timeout_handler - performs cyclic CAN frame transmissions + */ +static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) +{ + struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); + + tasklet_schedule(&op->tsklet); + + return HRTIMER_NORESTART; } /* @@ -482,8 +522,6 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) { struct bcm_msg_head head; - op->j_lastmsg = jiffies; - /* update statistics */ op->frames_filtered++; @@ -491,6 +529,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) if (op->frames_filtered > ULONG_MAX/100) op->frames_filtered = op->frames_abs = 0; + /* this element is not throttled anymore */ + data->can_dlc &= (BCM_CAN_DLC_MASK|RX_RECV); + head.opcode = RX_CHANGED; head.flags = op->flags; head.count = op->count; @@ -502,6 +543,19 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) bcm_send_to_user(op, &head, data, 1); } +#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,25) +/* is part of linux/hrtimer.h since 2.6.26 */ +static inline int hrtimer_callback_running(struct hrtimer *timer) +{ + return timer->state & HRTIMER_STATE_CALLBACK; +} +#endif +#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,22) +static inline s64 ktime_us_delta(const ktime_t later, const ktime_t earlier) +{ + return ktime_to_us(ktime_sub(later, earlier)); +} +#endif /* * bcm_rx_update_and_send - process a detected relevant receive content change * 1. update the last received data @@ -509,41 +563,50 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) */ static void bcm_rx_update_and_send(struct bcm_op *op, struct can_frame *lastdata, - struct can_frame *rxdata) + const struct can_frame *rxdata) { - unsigned long nexttx = op->j_lastmsg + op->j_ival2; - memcpy(lastdata, rxdata, CFSIZ); - /* mark as used */ - lastdata->can_dlc |= RX_RECV; + /* mark as used and throttled by default */ + lastdata->can_dlc |= (RX_RECV|RX_THR); - /* throttle bcm_rx_changed ? */ - if ((op->thrtimer.expires) || - ((op->j_ival2) && (nexttx > jiffies))) { - /* we are already waiting OR we have to start waiting */ - - /* mark as 'throttled' */ - lastdata->can_dlc |= RX_THR; + /* throtteling mode inactive ? */ + if (!op->kt_ival2.tv64) { + /* send RX_CHANGED to the user immediately */ + bcm_rx_changed(op, lastdata); + return; + } - if (!(op->thrtimer.expires)) { - /* start the timer only the first time */ - op->thrtimer.expires = nexttx; - add_timer(&op->thrtimer); - } + /* with active throttling timer we are just done here */ + if (hrtimer_active(&op->thrtimer)) + return; - } else { - /* send RX_CHANGED to the user immediately */ - bcm_rx_changed(op, rxdata); + /* first receiption with enabled throttling mode */ + if (!op->kt_lastmsg.tv64) + goto rx_changed_settime; + + /* got a second frame inside a potential throttle period? */ + if (ktime_us_delta(ktime_get(), op->kt_lastmsg) < + ktime_to_us(op->kt_ival2)) { + /* do not send the saved data - only start throttle timer */ + hrtimer_start(&op->thrtimer, + ktime_add(op->kt_lastmsg, op->kt_ival2), + HRTIMER_MODE_ABS); + return; } + + /* the gap was that big, that throttling was not needed here */ +rx_changed_settime: + bcm_rx_changed(op, lastdata); + op->kt_lastmsg = ktime_get(); } /* * bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly * received data stored in op->last_frames[] */ -static void bcm_rx_cmp_to_index(struct bcm_op *op, int index, - struct can_frame *rxdata) +static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index, + const struct can_frame *rxdata) { /* * no one uses the MSBs of can_dlc for comparation, @@ -583,20 +646,16 @@ static void bcm_rx_starttimer(struct bcm_op *op) if (op->flags & RX_NO_AUTOTIMER) return; - if (op->j_ival1) { - op->timer.expires = jiffies + op->j_ival1; - add_timer(&op->timer); - } + if (op->kt_ival1.tv64) + hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL); } -/* - * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out - */ -static void bcm_rx_timeout_handler(unsigned long data) +static void bcm_rx_timeout_tsklet(unsigned long data) { struct bcm_op *op = (struct bcm_op *)data; struct bcm_msg_head msg_head; + /* create notification to user */ msg_head.opcode = RX_TIMEOUT; msg_head.flags = op->flags; msg_head.count = op->count; @@ -606,6 +665,17 @@ static void bcm_rx_timeout_handler(unsigned long data) msg_head.nframes = 0; bcm_send_to_user(op, &msg_head, NULL, 0); +} + +/* + * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out + */ +static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) +{ + struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); + + /* schedule before NET_RX_SOFTIRQ */ + tasklet_hi_schedule(&op->tsklet); /* no restart of the timer is done here! */ @@ -614,36 +684,74 @@ static void bcm_rx_timeout_handler(unsigned long data) /* clear received can_frames to indicate 'nothing received' */ memset(op->last_frames, 0, op->nframes * CFSIZ); } + + return HRTIMER_NORESTART; } /* - * bcm_rx_thr_handler - the time for blocked content updates is over now: - * Check for throttled data and send it to the userspace + * bcm_rx_do_flush - helper for bcm_rx_thr_flush */ -static void bcm_rx_thr_handler(unsigned long data) +static inline int bcm_rx_do_flush(struct bcm_op *op, int update, + unsigned int index) { - struct bcm_op *op = (struct bcm_op *)data; - int i = 0; + if ((op->last_frames) && (op->last_frames[index].can_dlc & RX_THR)) { + if (update) + bcm_rx_changed(op, &op->last_frames[index]); + return 1; + } + return 0; +} - /* mark disabled / consumed timer */ - op->thrtimer.expires = 0; +/* + * bcm_rx_thr_flush - Check for throttled data and send it to the userspace + * + * update == 0 : just check if throttled data is available (any irq context) + * update == 1 : check and send throttled data to userspace (soft_irq context) + */ +static int bcm_rx_thr_flush(struct bcm_op *op, int update) +{ + int updated = 0; if (op->nframes > 1) { + unsigned int i; + /* for MUX filter we start at index 1 */ - for (i = 1; i < op->nframes; i++) { - if ((op->last_frames) && - (op->last_frames[i].can_dlc & RX_THR)) { - op->last_frames[i].can_dlc &= ~RX_THR; - bcm_rx_changed(op, &op->last_frames[i]); - } - } + for (i = 1; i < op->nframes; i++) + updated += bcm_rx_do_flush(op, update, i); } else { /* for RX_FILTER_ID and simple filter */ - if (op->last_frames && (op->last_frames[0].can_dlc & RX_THR)) { - op->last_frames[0].can_dlc &= ~RX_THR; - bcm_rx_changed(op, &op->last_frames[0]); - } + updated += bcm_rx_do_flush(op, update, 0); + } + + return updated; +} + +static void bcm_rx_thr_tsklet(unsigned long data) +{ + struct bcm_op *op = (struct bcm_op *)data; + + /* push the changed data to the userspace */ + bcm_rx_thr_flush(op, 1); +} + +/* + * bcm_rx_thr_handler - the time for blocked content updates is over now: + * Check for throttled data and send it to the userspace + */ +static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) +{ + struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); + + tasklet_schedule(&op->thrtsklet); + + if (bcm_rx_thr_flush(op, 0)) { + hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2); + return HRTIMER_RESTART; + } else { + /* rearm throttle handling */ + op->kt_lastmsg = ktime_set(0, 0); + return HRTIMER_NORESTART; } } @@ -653,74 +761,60 @@ static void bcm_rx_thr_handler(unsigned long data) static void bcm_rx_handler(struct sk_buff *skb, void *data) { struct bcm_op *op = (struct bcm_op *)data; - struct can_frame rxframe; - int i; + const struct can_frame *rxframe = (struct can_frame *)skb->data; + unsigned int i; /* disable timeout */ - del_timer(&op->timer); - - if (skb->len == sizeof(rxframe)) { - memcpy(&rxframe, skb->data, sizeof(rxframe)); - /* save rx timestamp */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) - op->rx_stamp = skb->tstamp; -#else - skb_get_timestamp(skb, &op->rx_stamp); -#endif - /* save originator for recvfrom() */ - op->rx_ifindex = skb->dev->ifindex; - /* update statistics */ - op->frames_abs++; - kfree_skb(skb); + hrtimer_cancel(&op->timer); - } else { - kfree_skb(skb); + if (op->can_id != rxframe->can_id) return; - } - if (op->can_id != rxframe.can_id) - return; + /* save rx timestamp */ + op->rx_stamp = skb->tstamp; + /* save originator for recvfrom() */ + op->rx_ifindex = skb->dev->ifindex; + /* update statistics */ + op->frames_abs++; if (op->flags & RX_RTR_FRAME) { - /* send reply for RTR-request */ - /* send op->frames[0] to CAN device */ + /* send reply for RTR-request (placed in op->frames[0]) */ bcm_can_tx(op); return; } if (op->flags & RX_FILTER_ID) { /* the easiest case */ - - bcm_rx_update_and_send(op, &op->last_frames[0], &rxframe); - bcm_rx_starttimer(op); - return; + bcm_rx_update_and_send(op, &op->last_frames[0], rxframe); + goto rx_starttimer; } if (op->nframes == 1) { /* simple compare with index 0 */ - - bcm_rx_cmp_to_index(op, 0, &rxframe); - bcm_rx_starttimer(op); - return; + bcm_rx_cmp_to_index(op, 0, rxframe); + goto rx_starttimer; } if (op->nframes > 1) { - /* multiplex compare + /* + * multiplex compare * * find the first multiplex mask that fits. * Remark: The MUX-mask is stored in index 0 */ for (i = 1; i < op->nframes; i++) { - if ((GET_U64(&op->frames[0]) & GET_U64(&rxframe)) == + if ((GET_U64(&op->frames[0]) & GET_U64(rxframe)) == (GET_U64(&op->frames[0]) & GET_U64(&op->frames[i]))) { - bcm_rx_cmp_to_index(op, i, &rxframe); + bcm_rx_cmp_to_index(op, i, rxframe); break; } } - bcm_rx_starttimer(op); } + +rx_starttimer: + bcm_rx_starttimer(op); } /* @@ -741,8 +835,14 @@ static struct bcm_op *bcm_find_op(struct list_head *ops, canid_t can_id, static void bcm_remove_op(struct bcm_op *op) { - del_timer(&op->timer); - del_timer(&op->thrtimer); + hrtimer_cancel(&op->timer); + hrtimer_cancel(&op->thrtimer); + + if (op->tsklet.func) + tasklet_kill(&op->tsklet); + + if (op->thrtsklet.func) + tasklet_kill(&op->thrtsklet); if ((op->frames) && (op->frames != &op->sframe)) kfree(op->frames); @@ -792,12 +892,8 @@ static int bcm_delete_rx_op(struct list_head *ops, canid_t can_id, int ifindex) if (op->rx_reg_dev) { struct net_device *dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) dev = dev_get_by_index(&init_net, op->ifindex); -#else - dev = dev_get_by_index(op->ifindex); -#endif if (dev) { bcm_rx_unreg(dev, op); dev_put(dev); @@ -866,22 +962,24 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, { struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; - int i, err; + unsigned int i; + int err; /* we need a real device to send frames */ if (!ifindex) return -ENODEV; - /* we need at least one can_frame */ - if (msg_head->nframes < 1) + /* check nframes boundaries - we need at least one can_frame */ + if (msg_head->nframes < 1 || msg_head->nframes > MAX_NFRAMES) return -EINVAL; /* check the given can_id */ op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex); if (op) { - /* update existing BCM operation - * + /* update existing BCM operation */ + + /* * Do we need more space for the can_frames than currently * allocated? -> This is a _really_ unusual use-case and * therefore (complexity / locking) it is not supported. @@ -893,6 +991,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, for (i = 0; i < msg_head->nframes; i++) { err = memcpy_fromiovec((u8 *)&op->frames[i], msg->msg_iov, CFSIZ); + + if (op->frames[i].can_dlc > 8) + err = -EINVAL; + if (err < 0) return err; @@ -925,6 +1027,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, for (i = 0; i < msg_head->nframes; i++) { err = memcpy_fromiovec((u8 *)&op->frames[i], msg->msg_iov, CFSIZ); + + if (op->frames[i].can_dlc > 8) + err = -EINVAL; + if (err < 0) { if (op->frames != &op->sframe) kfree(op->frames); @@ -943,20 +1049,18 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* bcm_can_tx / bcm_tx_timeout_handler needs this */ op->sk = sk; - op->ifindex = ifindex; - /* initialize uninitialized (kmalloc) structure */ - init_timer(&op->timer); - - /* currently unused in tx_ops */ - init_timer(&op->thrtimer); - - /* handler for tx_ops */ + /* initialize uninitialized (kzalloc) structure */ + hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->timer.function = bcm_tx_timeout_handler; - /* timer.data points to this op-structure */ - op->timer.data = (unsigned long)op; + /* initialize tasklet for tx countevent notification */ + tasklet_init(&op->tsklet, bcm_tx_timeout_tsklet, + (unsigned long) op); + + /* currently unused in tx_ops */ + hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); /* add this bcm_op to the list of the tx_ops */ list_add(&op->list, &bo->tx_ops); @@ -980,33 +1084,30 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (op->flags & SETTIMER) { /* set timer values */ - op->count = msg_head->count; op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; - op->j_ival1 = rounded_tv2jif(&msg_head->ival1); - op->j_ival2 = rounded_tv2jif(&msg_head->ival2); + op->kt_ival1 = timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = timeval_to_ktime(msg_head->ival2); /* disable an active timer due to zero values? */ - if (!op->j_ival1 && !op->j_ival2) - del_timer(&op->timer); + if (!op->kt_ival1.tv64 && !op->kt_ival2.tv64) + hrtimer_cancel(&op->timer); } if ((op->flags & STARTTIMER) && - ((op->j_ival1 && op->count) || op->j_ival2)) { - - del_timer(&op->timer); + ((op->kt_ival1.tv64 && op->count) || op->kt_ival2.tv64)) { /* spec: send can_frame when starting timer */ op->flags |= TX_ANNOUNCE; - if (op->j_ival1 && (op->count > 0)) { - op->timer.expires = jiffies + op->j_ival1; + if (op->kt_ival1.tv64 && (op->count > 0)) { /* op->count-- is done in bcm_tx_timeout_handler */ + hrtimer_start(&op->timer, op->kt_ival1, + HRTIMER_MODE_REL); } else - op->timer.expires = jiffies + op->j_ival2; - - add_timer(&op->timer); + hrtimer_start(&op->timer, op->kt_ival2, + HRTIMER_MODE_REL); } if (op->flags & TX_ANNOUNCE) @@ -1029,9 +1130,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) { /* be robust against wrong usage ... */ msg_head->flags |= RX_FILTER_ID; - msg_head->nframes = 0; /* ignore trailing garbage */ + /* ignore trailing garbage */ + msg_head->nframes = 0; } + /* the first element contains the mux-mask => MAX_NFRAMES + 1 */ + if (msg_head->nframes > MAX_NFRAMES + 1) + return -EINVAL; + if ((msg_head->flags & RX_RTR_FRAME) && ((msg_head->nframes != 1) || (!(msg_head->can_id & CAN_RTR_FLAG)))) @@ -1040,8 +1146,9 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check the given can_id */ op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex); if (op) { - /* update existing BCM operation - * + /* update existing BCM operation */ + + /* * Do we need more space for the can_frames than currently * allocated? -> This is a _really_ unusual use-case and * therefore (complexity / locking) it is not supported. @@ -1068,7 +1175,6 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } else { /* insert new BCM operation for the given can_id */ - op = kzalloc(OPSIZ, GFP_KERNEL); if (!op) return -ENOMEM; @@ -1112,29 +1218,24 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } } + /* bcm_can_tx / bcm_tx_timeout_handler needs this */ op->sk = sk; op->ifindex = ifindex; /* initialize uninitialized (kzalloc) structure */ - init_timer(&op->timer); - - /* init throttle timer for RX_CHANGED */ - init_timer(&op->thrtimer); - - /* handler for rx timeouts */ + hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->timer.function = bcm_rx_timeout_handler; - /* timer.data points to this op-structure */ - op->timer.data = (unsigned long)op; + /* initialize tasklet for rx timeout notification */ + tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet, + (unsigned long) op); - /* handler for RX_CHANGED throttle timeouts */ + hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->thrtimer.function = bcm_rx_thr_handler; - /* timer.data points to this op-structure */ - op->thrtimer.data = (unsigned long)op; - - /* mark disabled timer */ - op->thrtimer.expires = 0; + /* initialize tasklet for rx throttle handling */ + tasklet_init(&op->thrtsklet, bcm_rx_thr_tsklet, + (unsigned long) op); /* add this bcm_op to the list of the rx_ops */ list_add(&op->list, &bo->rx_ops); @@ -1150,8 +1251,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (op->flags & RX_RTR_FRAME) { /* no timers in RTR-mode */ - del_timer(&op->thrtimer); - del_timer(&op->timer); + hrtimer_cancel(&op->thrtimer); + hrtimer_cancel(&op->timer); /* * funny feature in RX(!)_SETUP only for RTR-mode: @@ -1168,32 +1269,25 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* set timer value */ op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; - op->j_ival1 = rounded_tv2jif(&msg_head->ival1); - op->j_ival2 = rounded_tv2jif(&msg_head->ival2); + op->kt_ival1 = timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = timeval_to_ktime(msg_head->ival2); /* disable an active timer due to zero value? */ - if (!op->j_ival1) - del_timer(&op->timer); - - /* free currently blocked msgs ? */ - if (op->thrtimer.expires) { - del_timer(&op->thrtimer); - /* send blocked msgs hereafter */ - op->thrtimer.expires = jiffies + 2; - add_timer(&op->thrtimer); - } + if (!op->kt_ival1.tv64) + hrtimer_cancel(&op->timer); + /* - * if (op->j_ival2) is zero, no (new) throttling - * will happen. For details see functions - * bcm_rx_update_and_send() and bcm_rx_thr_handler() + * In any case cancel the throttle timer, flush + * potentially blocked msgs and reset throttle handling */ + op->kt_lastmsg = ktime_set(0, 0); + hrtimer_cancel(&op->thrtimer); + bcm_rx_thr_flush(op, 1); } - if ((op->flags & STARTTIMER) && op->j_ival1) { - del_timer(&op->timer); - op->timer.expires = jiffies + op->j_ival1; - add_timer(&op->timer); - } + if ((op->flags & STARTTIMER) && op->kt_ival1.tv64) + hrtimer_start(&op->timer, op->kt_ival1, + HRTIMER_MODE_REL); } /* now we can register for can_ids, if we added a new bcm_op */ @@ -1201,11 +1295,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (ifindex) { struct net_device *dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) dev = dev_get_by_index(&init_net, ifindex); -#else - dev = dev_get_by_index(ifindex); -#endif if (dev) { err = can_rx_register(dev, op->can_id, REGMASK(op->can_id), @@ -1240,9 +1330,8 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) struct net_device *dev; int err; - /* just copy and send one can_frame */ - - if (!ifindex) /* we need a real device to send frames */ + /* we need a real device to send frames */ + if (!ifindex) return -ENODEV; skb = alloc_skb(CFSIZ, GFP_KERNEL); @@ -1256,11 +1345,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) return err; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) dev = dev_get_by_index(&init_net, ifindex); -#else - dev = dev_get_by_index(ifindex); -#endif if (!dev) { kfree_skb(skb); return -ENODEV; @@ -1268,9 +1353,12 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) skb->dev = dev; skb->sk = sk; - can_send(skb, 1); /* send with loopback */ + err = can_send(skb, 1); /* send with loopback */ dev_put(dev); + if (err) + return err; + return CFSIZ + MHSIZ; } @@ -1289,6 +1377,10 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, if (!bo->bound) return -ENOTCONN; + /* check for valid message length from userspace */ + if (size < MHSIZ || (size - MHSIZ) % CFSIZ) + return -EINVAL; + /* check for alternative ifindex for this bcm_op */ if (!ifindex && msg->msg_name) { @@ -1296,19 +1388,19 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, struct sockaddr_can *addr = (struct sockaddr_can *)msg->msg_name; + if (msg->msg_namelen < sizeof(*addr)) + return -EINVAL; + if (addr->can_family != AF_CAN) return -EINVAL; - ifindex = addr->can_ifindex; /* ifindex from sendto() */ + /* ifindex from sendto() */ + ifindex = addr->can_ifindex; if (ifindex) { struct net_device *dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) dev = dev_get_by_index(&init_net, ifindex); -#else - dev = dev_get_by_index(ifindex); -#endif if (!dev) return -ENODEV; @@ -1366,8 +1458,8 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, break; case TX_SEND: - /* we need at least one can_frame */ - if (msg_head.nframes < 1) + /* we need exactly one can_frame behind the msg head */ + if ((msg_head.nframes != 1) || (size != CFSIZ + MHSIZ)) ret = -EINVAL; else ret = bcm_tx_send(msg, ifindex, sk); @@ -1391,15 +1483,14 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg, { struct net_device *dev = (struct net_device *)data; struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) struct sock *sk = &bo->sk; -#else - struct sock *sk = bo->sk; -#endif struct bcm_op *op; int notify_enodev = 0; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) if (dev->nd_net != &init_net) return NOTIFY_DONE; #endif @@ -1451,9 +1542,6 @@ static int bcm_init(struct sock *sk) { struct bcm_sock *bo = bcm_sk(sk); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) - bo->sk = sk; -#endif bo->bound = 0; bo->ifindex = 0; bo->dropped_usr_msgs = 0; @@ -1476,9 +1564,14 @@ static int bcm_init(struct sock *sk) static int bcm_release(struct socket *sock) { struct sock *sk = sock->sk; - struct bcm_sock *bo = bcm_sk(sk); + struct bcm_sock *bo; struct bcm_op *op, *next; + if (sk == NULL) + return 0; + + bo = bcm_sk(sk); + /* remove bcm_ops, timer, rx_unregister(), etc. */ unregister_netdevice_notifier(&bo->notifier); @@ -1502,11 +1595,7 @@ static int bcm_release(struct socket *sock) if (op->rx_reg_dev) { struct net_device *dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) dev = dev_get_by_index(&init_net, op->ifindex); -#else - dev = dev_get_by_index(op->ifindex); -#endif if (dev) { bcm_rx_unreg(dev, op); dev_put(dev); @@ -1530,6 +1619,9 @@ static int bcm_release(struct socket *sock) bo->ifindex = 0; } + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); sock_put(sk); @@ -1543,6 +1635,9 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, struct sock *sk = sock->sk; struct bcm_sock *bo = bcm_sk(sk); + if (len < sizeof(*addr)) + return -EINVAL; + if (bo->bound) return -EISCONN; @@ -1550,11 +1645,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, if (addr->can_ifindex) { struct net_device *dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) dev = dev_get_by_index(&init_net, addr->can_ifindex); -#else - dev = dev_get_by_index(addr->can_ifindex); -#endif if (!dev) return -ENODEV; @@ -1575,10 +1666,16 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, if (proc_dir) { /* unique socket address as filename */ - sprintf(bo->procname, "%p", sock); + sprintf(bo->procname, "%lu", sock_i_ino(sk)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + bo->bcm_proc_read = proc_create_data(bo->procname, 0644, + proc_dir, + &bcm_proc_fops, sk); +#else bo->bcm_proc_read = create_proc_read_entry(bo->procname, 0644, proc_dir, bcm_read_proc, sk); +#endif } return 0; @@ -1620,7 +1717,7 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock, return size; } -static struct proto_ops bcm_ops __read_mostly = { +static const struct proto_ops bcm_ops = { .family = PF_CAN, .release = bcm_release, .bind = sock_no_bind, @@ -1629,7 +1726,7 @@ static struct proto_ops bcm_ops __read_mostly = { .accept = sock_no_accept, .getname = sock_no_getname, .poll = datagram_poll, - .ioctl = NULL, /* use can_ioctl() from af_can.c */ + .ioctl = can_ioctl, /* use can_ioctl() from af_can.c */ .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, @@ -1640,7 +1737,6 @@ static struct proto_ops bcm_ops __read_mostly = { .sendpage = sock_no_sendpage, }; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) static struct proto bcm_proto __read_mostly = { .name = "CAN_BCM", .owner = THIS_MODULE, @@ -1648,24 +1744,15 @@ static struct proto bcm_proto __read_mostly = { .init = bcm_init, }; -static struct can_proto bcm_can_proto __read_mostly = { +static const struct can_proto bcm_can_proto = { .type = SOCK_DGRAM, .protocol = CAN_BCM, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) .capability = -1, +#endif .ops = &bcm_ops, .prot = &bcm_proto, }; -#else -static struct can_proto bcm_can_proto __read_mostly = { - .type = SOCK_DGRAM, - .protocol = CAN_BCM, - .capability = -1, - .ops = &bcm_ops, - .owner = THIS_MODULE, - .obj_size = sizeof(struct bcm_sock), - .init = bcm_init, -}; -#endif static int __init bcm_module_init(void) { @@ -1686,8 +1773,10 @@ static int __init bcm_module_init(void) proc_dir = proc_mkdir("can-bcm", proc_net); #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) if (proc_dir) proc_dir->owner = THIS_MODULE; +#endif return 0; }