diff options
| author | Stephen Hemminger <shemminger@vyatta.com> | 2009-09-22 15:41:43 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2009-09-24 18:38:50 -0400 |
| commit | ef87979c273a2348430648ad1d5c2a5e5df50b6e (patch) | |
| tree | 944be9878e1a54d68f612374d4896d7e61820c0b | |
| parent | 6b80d6a6b4e27fd355641823c5f4ac3f96d072e5 (diff) | |
pktgen: better scheduler friendliness
Previous update did not resched in inner loop causing watchdogs.
Rewrite inner loop to:
* account for delays better with less clock calls
* more accurate timing of delay:
- only delay if packet was successfully sent
- if delay is 100ns and it takes 10ns to build packet then
account for that
* use wait_event_interruptible_timeout rather than open coding it.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | net/core/pktgen.c | 151 |
1 files changed, 72 insertions, 79 deletions
diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 475f52530b6c..4d11c28ca8ca 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c | |||
| @@ -2104,7 +2104,7 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev) | |||
| 2104 | 2104 | ||
| 2105 | static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until) | 2105 | static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until) |
| 2106 | { | 2106 | { |
| 2107 | ktime_t start; | 2107 | ktime_t start_time, end_time; |
| 2108 | s32 remaining; | 2108 | s32 remaining; |
| 2109 | struct hrtimer_sleeper t; | 2109 | struct hrtimer_sleeper t; |
| 2110 | 2110 | ||
| @@ -2115,7 +2115,7 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until) | |||
| 2115 | if (remaining <= 0) | 2115 | if (remaining <= 0) |
| 2116 | return; | 2116 | return; |
| 2117 | 2117 | ||
| 2118 | start = ktime_now(); | 2118 | start_time = ktime_now(); |
| 2119 | if (remaining < 100) | 2119 | if (remaining < 100) |
| 2120 | udelay(remaining); /* really small just spin */ | 2120 | udelay(remaining); /* really small just spin */ |
| 2121 | else { | 2121 | else { |
| @@ -2134,7 +2134,10 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until) | |||
| 2134 | } while (t.task && pkt_dev->running && !signal_pending(current)); | 2134 | } while (t.task && pkt_dev->running && !signal_pending(current)); |
| 2135 | __set_current_state(TASK_RUNNING); | 2135 | __set_current_state(TASK_RUNNING); |
| 2136 | } | 2136 | } |
| 2137 | pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), start)); | 2137 | end_time = ktime_now(); |
| 2138 | |||
| 2139 | pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time)); | ||
| 2140 | pkt_dev->next_tx = ktime_add_ns(end_time, pkt_dev->delay); | ||
| 2138 | } | 2141 | } |
| 2139 | 2142 | ||
| 2140 | static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev) | 2143 | static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev) |
| @@ -3364,19 +3367,29 @@ static void pktgen_rem_thread(struct pktgen_thread *t) | |||
| 3364 | mutex_unlock(&pktgen_thread_lock); | 3367 | mutex_unlock(&pktgen_thread_lock); |
| 3365 | } | 3368 | } |
| 3366 | 3369 | ||
| 3367 | static void idle(struct pktgen_dev *pkt_dev) | 3370 | static void pktgen_resched(struct pktgen_dev *pkt_dev) |
| 3368 | { | 3371 | { |
| 3369 | ktime_t idle_start = ktime_now(); | 3372 | ktime_t idle_start = ktime_now(); |
| 3373 | schedule(); | ||
| 3374 | pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start)); | ||
| 3375 | } | ||
| 3370 | 3376 | ||
| 3371 | if (need_resched()) | 3377 | static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev) |
| 3372 | schedule(); | 3378 | { |
| 3373 | else | 3379 | ktime_t idle_start = ktime_now(); |
| 3374 | cpu_relax(); | ||
| 3375 | 3380 | ||
| 3381 | while (atomic_read(&(pkt_dev->skb->users)) != 1) { | ||
| 3382 | if (signal_pending(current)) | ||
| 3383 | break; | ||
| 3384 | |||
| 3385 | if (need_resched()) | ||
| 3386 | pktgen_resched(pkt_dev); | ||
| 3387 | else | ||
| 3388 | cpu_relax(); | ||
| 3389 | } | ||
| 3376 | pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start)); | 3390 | pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start)); |
| 3377 | } | 3391 | } |
| 3378 | 3392 | ||
| 3379 | |||
| 3380 | static void pktgen_xmit(struct pktgen_dev *pkt_dev) | 3393 | static void pktgen_xmit(struct pktgen_dev *pkt_dev) |
| 3381 | { | 3394 | { |
| 3382 | struct net_device *odev = pkt_dev->odev; | 3395 | struct net_device *odev = pkt_dev->odev; |
| @@ -3386,36 +3399,21 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) | |||
| 3386 | u16 queue_map; | 3399 | u16 queue_map; |
| 3387 | int ret; | 3400 | int ret; |
| 3388 | 3401 | ||
| 3389 | if (pkt_dev->delay) { | 3402 | /* If device is offline, then don't send */ |
| 3390 | spin(pkt_dev, pkt_dev->next_tx); | 3403 | if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) { |
| 3391 | 3404 | pktgen_stop_device(pkt_dev); | |
| 3392 | /* This is max DELAY, this has special meaning of | 3405 | return; |
| 3393 | * "never transmit" | ||
| 3394 | */ | ||
| 3395 | if (pkt_dev->delay == ULLONG_MAX) { | ||
| 3396 | pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX); | ||
| 3397 | return; | ||
| 3398 | } | ||
| 3399 | } | ||
| 3400 | |||
| 3401 | if (!pkt_dev->skb) { | ||
| 3402 | set_cur_queue_map(pkt_dev); | ||
| 3403 | queue_map = pkt_dev->cur_queue_map; | ||
| 3404 | } else { | ||
| 3405 | queue_map = skb_get_queue_mapping(pkt_dev->skb); | ||
| 3406 | } | 3406 | } |
| 3407 | 3407 | ||
| 3408 | txq = netdev_get_tx_queue(odev, queue_map); | 3408 | /* This is max DELAY, this has special meaning of |
| 3409 | /* Did we saturate the queue already? */ | 3409 | * "never transmit" |
| 3410 | if (netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq)) { | 3410 | */ |
| 3411 | /* If device is down, then all queues are permnantly frozen */ | 3411 | if (unlikely(pkt_dev->delay == ULLONG_MAX)) { |
| 3412 | if (netif_running(odev)) | 3412 | pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX); |
| 3413 | idle(pkt_dev); | ||
| 3414 | else | ||
| 3415 | pktgen_stop_device(pkt_dev); | ||
| 3416 | return; | 3413 | return; |
| 3417 | } | 3414 | } |
| 3418 | 3415 | ||
| 3416 | /* If no skb or clone count exhausted then get new one */ | ||
| 3419 | if (!pkt_dev->skb || (pkt_dev->last_ok && | 3417 | if (!pkt_dev->skb || (pkt_dev->last_ok && |
| 3420 | ++pkt_dev->clone_count >= pkt_dev->clone_skb)) { | 3418 | ++pkt_dev->clone_count >= pkt_dev->clone_skb)) { |
| 3421 | /* build a new pkt */ | 3419 | /* build a new pkt */ |
| @@ -3434,54 +3432,45 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) | |||
| 3434 | pkt_dev->clone_count = 0; /* reset counter */ | 3432 | pkt_dev->clone_count = 0; /* reset counter */ |
| 3435 | } | 3433 | } |
| 3436 | 3434 | ||
| 3437 | /* fill_packet() might have changed the queue */ | 3435 | if (pkt_dev->delay && pkt_dev->last_ok) |
| 3436 | spin(pkt_dev, pkt_dev->next_tx); | ||
| 3437 | |||
| 3438 | queue_map = skb_get_queue_mapping(pkt_dev->skb); | 3438 | queue_map = skb_get_queue_mapping(pkt_dev->skb); |
| 3439 | txq = netdev_get_tx_queue(odev, queue_map); | 3439 | txq = netdev_get_tx_queue(odev, queue_map); |
| 3440 | 3440 | ||
| 3441 | __netif_tx_lock_bh(txq); | 3441 | __netif_tx_lock_bh(txq); |
| 3442 | if (unlikely(netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq))) | 3442 | atomic_inc(&(pkt_dev->skb->users)); |
| 3443 | pkt_dev->last_ok = 0; | ||
| 3444 | else { | ||
| 3445 | atomic_inc(&(pkt_dev->skb->users)); | ||
| 3446 | 3443 | ||
| 3447 | retry_now: | 3444 | if (unlikely(netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq))) |
| 3445 | ret = NETDEV_TX_BUSY; | ||
| 3446 | else | ||
| 3448 | ret = (*xmit)(pkt_dev->skb, odev); | 3447 | ret = (*xmit)(pkt_dev->skb, odev); |
| 3449 | switch (ret) { | 3448 | |
| 3450 | case NETDEV_TX_OK: | 3449 | switch (ret) { |
| 3451 | txq_trans_update(txq); | 3450 | case NETDEV_TX_OK: |
| 3452 | pkt_dev->last_ok = 1; | 3451 | txq_trans_update(txq); |
| 3453 | pkt_dev->sofar++; | 3452 | pkt_dev->last_ok = 1; |
| 3454 | pkt_dev->seq_num++; | 3453 | pkt_dev->sofar++; |
| 3455 | pkt_dev->tx_bytes += pkt_dev->cur_pkt_size; | 3454 | pkt_dev->seq_num++; |
| 3456 | break; | 3455 | pkt_dev->tx_bytes += pkt_dev->cur_pkt_size; |
| 3457 | case NETDEV_TX_LOCKED: | 3456 | break; |
| 3458 | cpu_relax(); | 3457 | default: /* Drivers are not supposed to return other values! */ |
| 3459 | goto retry_now; | 3458 | if (net_ratelimit()) |
| 3460 | default: /* Drivers are not supposed to return other values! */ | 3459 | pr_info("pktgen: %s xmit error: %d\n", |
| 3461 | if (net_ratelimit()) | 3460 | odev->name, ret); |
| 3462 | pr_info("pktgen: %s xmit error: %d\n", | 3461 | pkt_dev->errors++; |
| 3463 | odev->name, ret); | 3462 | /* fallthru */ |
| 3464 | pkt_dev->errors++; | 3463 | case NETDEV_TX_LOCKED: |
| 3465 | /* fallthru */ | 3464 | case NETDEV_TX_BUSY: |
| 3466 | case NETDEV_TX_BUSY: | 3465 | /* Retry it next time */ |
| 3467 | /* Retry it next time */ | 3466 | atomic_dec(&(pkt_dev->skb->users)); |
| 3468 | atomic_dec(&(pkt_dev->skb->users)); | 3467 | pkt_dev->last_ok = 0; |
| 3469 | pkt_dev->last_ok = 0; | ||
| 3470 | } | ||
| 3471 | |||
| 3472 | if (pkt_dev->delay) | ||
| 3473 | pkt_dev->next_tx = ktime_add_ns(ktime_now(), | ||
| 3474 | pkt_dev->delay); | ||
| 3475 | } | 3468 | } |
| 3476 | __netif_tx_unlock_bh(txq); | 3469 | __netif_tx_unlock_bh(txq); |
| 3477 | 3470 | ||
| 3478 | /* If pkt_dev->count is zero, then run forever */ | 3471 | /* If pkt_dev->count is zero, then run forever */ |
| 3479 | if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { | 3472 | if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { |
| 3480 | while (atomic_read(&(pkt_dev->skb->users)) != 1) { | 3473 | pktgen_wait_for_skb(pkt_dev); |
| 3481 | if (signal_pending(current)) | ||
| 3482 | break; | ||
| 3483 | idle(pkt_dev); | ||
| 3484 | } | ||
| 3485 | 3474 | ||
| 3486 | /* Done with this */ | 3475 | /* Done with this */ |
| 3487 | pktgen_stop_device(pkt_dev); | 3476 | pktgen_stop_device(pkt_dev); |
| @@ -3514,20 +3503,24 @@ static int pktgen_thread_worker(void *arg) | |||
| 3514 | while (!kthread_should_stop()) { | 3503 | while (!kthread_should_stop()) { |
| 3515 | pkt_dev = next_to_run(t); | 3504 | pkt_dev = next_to_run(t); |
| 3516 | 3505 | ||
| 3517 | if (!pkt_dev && | 3506 | if (unlikely(!pkt_dev && t->control == 0)) { |
| 3518 | (t->control & (T_STOP | T_RUN | T_REMDEVALL | T_REMDEV)) | 3507 | wait_event_interruptible_timeout(t->queue, |
| 3519 | == 0) { | 3508 | t->control != 0, |
| 3520 | prepare_to_wait(&(t->queue), &wait, | 3509 | HZ/10); |
| 3521 | TASK_INTERRUPTIBLE); | 3510 | continue; |
| 3522 | schedule_timeout(HZ / 10); | ||
| 3523 | finish_wait(&(t->queue), &wait); | ||
| 3524 | } | 3511 | } |
| 3525 | 3512 | ||
| 3526 | __set_current_state(TASK_RUNNING); | 3513 | __set_current_state(TASK_RUNNING); |
| 3527 | 3514 | ||
| 3528 | if (pkt_dev) | 3515 | if (likely(pkt_dev)) { |
| 3529 | pktgen_xmit(pkt_dev); | 3516 | pktgen_xmit(pkt_dev); |
| 3530 | 3517 | ||
| 3518 | if (need_resched()) | ||
| 3519 | pktgen_resched(pkt_dev); | ||
| 3520 | else | ||
| 3521 | cpu_relax(); | ||
| 3522 | } | ||
| 3523 | |||
| 3531 | if (t->control & T_STOP) { | 3524 | if (t->control & T_STOP) { |
| 3532 | pktgen_stop(t); | 3525 | pktgen_stop(t); |
| 3533 | t->control &= ~(T_STOP); | 3526 | t->control &= ~(T_STOP); |
