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>
This commit is contained in:

committed by
David S. Miller

parent
fd245a4adb
commit
a2da570d62
@@ -398,6 +398,11 @@ static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt)
|
||||
return stab;
|
||||
}
|
||||
|
||||
static void stab_kfree_rcu(struct rcu_head *head)
|
||||
{
|
||||
kfree(container_of(head, struct qdisc_size_table, rcu));
|
||||
}
|
||||
|
||||
void qdisc_put_stab(struct qdisc_size_table *tab)
|
||||
{
|
||||
if (!tab)
|
||||
@@ -407,7 +412,7 @@ void qdisc_put_stab(struct qdisc_size_table *tab)
|
||||
|
||||
if (--tab->refcnt == 0) {
|
||||
list_del(&tab->list);
|
||||
kfree(tab);
|
||||
call_rcu_bh(&tab->rcu, stab_kfree_rcu);
|
||||
}
|
||||
|
||||
spin_unlock(&qdisc_stab_lock);
|
||||
@@ -430,7 +435,7 @@ nla_put_failure:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void qdisc_calculate_pkt_len(struct sk_buff *skb, struct qdisc_size_table *stab)
|
||||
void __qdisc_calculate_pkt_len(struct sk_buff *skb, const struct qdisc_size_table *stab)
|
||||
{
|
||||
int pkt_len, slot;
|
||||
|
||||
@@ -456,7 +461,7 @@ out:
|
||||
pkt_len = 1;
|
||||
qdisc_skb_cb(skb)->pkt_len = pkt_len;
|
||||
}
|
||||
EXPORT_SYMBOL(qdisc_calculate_pkt_len);
|
||||
EXPORT_SYMBOL(__qdisc_calculate_pkt_len);
|
||||
|
||||
void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc)
|
||||
{
|
||||
@@ -835,7 +840,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
|
||||
err = PTR_ERR(stab);
|
||||
goto err_out4;
|
||||
}
|
||||
sch->stab = stab;
|
||||
rcu_assign_pointer(sch->stab, stab);
|
||||
}
|
||||
if (tca[TCA_RATE]) {
|
||||
spinlock_t *root_lock;
|
||||
@@ -875,7 +880,7 @@ err_out4:
|
||||
* Any broken qdiscs that would require a ops->reset() here?
|
||||
* The qdisc was never in action so it shouldn't be necessary.
|
||||
*/
|
||||
qdisc_put_stab(sch->stab);
|
||||
qdisc_put_stab(rtnl_dereference(sch->stab));
|
||||
if (ops->destroy)
|
||||
ops->destroy(sch);
|
||||
goto err_out3;
|
||||
@@ -883,7 +888,7 @@ err_out4:
|
||||
|
||||
static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
|
||||
{
|
||||
struct qdisc_size_table *stab = NULL;
|
||||
struct qdisc_size_table *ostab, *stab = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (tca[TCA_OPTIONS]) {
|
||||
@@ -900,8 +905,9 @@ static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
|
||||
return PTR_ERR(stab);
|
||||
}
|
||||
|
||||
qdisc_put_stab(sch->stab);
|
||||
sch->stab = stab;
|
||||
ostab = rtnl_dereference(sch->stab);
|
||||
rcu_assign_pointer(sch->stab, stab);
|
||||
qdisc_put_stab(ostab);
|
||||
|
||||
if (tca[TCA_RATE]) {
|
||||
/* NB: ignores errors from replace_estimator
|
||||
@@ -1180,6 +1186,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
|
||||
struct nlmsghdr *nlh;
|
||||
unsigned char *b = skb_tail_pointer(skb);
|
||||
struct gnet_dump d;
|
||||
struct qdisc_size_table *stab;
|
||||
|
||||
nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);
|
||||
tcm = NLMSG_DATA(nlh);
|
||||
@@ -1195,7 +1202,8 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
|
||||
goto nla_put_failure;
|
||||
q->qstats.qlen = q->q.qlen;
|
||||
|
||||
if (q->stab && qdisc_dump_stab(skb, q->stab) < 0)
|
||||
stab = rtnl_dereference(q->stab);
|
||||
if (stab && qdisc_dump_stab(skb, stab) < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS,
|
||||
|
Reference in New Issue
Block a user