net: sched: make bstats per cpu and estimator RCU safe
[cascardo/linux.git] / net / sched / sch_atm.c
index 8449b33..0101766 100644 (file)
@@ -41,7 +41,7 @@
 
 struct atm_flow_data {
        struct Qdisc            *q;     /* FIFO, TBF, etc. */
-       struct tcf_proto        *filter_list;
+       struct tcf_proto __rcu  *filter_list;
        struct atm_vcc          *vcc;   /* VCC; NULL if VCC is closed */
        void                    (*old_pop)(struct atm_vcc *vcc,
                                           struct sk_buff *skb); /* chaining */
@@ -273,7 +273,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
                error = -ENOBUFS;
                goto err_out;
        }
-       flow->filter_list = NULL;
+       RCU_INIT_POINTER(flow->filter_list, NULL);
        flow->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid);
        if (!flow->q)
                flow->q = &noop_qdisc;
@@ -311,7 +311,7 @@ static int atm_tc_delete(struct Qdisc *sch, unsigned long arg)
        pr_debug("atm_tc_delete(sch %p,[qdisc %p],flow %p)\n", sch, p, flow);
        if (list_empty(&flow->list))
                return -EINVAL;
-       if (flow->filter_list || flow == &p->link)
+       if (rcu_access_pointer(flow->filter_list) || flow == &p->link)
                return -EBUSY;
        /*
         * Reference count must be 2: one for "keepalive" (set at class
@@ -345,7 +345,8 @@ static void atm_tc_walk(struct Qdisc *sch, struct qdisc_walker *walker)
        }
 }
 
-static struct tcf_proto **atm_tc_find_tcf(struct Qdisc *sch, unsigned long cl)
+static struct tcf_proto __rcu **atm_tc_find_tcf(struct Qdisc *sch,
+                                               unsigned long cl)
 {
        struct atm_qdisc_data *p = qdisc_priv(sch);
        struct atm_flow_data *flow = (struct atm_flow_data *)cl;
@@ -369,11 +370,12 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
        flow = NULL;
        if (TC_H_MAJ(skb->priority) != sch->handle ||
            !(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority))) {
+               struct tcf_proto *fl;
+
                list_for_each_entry(flow, &p->flows, list) {
-                       if (flow->filter_list) {
-                               result = tc_classify_compat(skb,
-                                                           flow->filter_list,
-                                                           &res);
+                       fl = rcu_dereference_bh(flow->filter_list);
+                       if (fl) {
+                               result = tc_classify_compat(skb, fl, &res);
                                if (result < 0)
                                        continue;
                                flow = (struct atm_flow_data *)res.class;
@@ -544,7 +546,7 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt)
        if (!p->link.q)
                p->link.q = &noop_qdisc;
        pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q);
-       p->link.filter_list = NULL;
+       RCU_INIT_POINTER(p->link.filter_list, NULL);
        p->link.vcc = NULL;
        p->link.sock = NULL;
        p->link.classid = sch->handle;
@@ -637,7 +639,7 @@ atm_tc_dump_class_stats(struct Qdisc *sch, unsigned long arg,
 
        flow->qstats.qlen = flow->q->q.qlen;
 
-       if (gnet_stats_copy_basic(d, &flow->bstats) < 0 ||
+       if (gnet_stats_copy_basic(d, NULL, &flow->bstats) < 0 ||
            gnet_stats_copy_queue(d, &flow->qstats) < 0)
                return -1;