| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Thu, 31 Aug 2017 11:03:10 +0000 |
| Subject: [PATCH 19/25] can/bcm: Replace hrtimer_tasklet with softirq based |
| hrtimer |
| |
| Switch the timer to CLOCK_MONOTONIC_SOFT, which executed the timer |
| callback in softirq context and remove the hrtimer_tasklet. |
| |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de> |
| Cc: Oliver Hartkopp <socketcan@hartkopp.net> |
| Cc: Marc Kleine-Budde <mkl@pengutronix.de> |
| Cc: linux-can@vger.kernel.org |
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| --- |
| net/can/bcm.c | 150 ++++++++++++++++++---------------------------------------- |
| 1 file changed, 49 insertions(+), 101 deletions(-) |
| |
| --- a/net/can/bcm.c |
| +++ b/net/can/bcm.c |
| @@ -102,7 +102,6 @@ struct bcm_op { |
| unsigned long frames_abs, frames_filtered; |
| struct bcm_timeval ival1, ival2; |
| struct hrtimer timer, thrtimer; |
| - struct tasklet_struct tsklet, thrtsklet; |
| ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; |
| int rx_ifindex; |
| int cfsiz; |
| @@ -363,25 +362,34 @@ static void bcm_send_to_user(struct bcm_ |
| } |
| } |
| |
| -static void bcm_tx_start_timer(struct bcm_op *op) |
| +static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt) |
| { |
| + ktime_t ival; |
| + |
| if (op->kt_ival1 && op->count) |
| - hrtimer_start(&op->timer, |
| - ktime_add(ktime_get(), op->kt_ival1), |
| - HRTIMER_MODE_ABS); |
| + ival = op->kt_ival1; |
| else if (op->kt_ival2) |
| - hrtimer_start(&op->timer, |
| - ktime_add(ktime_get(), op->kt_ival2), |
| - HRTIMER_MODE_ABS); |
| + ival = op->kt_ival2; |
| + else |
| + return false; |
| + |
| + hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival)); |
| + return true; |
| } |
| |
| -static void bcm_tx_timeout_tsklet(unsigned long data) |
| +static void bcm_tx_start_timer(struct bcm_op *op) |
| { |
| - struct bcm_op *op = (struct bcm_op *)data; |
| + if (bcm_tx_set_expiry(op, &op->timer)) |
| + hrtimer_start_expires(&op->timer, HRTIMER_MODE_ABS); |
| +} |
| + |
| +/* 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); |
| struct bcm_msg_head msg_head; |
| |
| if (op->kt_ival1 && (op->count > 0)) { |
| - |
| op->count--; |
| if (!op->count && (op->flags & TX_COUNTEVT)) { |
| |
| @@ -398,22 +406,12 @@ static void bcm_tx_timeout_tsklet(unsign |
| } |
| bcm_can_tx(op); |
| |
| - } else if (op->kt_ival2) |
| + } else if (op->kt_ival2) { |
| bcm_can_tx(op); |
| + } |
| |
| - bcm_tx_start_timer(op); |
| -} |
| - |
| -/* |
| - * 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; |
| + return bcm_tx_set_expiry(op, &op->timer) ? |
| + HRTIMER_RESTART : HRTIMER_NORESTART; |
| } |
| |
| /* |
| @@ -541,11 +539,18 @@ static void bcm_rx_starttimer(struct bcm |
| hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL); |
| } |
| |
| -static void bcm_rx_timeout_tsklet(unsigned long data) |
| +/* bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out */ |
| +static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) |
| { |
| - struct bcm_op *op = (struct bcm_op *)data; |
| + struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); |
| struct bcm_msg_head msg_head; |
| |
| + /* if user wants to be informed, when cyclic CAN-Messages come back */ |
| + if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) { |
| + /* clear received CAN frames to indicate 'nothing received' */ |
| + memset(op->last_frames, 0, op->nframes * op->cfsiz); |
| + } |
| + |
| /* create notification to user */ |
| msg_head.opcode = RX_TIMEOUT; |
| msg_head.flags = op->flags; |
| @@ -556,25 +561,6 @@ static void bcm_rx_timeout_tsklet(unsign |
| msg_head.nframes = 0; |
| |
| bcm_send_to_user(op, &msg_head, NULL, 0); |
| -} |
| - |
| -/* |
| - * bcm_rx_timeout_handler - when the (cyclic) CAN frame reception 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! */ |
| - |
| - /* if user wants to be informed, when cyclic CAN-Messages come back */ |
| - if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) { |
| - /* clear received CAN frames to indicate 'nothing received' */ |
| - memset(op->last_frames, 0, op->nframes * op->cfsiz); |
| - } |
| |
| return HRTIMER_NORESTART; |
| } |
| @@ -582,14 +568,12 @@ static enum hrtimer_restart bcm_rx_timeo |
| /* |
| * bcm_rx_do_flush - helper for bcm_rx_thr_flush |
| */ |
| -static inline int bcm_rx_do_flush(struct bcm_op *op, int update, |
| - unsigned int index) |
| +static inline int bcm_rx_do_flush(struct bcm_op *op, unsigned int index) |
| { |
| struct canfd_frame *lcf = op->last_frames + op->cfsiz * index; |
| |
| if ((op->last_frames) && (lcf->flags & RX_THR)) { |
| - if (update) |
| - bcm_rx_changed(op, lcf); |
| + bcm_rx_changed(op, lcf); |
| return 1; |
| } |
| return 0; |
| @@ -597,11 +581,8 @@ static inline int bcm_rx_do_flush(struct |
| |
| /* |
| * 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) |
| +static int bcm_rx_thr_flush(struct bcm_op *op) |
| { |
| int updated = 0; |
| |
| @@ -610,24 +591,16 @@ static int bcm_rx_thr_flush(struct bcm_o |
| |
| /* for MUX filter we start at index 1 */ |
| for (i = 1; i < op->nframes; i++) |
| - updated += bcm_rx_do_flush(op, update, i); |
| + updated += bcm_rx_do_flush(op, i); |
| |
| } else { |
| /* for RX_FILTER_ID and simple filter */ |
| - updated += bcm_rx_do_flush(op, update, 0); |
| + updated += bcm_rx_do_flush(op, 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 |
| @@ -636,9 +609,7 @@ static enum hrtimer_restart bcm_rx_thr_h |
| { |
| struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); |
| |
| - tasklet_schedule(&op->thrtsklet); |
| - |
| - if (bcm_rx_thr_flush(op, 0)) { |
| + if (bcm_rx_thr_flush(op)) { |
| hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2); |
| return HRTIMER_RESTART; |
| } else { |
| @@ -734,23 +705,8 @@ static struct bcm_op *bcm_find_op(struct |
| |
| static void bcm_remove_op(struct bcm_op *op) |
| { |
| - if (op->tsklet.func) { |
| - while (test_bit(TASKLET_STATE_SCHED, &op->tsklet.state) || |
| - test_bit(TASKLET_STATE_RUN, &op->tsklet.state) || |
| - hrtimer_active(&op->timer)) { |
| - hrtimer_cancel(&op->timer); |
| - tasklet_kill(&op->tsklet); |
| - } |
| - } |
| - |
| - if (op->thrtsklet.func) { |
| - while (test_bit(TASKLET_STATE_SCHED, &op->thrtsklet.state) || |
| - test_bit(TASKLET_STATE_RUN, &op->thrtsklet.state) || |
| - hrtimer_active(&op->thrtimer)) { |
| - hrtimer_cancel(&op->thrtimer); |
| - tasklet_kill(&op->thrtsklet); |
| - } |
| - } |
| + hrtimer_cancel(&op->timer); |
| + hrtimer_cancel(&op->thrtimer); |
| |
| if ((op->frames) && (op->frames != &op->sframe)) |
| kfree(op->frames); |
| @@ -977,15 +933,13 @@ static int bcm_tx_setup(struct bcm_msg_h |
| op->ifindex = ifindex; |
| |
| /* initialize uninitialized (kzalloc) structure */ |
| - hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| + hrtimer_init(&op->timer, CLOCK_MONOTONIC_SOFT, |
| + 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 */ |
| - hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| + hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC_SOFT, |
| + HRTIMER_MODE_REL); |
| |
| /* add this bcm_op to the list of the tx_ops */ |
| list_add(&op->list, &bo->tx_ops); |
| @@ -1148,20 +1102,14 @@ static int bcm_rx_setup(struct bcm_msg_h |
| op->rx_ifindex = ifindex; |
| |
| /* initialize uninitialized (kzalloc) structure */ |
| - hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| + hrtimer_init(&op->timer, CLOCK_MONOTONIC_SOFT, |
| + 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); |
| - |
| - hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| + hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC_SOFT, |
| + 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); |
| |
| @@ -1207,7 +1155,7 @@ static int bcm_rx_setup(struct bcm_msg_h |
| */ |
| op->kt_lastmsg = 0; |
| hrtimer_cancel(&op->thrtimer); |
| - bcm_rx_thr_flush(op, 1); |
| + bcm_rx_thr_flush(op); |
| } |
| |
| if ((op->flags & STARTTIMER) && op->kt_ival1) |