Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net_sched: accurate bytes/packets stats/rates

In commit 44b8288308ac9d (net_sched: pfifo_head_drop problem), we fixed
a problem with pfifo_head drops that incorrectly decreased
sch->bstats.bytes and sch->bstats.packets

Several qdiscs (CHOKe, SFQ, pfifo_head, ...) are able to drop a
previously enqueued packet, and bstats cannot be changed, so
bstats/rates are not accurate (over estimated)

This patch changes the qdisc_bstats updates to be done at dequeue() time
instead of enqueue() time. bstats counters no longer account for dropped
frames, and rates are more correct, since enqueue() bursts dont have
effect on dequeue() rate.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Acked-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
9190b3b3 b3053251

+29 -33
+5 -3
include/net/sch_generic.h
··· 445 445 { 446 446 __skb_queue_tail(list, skb); 447 447 sch->qstats.backlog += qdisc_pkt_len(skb); 448 - qdisc_bstats_update(sch, skb); 449 448 450 449 return NET_XMIT_SUCCESS; 451 450 } ··· 459 460 { 460 461 struct sk_buff *skb = __skb_dequeue(list); 461 462 462 - if (likely(skb != NULL)) 463 + if (likely(skb != NULL)) { 463 464 sch->qstats.backlog -= qdisc_pkt_len(skb); 465 + qdisc_bstats_update(sch, skb); 466 + } 464 467 465 468 return skb; 466 469 } ··· 475 474 static inline unsigned int __qdisc_queue_drop_head(struct Qdisc *sch, 476 475 struct sk_buff_head *list) 477 476 { 478 - struct sk_buff *skb = __qdisc_dequeue_head(sch, list); 477 + struct sk_buff *skb = __skb_dequeue(list); 479 478 480 479 if (likely(skb != NULL)) { 481 480 unsigned int len = qdisc_pkt_len(skb); 481 + sch->qstats.backlog -= len; 482 482 kfree_skb(skb); 483 483 return len; 484 484 }
+1 -2
net/sched/sch_cbq.c
··· 390 390 ret = qdisc_enqueue(skb, cl->q); 391 391 if (ret == NET_XMIT_SUCCESS) { 392 392 sch->q.qlen++; 393 - qdisc_bstats_update(sch, skb); 394 393 cbq_mark_toplevel(q, cl); 395 394 if (!cl->next_alive) 396 395 cbq_activate_class(cl); ··· 648 649 ret = qdisc_enqueue(skb, cl->q); 649 650 if (ret == NET_XMIT_SUCCESS) { 650 651 sch->q.qlen++; 651 - qdisc_bstats_update(sch, skb); 652 652 if (!cl->next_alive) 653 653 cbq_activate_class(cl); 654 654 return 0; ··· 969 971 970 972 skb = cbq_dequeue_1(sch); 971 973 if (skb) { 974 + qdisc_bstats_update(sch, skb); 972 975 sch->q.qlen--; 973 976 sch->flags &= ~TCQ_F_THROTTLED; 974 977 return skb;
+1 -1
net/sched/sch_drr.c
··· 376 376 } 377 377 378 378 bstats_update(&cl->bstats, skb); 379 - qdisc_bstats_update(sch, skb); 380 379 381 380 sch->q.qlen++; 382 381 return err; ··· 402 403 skb = qdisc_dequeue_peeked(cl->qdisc); 403 404 if (cl->qdisc->q.qlen == 0) 404 405 list_del(&cl->alist); 406 + qdisc_bstats_update(sch, skb); 405 407 sch->q.qlen--; 406 408 return skb; 407 409 }
+1 -1
net/sched/sch_dsmark.c
··· 260 260 return err; 261 261 } 262 262 263 - qdisc_bstats_update(sch, skb); 264 263 sch->q.qlen++; 265 264 266 265 return NET_XMIT_SUCCESS; ··· 282 283 if (skb == NULL) 283 284 return NULL; 284 285 286 + qdisc_bstats_update(sch, skb); 285 287 sch->q.qlen--; 286 288 287 289 index = skb->tc_index & (p->indices - 1);
+1 -4
net/sched/sch_fifo.c
··· 46 46 47 47 static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc* sch) 48 48 { 49 - struct sk_buff *skb_head; 50 49 struct fifo_sched_data *q = qdisc_priv(sch); 51 50 52 51 if (likely(skb_queue_len(&sch->q) < q->limit)) 53 52 return qdisc_enqueue_tail(skb, sch); 54 53 55 54 /* queue full, remove one skb to fulfill the limit */ 56 - skb_head = qdisc_dequeue_head(sch); 55 + __qdisc_queue_drop_head(sch, &sch->q); 57 56 sch->qstats.drops++; 58 - kfree_skb(skb_head); 59 - 60 57 qdisc_enqueue_tail(skb, sch); 61 58 62 59 return NET_XMIT_CN;
+1 -1
net/sched/sch_hfsc.c
··· 1600 1600 set_active(cl, qdisc_pkt_len(skb)); 1601 1601 1602 1602 bstats_update(&cl->bstats, skb); 1603 - qdisc_bstats_update(sch, skb); 1604 1603 sch->q.qlen++; 1605 1604 1606 1605 return NET_XMIT_SUCCESS; ··· 1665 1666 } 1666 1667 1667 1668 sch->flags &= ~TCQ_F_THROTTLED; 1669 + qdisc_bstats_update(sch, skb); 1668 1670 sch->q.qlen--; 1669 1671 1670 1672 return skb;
+5 -7
net/sched/sch_htb.c
··· 574 574 } 575 575 576 576 sch->q.qlen++; 577 - qdisc_bstats_update(sch, skb); 578 577 return NET_XMIT_SUCCESS; 579 578 } 580 579 ··· 841 842 842 843 static struct sk_buff *htb_dequeue(struct Qdisc *sch) 843 844 { 844 - struct sk_buff *skb = NULL; 845 + struct sk_buff *skb; 845 846 struct htb_sched *q = qdisc_priv(sch); 846 847 int level; 847 848 psched_time_t next_event; ··· 850 851 /* try to dequeue direct packets as high prio (!) to minimize cpu work */ 851 852 skb = __skb_dequeue(&q->direct_queue); 852 853 if (skb != NULL) { 854 + ok: 855 + qdisc_bstats_update(sch, skb); 853 856 sch->flags &= ~TCQ_F_THROTTLED; 854 857 sch->q.qlen--; 855 858 return skb; ··· 885 884 int prio = ffz(m); 886 885 m |= 1 << prio; 887 886 skb = htb_dequeue_tree(q, prio, level); 888 - if (likely(skb != NULL)) { 889 - sch->q.qlen--; 890 - sch->flags &= ~TCQ_F_THROTTLED; 891 - goto fin; 892 - } 887 + if (likely(skb != NULL)) 888 + goto ok; 893 889 } 894 890 } 895 891 sch->qstats.overlimits++;
+1 -1
net/sched/sch_multiq.c
··· 83 83 84 84 ret = qdisc_enqueue(skb, qdisc); 85 85 if (ret == NET_XMIT_SUCCESS) { 86 - qdisc_bstats_update(sch, skb); 87 86 sch->q.qlen++; 88 87 return NET_XMIT_SUCCESS; 89 88 } ··· 111 112 qdisc = q->queues[q->curband]; 112 113 skb = qdisc->dequeue(qdisc); 113 114 if (skb) { 115 + qdisc_bstats_update(sch, skb); 114 116 sch->q.qlen--; 115 117 return skb; 116 118 }
+1 -2
net/sched/sch_netem.c
··· 240 240 241 241 if (likely(ret == NET_XMIT_SUCCESS)) { 242 242 sch->q.qlen++; 243 - qdisc_bstats_update(sch, skb); 244 243 } else if (net_xmit_drop_count(ret)) { 245 244 sch->qstats.drops++; 246 245 } ··· 288 289 skb->tstamp.tv64 = 0; 289 290 #endif 290 291 pr_debug("netem_dequeue: return skb=%p\n", skb); 292 + qdisc_bstats_update(sch, skb); 291 293 sch->q.qlen--; 292 294 return skb; 293 295 } ··· 476 476 __skb_queue_after(list, skb, nskb); 477 477 478 478 sch->qstats.backlog += qdisc_pkt_len(nskb); 479 - qdisc_bstats_update(sch, nskb); 480 479 481 480 return NET_XMIT_SUCCESS; 482 481 }
+1 -1
net/sched/sch_prio.c
··· 84 84 85 85 ret = qdisc_enqueue(skb, qdisc); 86 86 if (ret == NET_XMIT_SUCCESS) { 87 - qdisc_bstats_update(sch, skb); 88 87 sch->q.qlen++; 89 88 return NET_XMIT_SUCCESS; 90 89 } ··· 115 116 struct Qdisc *qdisc = q->queues[prio]; 116 117 struct sk_buff *skb = qdisc->dequeue(qdisc); 117 118 if (skb) { 119 + qdisc_bstats_update(sch, skb); 118 120 sch->q.qlen--; 119 121 return skb; 120 122 }
+6 -5
net/sched/sch_red.c
··· 94 94 95 95 ret = qdisc_enqueue(skb, child); 96 96 if (likely(ret == NET_XMIT_SUCCESS)) { 97 - qdisc_bstats_update(sch, skb); 98 97 sch->q.qlen++; 99 98 } else if (net_xmit_drop_count(ret)) { 100 99 q->stats.pdrop++; ··· 113 114 struct Qdisc *child = q->qdisc; 114 115 115 116 skb = child->dequeue(child); 116 - if (skb) 117 + if (skb) { 118 + qdisc_bstats_update(sch, skb); 117 119 sch->q.qlen--; 118 - else if (!red_is_idling(&q->parms)) 119 - red_start_of_idle_period(&q->parms); 120 - 120 + } else { 121 + if (!red_is_idling(&q->parms)) 122 + red_start_of_idle_period(&q->parms); 123 + } 121 124 return skb; 122 125 } 123 126
+2 -3
net/sched/sch_sfq.c
··· 402 402 q->tail = slot; 403 403 slot->allot = q->scaled_quantum; 404 404 } 405 - if (++sch->q.qlen <= q->limit) { 406 - qdisc_bstats_update(sch, skb); 405 + if (++sch->q.qlen <= q->limit) 407 406 return NET_XMIT_SUCCESS; 408 - } 409 407 410 408 sfq_drop(sch); 411 409 return NET_XMIT_CN; ··· 443 445 } 444 446 skb = slot_dequeue_head(slot); 445 447 sfq_dec(q, a); 448 + qdisc_bstats_update(sch, skb); 446 449 sch->q.qlen--; 447 450 sch->qstats.backlog -= qdisc_pkt_len(skb); 448 451
+1 -1
net/sched/sch_tbf.c
··· 134 134 } 135 135 136 136 sch->q.qlen++; 137 - qdisc_bstats_update(sch, skb); 138 137 return NET_XMIT_SUCCESS; 139 138 } 140 139 ··· 186 187 q->ptokens = ptoks; 187 188 sch->q.qlen--; 188 189 sch->flags &= ~TCQ_F_THROTTLED; 190 + qdisc_bstats_update(sch, skb); 189 191 return skb; 190 192 } 191 193
+2 -1
net/sched/sch_teql.c
··· 87 87 88 88 if (q->q.qlen < dev->tx_queue_len) { 89 89 __skb_queue_tail(&q->q, skb); 90 - qdisc_bstats_update(sch, skb); 91 90 return NET_XMIT_SUCCESS; 92 91 } 93 92 ··· 110 111 dat->m->slaves = sch; 111 112 netif_wake_queue(m); 112 113 } 114 + } else { 115 + qdisc_bstats_update(sch, skb); 113 116 } 114 117 sch->q.qlen = dat->q.qlen + dat_queue->qdisc->q.qlen; 115 118 return skb;