#include <linux/module.h>
#include <linux/init.h>
+#include <linux/hrtimer.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/uio.h>
#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");
int ifindex;
canid_t can_id;
int flags;
- unsigned long j_ival1, j_ival2, j_lastmsg;
unsigned long frames_abs, frames_filtered;
- struct timer_list timer, thrtimer;
struct timeval ival1, ival2;
- ktime_t rx_stamp;
+ 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;
#define MHSIZ sizeof(struct bcm_msg_head)
/*
- * 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;
-
- if (sec > ULONG_MAX / HZ)
- return ULONG_MAX;
-
- /* round up to get at least the requested time */
- usec += 1000000 / HZ - 1;
-
- jif = usec / (1000000 / HZ);
-
- if (sec * HZ > ULONG_MAX - jif)
- return ULONG_MAX;
-
- return jif + sec * HZ;
-}
-
-/*
* procfs functions
*/
static char *bcm_proc_getifname(int 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: ",
"tx_op: %03X %s [%d] ",
op->can_id, bcm_proc_getifname(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);
if (head->nframes) {
/* can_frames starting here */
- firstframe = (struct can_frame *) skb_tail_pointer(skb);
+ firstframe = (struct can_frame *)skb_tail_pointer(skb);
memcpy(skb_put(skb, datalen), frames, datalen);
}
}
-/*
- * 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;
}
}
- 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 - performes 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;
}
/*
{
struct bcm_msg_head head;
- op->j_lastmsg = jiffies;
-
/* update statistics */
op->frames_filtered++;
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;
*/
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();
}
/*
* received data stored in op->last_frames[]
*/
static void bcm_rx_cmp_to_index(struct bcm_op *op, int index,
- struct can_frame *rxdata)
+ const struct can_frame *rxdata)
{
/*
* no one uses the MSBs of can_dlc for comparation,
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;
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! */
/* 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, 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) {
+ 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;
}
}
static void bcm_rx_handler(struct sk_buff *skb, void *data)
{
struct bcm_op *op = (struct bcm_op *)data;
- struct can_frame rxframe;
+ const struct can_frame *rxframe = (struct can_frame *)skb->data;
int i;
/* disable timeout */
- del_timer(&op->timer);
-
- if (skb->len == sizeof(rxframe)) {
- memcpy(&rxframe, skb->data, sizeof(rxframe));
- /* save rx timestamp */
- op->rx_stamp = skb->tstamp;
- /* 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]) */
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) {
*/
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);
}
/*
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);
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;
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);
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);
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)
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;
+
+ /* initialize tasklet for rx timeout notification */
+ tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet,
+ (unsigned long) op);
- /* init throttle timer for RX_CHANGED */
- setup_timer(&op->thrtimer, bcm_rx_thr_handler,
- (unsigned long)op);
+ hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ op->thrtimer.function = bcm_rx_thr_handler;
- /* 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);
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:
/* 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 */
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;
}
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) {
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);
struct bcm_op *op;
int notify_enodev = 0;
- if (dev->nd_net != &init_net)
+ if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN)