diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-01-19 22:48:19 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-01-20 19:59:32 -0500 |
commit | a2da570d62fcb9e8816f6920e1ec02c706b289fa (patch) | |
tree | 9a24b76262c40ec2f1be11e29e224c7d7b20fa26 /net/sched | |
parent | fd245a4adb5288eac37250875f237c40a20a1944 (diff) |
net_sched: RCU conversion of stab
This patch converts stab qdisc management to RCU, so that we can perform
the qdisc_calculate_pkt_len() call before getting qdisc lock.
This shortens the lock's held time in __dev_xmit_skb().
This permits more qdiscs to get TCQ_F_CAN_BYPASS status, avoiding lot of
cache misses and so reducing latencies.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Patrick McHardy <kaber@trash.net>
CC: Jesper Dangaard Brouer <hawk@diku.dk>
CC: Jarek Poplawski <jarkao2@gmail.com>
CC: Jamal Hadi Salim <hadi@cyberus.ca>
CC: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/sch_api.c | 26 | ||||
-rw-r--r-- | net/sched/sch_generic.c | 2 |
2 files changed, 18 insertions, 10 deletions
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 374fcbef80e8..150741579408 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c | |||
@@ -398,6 +398,11 @@ static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt) | |||
398 | return stab; | 398 | return stab; |
399 | } | 399 | } |
400 | 400 | ||
401 | static void stab_kfree_rcu(struct rcu_head *head) | ||
402 | { | ||
403 | kfree(container_of(head, struct qdisc_size_table, rcu)); | ||
404 | } | ||
405 | |||
401 | void qdisc_put_stab(struct qdisc_size_table *tab) | 406 | void qdisc_put_stab(struct qdisc_size_table *tab) |
402 | { | 407 | { |
403 | if (!tab) | 408 | if (!tab) |
@@ -407,7 +412,7 @@ void qdisc_put_stab(struct qdisc_size_table *tab) | |||
407 | 412 | ||
408 | if (--tab->refcnt == 0) { | 413 | if (--tab->refcnt == 0) { |
409 | list_del(&tab->list); | 414 | list_del(&tab->list); |
410 | kfree(tab); | 415 | call_rcu_bh(&tab->rcu, stab_kfree_rcu); |
411 | } | 416 | } |
412 | 417 | ||
413 | spin_unlock(&qdisc_stab_lock); | 418 | spin_unlock(&qdisc_stab_lock); |
@@ -430,7 +435,7 @@ nla_put_failure: | |||
430 | return -1; | 435 | return -1; |
431 | } | 436 | } |
432 | 437 | ||
433 | void qdisc_calculate_pkt_len(struct sk_buff *skb, struct qdisc_size_table *stab) | 438 | void __qdisc_calculate_pkt_len(struct sk_buff *skb, const struct qdisc_size_table *stab) |
434 | { | 439 | { |
435 | int pkt_len, slot; | 440 | int pkt_len, slot; |
436 | 441 | ||
@@ -456,7 +461,7 @@ out: | |||
456 | pkt_len = 1; | 461 | pkt_len = 1; |
457 | qdisc_skb_cb(skb)->pkt_len = pkt_len; | 462 | qdisc_skb_cb(skb)->pkt_len = pkt_len; |
458 | } | 463 | } |
459 | EXPORT_SYMBOL(qdisc_calculate_pkt_len); | 464 | EXPORT_SYMBOL(__qdisc_calculate_pkt_len); |
460 | 465 | ||
461 | void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc) | 466 | void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc) |
462 | { | 467 | { |
@@ -835,7 +840,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, | |||
835 | err = PTR_ERR(stab); | 840 | err = PTR_ERR(stab); |
836 | goto err_out4; | 841 | goto err_out4; |
837 | } | 842 | } |
838 | sch->stab = stab; | 843 | rcu_assign_pointer(sch->stab, stab); |
839 | } | 844 | } |
840 | if (tca[TCA_RATE]) { | 845 | if (tca[TCA_RATE]) { |
841 | spinlock_t *root_lock; | 846 | spinlock_t *root_lock; |
@@ -875,7 +880,7 @@ err_out4: | |||
875 | * Any broken qdiscs that would require a ops->reset() here? | 880 | * Any broken qdiscs that would require a ops->reset() here? |
876 | * The qdisc was never in action so it shouldn't be necessary. | 881 | * The qdisc was never in action so it shouldn't be necessary. |
877 | */ | 882 | */ |
878 | qdisc_put_stab(sch->stab); | 883 | qdisc_put_stab(rtnl_dereference(sch->stab)); |
879 | if (ops->destroy) | 884 | if (ops->destroy) |
880 | ops->destroy(sch); | 885 | ops->destroy(sch); |
881 | goto err_out3; | 886 | goto err_out3; |
@@ -883,7 +888,7 @@ err_out4: | |||
883 | 888 | ||
884 | static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) | 889 | static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) |
885 | { | 890 | { |
886 | struct qdisc_size_table *stab = NULL; | 891 | struct qdisc_size_table *ostab, *stab = NULL; |
887 | int err = 0; | 892 | int err = 0; |
888 | 893 | ||
889 | if (tca[TCA_OPTIONS]) { | 894 | if (tca[TCA_OPTIONS]) { |
@@ -900,8 +905,9 @@ static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) | |||
900 | return PTR_ERR(stab); | 905 | return PTR_ERR(stab); |
901 | } | 906 | } |
902 | 907 | ||
903 | qdisc_put_stab(sch->stab); | 908 | ostab = rtnl_dereference(sch->stab); |
904 | sch->stab = stab; | 909 | rcu_assign_pointer(sch->stab, stab); |
910 | qdisc_put_stab(ostab); | ||
905 | 911 | ||
906 | if (tca[TCA_RATE]) { | 912 | if (tca[TCA_RATE]) { |
907 | /* NB: ignores errors from replace_estimator | 913 | /* NB: ignores errors from replace_estimator |
@@ -1180,6 +1186,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, | |||
1180 | struct nlmsghdr *nlh; | 1186 | struct nlmsghdr *nlh; |
1181 | unsigned char *b = skb_tail_pointer(skb); | 1187 | unsigned char *b = skb_tail_pointer(skb); |
1182 | struct gnet_dump d; | 1188 | struct gnet_dump d; |
1189 | struct qdisc_size_table *stab; | ||
1183 | 1190 | ||
1184 | nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags); | 1191 | nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags); |
1185 | tcm = NLMSG_DATA(nlh); | 1192 | tcm = NLMSG_DATA(nlh); |
@@ -1195,7 +1202,8 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, | |||
1195 | goto nla_put_failure; | 1202 | goto nla_put_failure; |
1196 | q->qstats.qlen = q->q.qlen; | 1203 | q->qstats.qlen = q->q.qlen; |
1197 | 1204 | ||
1198 | if (q->stab && qdisc_dump_stab(skb, q->stab) < 0) | 1205 | stab = rtnl_dereference(q->stab); |
1206 | if (stab && qdisc_dump_stab(skb, stab) < 0) | ||
1199 | goto nla_put_failure; | 1207 | goto nla_put_failure; |
1200 | 1208 | ||
1201 | if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, | 1209 | if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, |
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 2f1cb62130da..cc17e794c41e 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c | |||
@@ -632,7 +632,7 @@ void qdisc_destroy(struct Qdisc *qdisc) | |||
632 | #ifdef CONFIG_NET_SCHED | 632 | #ifdef CONFIG_NET_SCHED |
633 | qdisc_list_del(qdisc); | 633 | qdisc_list_del(qdisc); |
634 | 634 | ||
635 | qdisc_put_stab(qdisc->stab); | 635 | qdisc_put_stab(rtnl_dereference(qdisc->stab)); |
636 | #endif | 636 | #endif |
637 | gen_kill_estimator(&qdisc->bstats, &qdisc->rate_est); | 637 | gen_kill_estimator(&qdisc->bstats, &qdisc->rate_est); |
638 | if (ops->reset) | 638 | if (ops->reset) |