Merge remote-tracking branch 'net-next/master' into mac80211-next
[cascardo/linux.git] / net / sched / cls_tcindex.c
index 944c8ff..96144bd 100644 (file)
@@ -50,14 +50,13 @@ struct tcindex_data {
        struct rcu_head rcu;
 };
 
-static inline int
-tcindex_filter_is_set(struct tcindex_filter_result *r)
+static inline int tcindex_filter_is_set(struct tcindex_filter_result *r)
 {
        return tcf_exts_is_predicative(&r->exts) || r->res.classid;
 }
 
-static struct tcindex_filter_result *
-tcindex_lookup(struct tcindex_data *p, u16 key)
+static struct tcindex_filter_result *tcindex_lookup(struct tcindex_data *p,
+                                                   u16 key)
 {
        if (p->perfect) {
                struct tcindex_filter_result *f = p->perfect + key;
@@ -144,7 +143,8 @@ static void tcindex_destroy_rexts(struct rcu_head *head)
 
 static void tcindex_destroy_fexts(struct rcu_head *head)
 {
-       struct tcindex_filter *f = container_of(head, struct tcindex_filter, rcu);
+       struct tcindex_filter *f = container_of(head, struct tcindex_filter,
+                                               rcu);
 
        tcf_exts_destroy(&f->result.exts);
        kfree(f);
@@ -219,10 +219,10 @@ static const struct nla_policy tcindex_policy[TCA_TCINDEX_MAX + 1] = {
        [TCA_TCINDEX_CLASSID]           = { .type = NLA_U32 },
 };
 
-static void tcindex_filter_result_init(struct tcindex_filter_result *r)
+static int tcindex_filter_result_init(struct tcindex_filter_result *r)
 {
        memset(r, 0, sizeof(*r));
-       tcf_exts_init(&r->exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
+       return tcf_exts_init(&r->exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
 }
 
 static void __tcindex_partial_destroy(struct rcu_head *head)
@@ -233,23 +233,57 @@ static void __tcindex_partial_destroy(struct rcu_head *head)
        kfree(p);
 }
 
+static void tcindex_free_perfect_hash(struct tcindex_data *cp)
+{
+       int i;
+
+       for (i = 0; i < cp->hash; i++)
+               tcf_exts_destroy(&cp->perfect[i].exts);
+       kfree(cp->perfect);
+}
+
+static int tcindex_alloc_perfect_hash(struct tcindex_data *cp)
+{
+       int i, err = 0;
+
+       cp->perfect = kcalloc(cp->hash, sizeof(struct tcindex_filter_result),
+                             GFP_KERNEL);
+       if (!cp->perfect)
+               return -ENOMEM;
+
+       for (i = 0; i < cp->hash; i++) {
+               err = tcf_exts_init(&cp->perfect[i].exts,
+                                   TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
+               if (err < 0)
+                       goto errout;
+       }
+
+       return 0;
+
+errout:
+       tcindex_free_perfect_hash(cp);
+       return err;
+}
+
 static int
 tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
                  u32 handle, struct tcindex_data *p,
                  struct tcindex_filter_result *r, struct nlattr **tb,
                  struct nlattr *est, bool ovr)
 {
-       int err, balloc = 0;
        struct tcindex_filter_result new_filter_result, *old_r = r;
        struct tcindex_filter_result cr;
-       struct tcindex_data *cp, *oldp;
+       struct tcindex_data *cp = NULL, *oldp;
        struct tcindex_filter *f = NULL; /* make gcc behave */
+       int err, balloc = 0;
        struct tcf_exts e;
 
-       tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
+       err = tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
        if (err < 0)
                return err;
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
+       if (err < 0)
+               goto errout;
 
        err = -ENOMEM;
        /* tcindex_data attributes must look atomic to classifier/lookup so
@@ -270,19 +304,20 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        if (p->perfect) {
                int i;
 
-               cp->perfect = kmemdup(p->perfect,
-                                     sizeof(*r) * cp->hash, GFP_KERNEL);
-               if (!cp->perfect)
+               if (tcindex_alloc_perfect_hash(cp) < 0)
                        goto errout;
                for (i = 0; i < cp->hash; i++)
-                       tcf_exts_init(&cp->perfect[i].exts,
-                                     TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
+                       cp->perfect[i].res = p->perfect[i].res;
                balloc = 1;
        }
        cp->h = p->h;
 
-       tcindex_filter_result_init(&new_filter_result);
-       tcindex_filter_result_init(&cr);
+       err = tcindex_filter_result_init(&new_filter_result);
+       if (err < 0)
+               goto errout1;
+       err = tcindex_filter_result_init(&cr);
+       if (err < 0)
+               goto errout1;
        if (old_r)
                cr.res = r->res;
 
@@ -338,15 +373,8 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        err = -ENOMEM;
        if (!cp->perfect && !cp->h) {
                if (valid_perfect_hash(cp)) {
-                       int i;
-
-                       cp->perfect = kcalloc(cp->hash, sizeof(*r), GFP_KERNEL);
-                       if (!cp->perfect)
+                       if (tcindex_alloc_perfect_hash(cp) < 0)
                                goto errout_alloc;
-                       for (i = 0; i < cp->hash; i++)
-                               tcf_exts_init(&cp->perfect[i].exts,
-                                             TCA_TCINDEX_ACT,
-                                             TCA_TCINDEX_POLICE);
                        balloc = 1;
                } else {
                        struct tcindex_filter __rcu **hash;
@@ -373,8 +401,12 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
                if (!f)
                        goto errout_alloc;
                f->key = handle;
-               tcindex_filter_result_init(&f->result);
                f->next = NULL;
+               err = tcindex_filter_result_init(&f->result);
+               if (err < 0) {
+                       kfree(f);
+                       goto errout_alloc;
+               }
        }
 
        if (tb[TCA_TCINDEX_CLASSID]) {
@@ -387,8 +419,13 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        else
                tcf_exts_change(tp, &cr.exts, &e);
 
-       if (old_r && old_r != r)
-               tcindex_filter_result_init(old_r);
+       if (old_r && old_r != r) {
+               err = tcindex_filter_result_init(old_r);
+               if (err < 0) {
+                       kfree(f);
+                       goto errout_alloc;
+               }
+       }
 
        oldp = p;
        r->res = cr.res;
@@ -415,9 +452,12 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
 
 errout_alloc:
        if (balloc == 1)
-               kfree(cp->perfect);
+               tcindex_free_perfect_hash(cp);
        else if (balloc == 2)
                kfree(cp->h);
+errout1:
+       tcf_exts_destroy(&cr.exts);
+       tcf_exts_destroy(&new_filter_result.exts);
 errout:
        kfree(cp);
        tcf_exts_destroy(&e);
@@ -510,7 +550,7 @@ static bool tcindex_destroy(struct tcf_proto *tp, bool force)
 
 
 static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
-    struct sk_buff *skb, struct tcmsg *t)
+                       struct sk_buff *skb, struct tcmsg *t)
 {
        struct tcindex_data *p = rtnl_dereference(tp->root);
        struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh;