Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6-block.git] / net / sched / sch_choke.c
index ed30e436128bff7c556e0c334e4cb5ae8d088e5e..8abc2625c3a104a7c79a9bb62b1df599060eb10e 100644 (file)
@@ -57,7 +57,7 @@ struct choke_sched_data {
 
 /* Variables */
        struct red_vars  vars;
-       struct tcf_proto *filter_list;
+       struct tcf_proto __rcu *filter_list;
        struct {
                u32     prob_drop;      /* Early probability drops */
                u32     prob_mark;      /* Early probability marks */
@@ -133,10 +133,16 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx)
        --sch->q.qlen;
 }
 
+/* private part of skb->cb[] that a qdisc is allowed to use
+ * is limited to QDISC_CB_PRIV_LEN bytes.
+ * As a flow key might be too large, we store a part of it only.
+ */
+#define CHOKE_K_LEN min_t(u32, sizeof(struct flow_keys), QDISC_CB_PRIV_LEN - 3)
+
 struct choke_skb_cb {
        u16                     classid;
        u8                      keys_valid;
-       struct flow_keys        keys;
+       u8                      keys[QDISC_CB_PRIV_LEN - 3];
 };
 
 static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb)
@@ -163,22 +169,26 @@ static u16 choke_get_classid(const struct sk_buff *skb)
 static bool choke_match_flow(struct sk_buff *skb1,
                             struct sk_buff *skb2)
 {
+       struct flow_keys temp;
+
        if (skb1->protocol != skb2->protocol)
                return false;
 
        if (!choke_skb_cb(skb1)->keys_valid) {
                choke_skb_cb(skb1)->keys_valid = 1;
-               skb_flow_dissect(skb1, &choke_skb_cb(skb1)->keys);
+               skb_flow_dissect(skb1, &temp);
+               memcpy(&choke_skb_cb(skb1)->keys, &temp, CHOKE_K_LEN);
        }
 
        if (!choke_skb_cb(skb2)->keys_valid) {
                choke_skb_cb(skb2)->keys_valid = 1;
-               skb_flow_dissect(skb2, &choke_skb_cb(skb2)->keys);
+               skb_flow_dissect(skb2, &temp);
+               memcpy(&choke_skb_cb(skb2)->keys, &temp, CHOKE_K_LEN);
        }
 
        return !memcmp(&choke_skb_cb(skb1)->keys,
                       &choke_skb_cb(skb2)->keys,
-                      sizeof(struct flow_keys));
+                      CHOKE_K_LEN);
 }
 
 /*
@@ -193,9 +203,11 @@ static bool choke_classify(struct sk_buff *skb,
 {
        struct choke_sched_data *q = qdisc_priv(sch);
        struct tcf_result res;
+       struct tcf_proto *fl;
        int result;
 
-       result = tc_classify(skb, q->filter_list, &res);
+       fl = rcu_dereference_bh(q->filter_list);
+       result = tc_classify(skb, fl, &res);
        if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
                switch (result) {
@@ -249,7 +261,7 @@ static bool choke_match_random(const struct choke_sched_data *q,
                return false;
 
        oskb = choke_peek_random(q, pidx);
-       if (q->filter_list)
+       if (rcu_access_pointer(q->filter_list))
                return choke_get_classid(nskb) == choke_get_classid(oskb);
 
        return choke_match_flow(oskb, nskb);
@@ -257,11 +269,11 @@ static bool choke_match_random(const struct choke_sched_data *q,
 
 static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 {
+       int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
        struct choke_sched_data *q = qdisc_priv(sch);
        const struct red_parms *p = &q->parms;
-       int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 
-       if (q->filter_list) {
+       if (rcu_access_pointer(q->filter_list)) {
                /* If using external classifiers, get result and record it. */
                if (!choke_classify(skb, sch, &ret))
                        goto other_drop;        /* Packet was eaten by filter */
@@ -554,7 +566,8 @@ static unsigned long choke_bind(struct Qdisc *sch, unsigned long parent,
        return 0;
 }
 
-static struct tcf_proto **choke_find_tcf(struct Qdisc *sch, unsigned long cl)
+static struct tcf_proto __rcu **choke_find_tcf(struct Qdisc *sch,
+                                              unsigned long cl)
 {
        struct choke_sched_data *q = qdisc_priv(sch);