Merge branch 'akpm' (patches from Andrew Morton)
[cascardo/linux.git] / net / sched / sch_fq_codel.c
index 063b726..b9ca32e 100644 (file)
@@ -52,7 +52,7 @@ struct fq_codel_flow {
 }; /* please try to keep this structure <= 64 bytes */
 
 struct fq_codel_sched_data {
-       struct tcf_proto *filter_list;  /* optional external classifier */
+       struct tcf_proto __rcu *filter_list; /* optional external classifier */
        struct fq_codel_flow *flows;    /* Flows table [flows_cnt] */
        u32             *backlogs;      /* backlog table [flows_cnt] */
        u32             flows_cnt;      /* number of flows */
@@ -77,13 +77,15 @@ static unsigned int fq_codel_hash(const struct fq_codel_sched_data *q,
        hash = jhash_3words((__force u32)keys.dst,
                            (__force u32)keys.src ^ keys.ip_proto,
                            (__force u32)keys.ports, q->perturbation);
-       return ((u64)hash * q->flows_cnt) >> 32;
+
+       return reciprocal_scale(hash, q->flows_cnt);
 }
 
 static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
                                      int *qerr)
 {
        struct fq_codel_sched_data *q = qdisc_priv(sch);
+       struct tcf_proto *filter;
        struct tcf_result res;
        int result;
 
@@ -92,11 +94,12 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
            TC_H_MIN(skb->priority) <= q->flows_cnt)
                return TC_H_MIN(skb->priority);
 
-       if (!q->filter_list)
+       filter = rcu_dereference(q->filter_list);
+       if (!filter)
                return fq_codel_hash(q, skb) + 1;
 
        *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
-       result = tc_classify(skb, q->filter_list, &res);
+       result = tc_classify(skb, filter, &res);
        if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
                switch (result) {
@@ -161,8 +164,8 @@ static unsigned int fq_codel_drop(struct Qdisc *sch)
        q->backlogs[idx] -= len;
        kfree_skb(skb);
        sch->q.qlen--;
-       sch->qstats.drops++;
-       sch->qstats.backlog -= len;
+       qdisc_qstats_drop(sch);
+       qdisc_qstats_backlog_dec(sch, skb);
        flow->dropped++;
        return idx;
 }
@@ -177,7 +180,7 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
        idx = fq_codel_classify(skb, sch, &ret);
        if (idx == 0) {
                if (ret & __NET_XMIT_BYPASS)
-                       sch->qstats.drops++;
+                       qdisc_qstats_drop(sch);
                kfree_skb(skb);
                return ret;
        }
@@ -187,7 +190,7 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
        flow = &q->flows[idx];
        flow_queue_add(flow, skb);
        q->backlogs[idx] += qdisc_pkt_len(skb);
-       sch->qstats.backlog += qdisc_pkt_len(skb);
+       qdisc_qstats_backlog_inc(sch, skb);
 
        if (list_empty(&flow->flowchain)) {
                list_add_tail(&flow->flowchain, &q->new_flows);
@@ -495,7 +498,8 @@ static void fq_codel_put(struct Qdisc *q, unsigned long cl)
 {
 }
 
-static struct tcf_proto **fq_codel_find_tcf(struct Qdisc *sch, unsigned long cl)
+static struct tcf_proto __rcu **fq_codel_find_tcf(struct Qdisc *sch,
+                                                 unsigned long cl)
 {
        struct fq_codel_sched_data *q = qdisc_priv(sch);
 
@@ -546,7 +550,7 @@ static int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl,
                qs.backlog = q->backlogs[idx];
                qs.drops = flow->dropped;
        }
-       if (gnet_stats_copy_queue(d, &qs) < 0)
+       if (gnet_stats_copy_queue(d, NULL, &qs, 0) < 0)
                return -1;
        if (idx < q->flows_cnt)
                return gnet_stats_copy_app(d, &xstats, sizeof(xstats));