X-Git-Url: http://rtime.felk.cvut.cz/gitweb/socketcan-devel.git/blobdiff_plain/0d0749f808112d6dc0c1d076058f5f9de29f5443..cb42c174307d6fe15f5602716966dd9f6142cc57:/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 5769bce..1b013a1 100644 --- a/kernel/2.6/net/can/bcm.c +++ b/kernel/2.6/net/can/bcm.c @@ -44,41 +44,62 @@ #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 +111,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 +132,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,106 +140,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) { - /* no usage counting */ -#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 dev->name; + seq_printf(m, "tx_op: %03X %s [%u] ", + op->can_id, + bcm_proc_getifname(ifname, op->ifindex), + op->nframes); + + if (op->kt_ival1.tv64) + seq_printf(m, "t1=%lld ", + (long long) ktime_to_us(op->kt_ival1)); - return "???"; + 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); @@ -239,7 +275,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) { @@ -252,17 +288,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: ", @@ -284,15 +324,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); @@ -309,6 +351,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 @@ -324,11 +367,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; @@ -367,7 +406,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()); @@ -378,11 +417,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); @@ -398,11 +433,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 } /* @@ -428,18 +459,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; @@ -454,22 +482,36 @@ static void bcm_tx_timeout_handler(unsigned long data) } } - if (op->j_ival1 && (op->count > 0)) { + if (op->kt_ival1.tv64 && (op->count > 0)) { /* send (next) frame */ bcm_can_tx(op); - mod_timer(&op->timer, jiffies + op->j_ival1); + hrtimer_start(&op->timer, + ktime_add(ktime_get(), op->kt_ival1), + HRTIMER_MODE_ABS); } else { - if (op->j_ival2) { + if (op->kt_ival2.tv64) { /* send (next) frame */ bcm_can_tx(op); - mod_timer(&op->timer, jiffies + op->j_ival2); + 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; } /* @@ -479,8 +521,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++; @@ -488,6 +528,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; @@ -499,6 +542,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 @@ -506,40 +562,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; - - /* 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 used and throttled by default */ + lastdata->can_dlc |= (RX_RECV|RX_THR); - /* 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 */ - mod_timer(&op->thrtimer, nexttx); - } + /* 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, @@ -579,18 +645,16 @@ static void bcm_rx_starttimer(struct bcm_op *op) if (op->flags & RX_NO_AUTOTIMER) return; - if (op->j_ival1) - mod_timer(&op->timer, jiffies + op->j_ival1); + 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; @@ -600,6 +664,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! */ @@ -608,36 +683,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; } } @@ -647,33 +760,21 @@ 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 (placed in op->frames[0]) */ @@ -683,16 +784,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) 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) { @@ -704,15 +803,17 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) */ 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); } /* @@ -733,8 +834,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); @@ -784,12 +891,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); @@ -858,14 +961,15 @@ 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 */ @@ -886,6 +990,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; @@ -918,6 +1026,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); @@ -939,11 +1051,15 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->ifindex = ifindex; /* initialize uninitialized (kzalloc) structure */ - setup_timer(&op->timer, bcm_tx_timeout_handler, - (unsigned long)op); + hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + op->timer.function = bcm_tx_timeout_handler; + + /* initialize tasklet for tx countevent notification */ + tasklet_init(&op->tsklet, bcm_tx_timeout_tsklet, + (unsigned long) op); /* currently unused in tx_ops */ - init_timer(&op->thrtimer); + 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); @@ -970,25 +1086,27 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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)) { + ((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)) { + if (op->kt_ival1.tv64 && (op->count > 0)) { /* op->count-- is done in bcm_tx_timeout_handler */ - mod_timer(&op->timer, jiffies + op->j_ival1); + hrtimer_start(&op->timer, op->kt_ival1, + HRTIMER_MODE_REL); } else - mod_timer(&op->timer, jiffies + op->j_ival2); + hrtimer_start(&op->timer, op->kt_ival2, + HRTIMER_MODE_REL); } if (op->flags & TX_ANNOUNCE) @@ -1015,6 +1133,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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)))) @@ -1100,15 +1222,19 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->ifindex = ifindex; /* initialize uninitialized (kzalloc) structure */ - setup_timer(&op->timer, bcm_rx_timeout_handler, - (unsigned long)op); + hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + op->timer.function = bcm_rx_timeout_handler; - /* init throttle timer for RX_CHANGED */ - setup_timer(&op->thrtimer, bcm_rx_thr_handler, - (unsigned long)op); + /* initialize tasklet for rx timeout notification */ + tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet, + (unsigned long) op); - /* mark disabled timer */ - op->thrtimer.expires = 0; + hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + op->thrtimer.function = bcm_rx_thr_handler; + + /* 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); @@ -1124,8 +1250,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: @@ -1142,28 +1268,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) { - /* send blocked msgs hereafter */ - mod_timer(&op->thrtimer, jiffies + 2); - } + 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) - mod_timer(&op->timer, jiffies + op->j_ival1); + 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 */ @@ -1171,11 +1294,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), @@ -1225,11 +1344,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; @@ -1237,9 +1352,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; } @@ -1258,6 +1376,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) { @@ -1265,6 +1387,9 @@ 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; @@ -1274,11 +1399,7 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, 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; @@ -1336,8 +1457,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); @@ -1361,15 +1482,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 @@ -1421,9 +1541,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; @@ -1446,9 +1563,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); @@ -1472,11 +1594,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); @@ -1500,6 +1618,9 @@ static int bcm_release(struct socket *sock) bo->ifindex = 0; } + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); sock_put(sk); @@ -1513,6 +1634,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; @@ -1520,11 +1644,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; @@ -1545,10 +1665,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; @@ -1590,7 +1716,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, @@ -1599,7 +1725,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, @@ -1610,7 +1736,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, @@ -1618,24 +1743,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) { @@ -1656,8 +1772,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; }