From c53a6ee88b0a91bd012ef1b7988c0b93dae6f24d Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 14 Jan 2009 21:06:55 -0800 Subject: [PATCH] can: fix slowpath issue in hrtimer callback function Due to the loopback functionality in can_send() we can not invoke it from hardirq context which was done inside the bcm_tx_timeout_handler() hrtimer callback: [ 700.361154] [] warn_slowpath+0x80/0xb6 [ 700.361163] [] valid_state+0x125/0x136 [ 700.361171] [] mark_lock+0x18e/0x332 [ 700.361180] [] __lock_acquire+0x12e/0xb1e [ 700.361189] [] bcm_tx_timeout_handler+0x0/0xbc [can_bcm] [ 700.361198] [] dev_queue_xmit+0x191/0x479 [ 700.361206] [] __local_bh_disable+0x2b/0x64 [ 700.361213] [] dev_queue_xmit+0x191/0x479 [ 700.361225] [] can_send+0xd7/0x11a [can] [ 700.361235] [] bcm_can_tx+0x9d/0xd9 [can_bcm] [ 700.361245] [] bcm_tx_timeout_handler+0x6a/0xbc [can_bcm] [ 700.361255] [] bcm_tx_timeout_handler+0x0/0xbc [can_bcm] [ 700.361263] [] __run_hrtimer+0x5a/0x86 [ 700.361273] [] bcm_tx_timeout_handler+0x0/0xbc [can_bcm] [ 700.361282] [] hrtimer_interrupt+0xb9/0x110 This patch moves the rest of the functionality from the hrtimer callback to the already existing tasklet to fix this slowpath problem. Signed-off-by: Oliver Hartkopp Signed-off-by: David S. Miller --- net/can/bcm.c | 71 +++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 1649c8ab2c2..b7c7d465113 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -347,16 +347,42 @@ static void bcm_tx_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 = TX_EXPIRED; - msg_head.flags = op->flags; - msg_head.count = op->count; - msg_head.ival1 = op->ival1; - msg_head.ival2 = op->ival2; - msg_head.can_id = op->can_id; - msg_head.nframes = 0; + if (op->kt_ival1.tv64 && (op->count > 0)) { - bcm_send_to_user(op, &msg_head, NULL, 0); + op->count--; + if (!op->count && (op->flags & TX_COUNTEVT)) { + + /* create notification to user */ + msg_head.opcode = TX_EXPIRED; + msg_head.flags = op->flags; + msg_head.count = op->count; + msg_head.ival1 = op->ival1; + msg_head.ival2 = op->ival2; + msg_head.can_id = op->can_id; + msg_head.nframes = 0; + + bcm_send_to_user(op, &msg_head, NULL, 0); + } + } + + 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->kt_ival2.tv64) { + + /* send (next) frame */ + bcm_can_tx(op); + hrtimer_start(&op->timer, + ktime_add(ktime_get(), op->kt_ival2), + HRTIMER_MODE_ABS); + } + } } /* @@ -365,33 +391,10 @@ static void bcm_tx_timeout_tsklet(unsigned long data) static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); - enum hrtimer_restart ret = HRTIMER_NORESTART; - if (op->kt_ival1.tv64 && (op->count > 0)) { + tasklet_schedule(&op->tsklet); - op->count--; - if (!op->count && (op->flags & TX_COUNTEVT)) - tasklet_schedule(&op->tsklet); - } - - if (op->kt_ival1.tv64 && (op->count > 0)) { - - /* send (next) frame */ - bcm_can_tx(op); - hrtimer_forward(hrtimer, ktime_get(), op->kt_ival1); - ret = HRTIMER_RESTART; - - } else { - if (op->kt_ival2.tv64) { - - /* send (next) frame */ - bcm_can_tx(op); - hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2); - ret = HRTIMER_RESTART; - } - } - - return ret; + return HRTIMER_NORESTART; } /*