diff options
| author | Oliver Hartkopp <oliver@hartkopp.net> | 2009-01-15 00:06:55 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2009-01-15 00:06:55 -0500 |
| commit | c53a6ee88b0a91bd012ef1b7988c0b93dae6f24d (patch) | |
| tree | 29cd62321b70f58a72b20cbd30bb933b06525df7 /net/can | |
| parent | d57bc36e7aba9e3a00d154f5eff80ff596146fc4 (diff) | |
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] [<c012228c>] warn_slowpath+0x80/0xb6
[ 700.361163] [<c013d559>] valid_state+0x125/0x136
[ 700.361171] [<c013d858>] mark_lock+0x18e/0x332
[ 700.361180] [<c013e300>] __lock_acquire+0x12e/0xb1e
[ 700.361189] [<f8ab5915>] bcm_tx_timeout_handler+0x0/0xbc [can_bcm]
[ 700.361198] [<c031e20a>] dev_queue_xmit+0x191/0x479
[ 700.361206] [<c01262a7>] __local_bh_disable+0x2b/0x64
[ 700.361213] [<c031e20a>] dev_queue_xmit+0x191/0x479
[ 700.361225] [<f8aa69a1>] can_send+0xd7/0x11a [can]
[ 700.361235] [<f8ab522b>] bcm_can_tx+0x9d/0xd9 [can_bcm]
[ 700.361245] [<f8ab597f>] bcm_tx_timeout_handler+0x6a/0xbc [can_bcm]
[ 700.361255] [<f8ab5915>] bcm_tx_timeout_handler+0x0/0xbc [can_bcm]
[ 700.361263] [<c0134143>] __run_hrtimer+0x5a/0x86
[ 700.361273] [<f8ab5915>] bcm_tx_timeout_handler+0x0/0xbc [can_bcm]
[ 700.361282] [<c0134a50>] 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 <oliver@hartkopp.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/can')
| -rw-r--r-- | net/can/bcm.c | 57 |
1 files changed, 30 insertions, 27 deletions
diff --git a/net/can/bcm.c b/net/can/bcm.c index 1649c8ab2c2f..b7c7d4651136 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c | |||
| @@ -347,51 +347,54 @@ static void bcm_tx_timeout_tsklet(unsigned long data) | |||
| 347 | struct bcm_op *op = (struct bcm_op *)data; | 347 | struct bcm_op *op = (struct bcm_op *)data; |
| 348 | struct bcm_msg_head msg_head; | 348 | struct bcm_msg_head msg_head; |
| 349 | 349 | ||
| 350 | /* create notification to user */ | ||
| 351 | msg_head.opcode = TX_EXPIRED; | ||
| 352 | msg_head.flags = op->flags; | ||
| 353 | msg_head.count = op->count; | ||
| 354 | msg_head.ival1 = op->ival1; | ||
| 355 | msg_head.ival2 = op->ival2; | ||
| 356 | msg_head.can_id = op->can_id; | ||
| 357 | msg_head.nframes = 0; | ||
| 358 | |||
| 359 | bcm_send_to_user(op, &msg_head, NULL, 0); | ||
| 360 | } | ||
| 361 | |||
| 362 | /* | ||
| 363 | * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions | ||
| 364 | */ | ||
| 365 | static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) | ||
| 366 | { | ||
| 367 | struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); | ||
| 368 | enum hrtimer_restart ret = HRTIMER_NORESTART; | ||
| 369 | |||
| 370 | if (op->kt_ival1.tv64 && (op->count > 0)) { | 350 | if (op->kt_ival1.tv64 && (op->count > 0)) { |
| 371 | 351 | ||
| 372 | op->count--; | 352 | op->count--; |
| 373 | if (!op->count && (op->flags & TX_COUNTEVT)) | 353 | if (!op->count && (op->flags & TX_COUNTEVT)) { |
| 374 | tasklet_schedule(&op->tsklet); | 354 | |
| 355 | /* create notification to user */ | ||
| 356 | msg_head.opcode = TX_EXPIRED; | ||
| 357 | msg_head.flags = op->flags; | ||
| 358 | msg_head.count = op->count; | ||
| 359 | msg_head.ival1 = op->ival1; | ||
| 360 | msg_head.ival2 = op->ival2; | ||
| 361 | msg_head.can_id = op->can_id; | ||
| 362 | msg_head.nframes = 0; | ||
| 363 | |||
| 364 | bcm_send_to_user(op, &msg_head, NULL, 0); | ||
| 365 | } | ||
| 375 | } | 366 | } |
| 376 | 367 | ||
| 377 | if (op->kt_ival1.tv64 && (op->count > 0)) { | 368 | if (op->kt_ival1.tv64 && (op->count > 0)) { |
| 378 | 369 | ||
| 379 | /* send (next) frame */ | 370 | /* send (next) frame */ |
| 380 | bcm_can_tx(op); | 371 | bcm_can_tx(op); |
| 381 | hrtimer_forward(hrtimer, ktime_get(), op->kt_ival1); | 372 | hrtimer_start(&op->timer, |
| 382 | ret = HRTIMER_RESTART; | 373 | ktime_add(ktime_get(), op->kt_ival1), |
| 374 | HRTIMER_MODE_ABS); | ||
| 383 | 375 | ||
| 384 | } else { | 376 | } else { |
| 385 | if (op->kt_ival2.tv64) { | 377 | if (op->kt_ival2.tv64) { |
| 386 | 378 | ||
| 387 | /* send (next) frame */ | 379 | /* send (next) frame */ |
| 388 | bcm_can_tx(op); | 380 | bcm_can_tx(op); |
| 389 | hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2); | 381 | hrtimer_start(&op->timer, |
| 390 | ret = HRTIMER_RESTART; | 382 | ktime_add(ktime_get(), op->kt_ival2), |
| 383 | HRTIMER_MODE_ABS); | ||
| 391 | } | 384 | } |
| 392 | } | 385 | } |
| 386 | } | ||
| 393 | 387 | ||
| 394 | return ret; | 388 | /* |
| 389 | * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions | ||
| 390 | */ | ||
| 391 | static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) | ||
| 392 | { | ||
| 393 | struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); | ||
| 394 | |||
| 395 | tasklet_schedule(&op->tsklet); | ||
| 396 | |||
| 397 | return HRTIMER_NORESTART; | ||
| 395 | } | 398 | } |
| 396 | 399 | ||
| 397 | /* | 400 | /* |
