etf: Use cached rb_root
authorJesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com>
Thu, 15 Nov 2018 01:26:33 +0000 (17:26 -0800)
committerDavid S. Miller <davem@davemloft.net>
Sat, 17 Nov 2018 04:39:34 +0000 (20:39 -0800)
ETF's peek() operation is heavily used so use an rb_root_cached instead
and leverage rb_first_cached() which will run in O(1) instead of
O(log n).

Even if on 'timesortedlist_clear()' we could be using rb_erase(), we
choose to use rb_erase_cached(), because if in the future we allow
runtime changes to ETF parameters, and need to do a '_clear()', this
might cause some hard to debug issues.

Signed-off-by: Jesus Sanchez-Palencia <jesus.s.palencia@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/sch_etf.c

index fa85b24ac794d51b3f7e5134c266236ff98dac0b..52452b546564c971ffbf4b63078063af809a8cd6 100644 (file)
@@ -30,7 +30,7 @@ struct etf_sched_data {
        int queue;
        s32 delta; /* in ns */
        ktime_t last; /* The txtime of the last skb sent to the netdevice. */
-       struct rb_root head;
+       struct rb_root_cached head;
        struct qdisc_watchdog watchdog;
        ktime_t (*get_time)(void);
 };
@@ -104,7 +104,7 @@ static struct sk_buff *etf_peek_timesortedlist(struct Qdisc *sch)
        struct etf_sched_data *q = qdisc_priv(sch);
        struct rb_node *p;
 
-       p = rb_first(&q->head);
+       p = rb_first_cached(&q->head);
        if (!p)
                return NULL;
 
@@ -156,8 +156,9 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
                                      struct sk_buff **to_free)
 {
        struct etf_sched_data *q = qdisc_priv(sch);
-       struct rb_node **p = &q->head.rb_node, *parent = NULL;
+       struct rb_node **p = &q->head.rb_root.rb_node, *parent = NULL;
        ktime_t txtime = nskb->tstamp;
+       bool leftmost = true;
 
        if (!is_packet_valid(sch, nskb)) {
                report_sock_error(nskb, EINVAL,
@@ -170,13 +171,15 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
 
                parent = *p;
                skb = rb_to_skb(parent);
-               if (ktime_after(txtime, skb->tstamp))
+               if (ktime_after(txtime, skb->tstamp)) {
                        p = &parent->rb_right;
-               else
+                       leftmost = false;
+               } else {
                        p = &parent->rb_left;
+               }
        }
        rb_link_node(&nskb->rbnode, parent, p);
-       rb_insert_color(&nskb->rbnode, &q->head);
+       rb_insert_color_cached(&nskb->rbnode, &q->head, leftmost);
 
        qdisc_qstats_backlog_inc(sch, nskb);
        sch->q.qlen++;
@@ -192,7 +195,7 @@ static void timesortedlist_erase(struct Qdisc *sch, struct sk_buff *skb,
 {
        struct etf_sched_data *q = qdisc_priv(sch);
 
-       rb_erase(&skb->rbnode, &q->head);
+       rb_erase_cached(&skb->rbnode, &q->head);
 
        /* The rbnode field in the skb re-uses these fields, now that
         * we are done with the rbnode, reset them.
@@ -388,14 +391,14 @@ static int etf_init(struct Qdisc *sch, struct nlattr *opt,
 static void timesortedlist_clear(struct Qdisc *sch)
 {
        struct etf_sched_data *q = qdisc_priv(sch);
-       struct rb_node *p = rb_first(&q->head);
+       struct rb_node *p = rb_first_cached(&q->head);
 
        while (p) {
                struct sk_buff *skb = rb_to_skb(p);
 
                p = rb_next(p);
 
-               rb_erase(&skb->rbnode, &q->head);
+               rb_erase_cached(&skb->rbnode, &q->head);
                rtnl_kfree_skbs(skb, skb);
                sch->q.qlen--;
        }