]> rtime.felk.cvut.cz Git - lisovros/linux_canprio.git/blobdiff - net/sched/cls_canprio.c
SFF rules can be stored either in bitmap or in array of struct can_filter + Some...
[lisovros/linux_canprio.git] / net / sched / cls_canprio.c
index b375d4741733fe39a7f82dc6f5ff7a75faa4053a..653d8a99dec886ad2ffa2536a7d9ee9a90a88f9e 100644 (file)
@@ -1,9 +1,26 @@
-/* cls_canprio.c  -- Canprio classifier.
- * Makes decisions accoring to Can IDs.
+/* 
+ * cls_canprio.c  -- Canprio classifier.
+ * Makes decisions accoring to controller area network (CAN) identifiers.
+ *
+ *             This program is free software; you can distribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; version 2 of
+ *             the License.
+ * 
+ * Idea:       Oliver Hartkopp <oliver.hartkopp@volkswagen.de> 
+ * Copyright:  (c) 2011 Czech Technical University in Prague
+ *             (c) 2011 Volkswagen Group Research
+ * Authors:    Michal Sojka <sojkam1@fel.cvut.cz>
+ *             Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *             Rostislav Lisovy <lisovy@kormus.cz>
+ * Founded by: Volkswagen Group Research
+ *
+ * Some function descriptions are heavily inspired by article "Linux Network
+ * Traffic Control -- Implementation Overview" by Werner Almesberger
+ * 
  *
  * Implementation notes;
  *   parameter of functions named "base" is pointer to some parent element
- *   
  */
 
 #include <linux/module.h>
 #include <net/netlink.h>
 #include <net/act_api.h>
 #include <net/pkt_cls.h>
-#include <linux/can.h>
 #include <linux/bitmap.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/can.h>
 
-//--------------------------------------
-//FIXME put in some *.h file shared with userspace "tc"?
-struct canprio_rule {
-       __u32 canid;
-       __u32 canid_mask;
-};
-
-struct canprio_rule *canprio_rules;
-int canprio_rules_count = 0;
-int canprio_inv_en;
-//--------------------------------------
+#define SFF_BITMAP     1
+#undef SFF_BITMAP
 
 /* Definition of Netlink messages */
 enum {
        TCA_CANPRIO_A_UNSPEC,
        TCA_CANPRIO_CLASSID,
-       TCA_CANPRIO_RULES,
+       TCA_CANPRIO_RULES,  /* Array of can_filter structs; We are able 
+                               to determine the length after receiving */
+       TCA_CANPRIO_INV_EN, /* enable inverse rules */
        __TCA_CANPRIO_MAX,
 };
 #define TCA_CANPRIO_MAX (__TCA_CANPRIO_MAX - 1)
@@ -44,58 +56,74 @@ enum {
 static const struct nla_policy canprio_policy[TCA_CANPRIO_MAX + 1] = {
        [TCA_CANPRIO_CLASSID]    = { .type = NLA_U32 },
        //FIXME Be aware of possible problems with 64bit kernel and 32bit userspace etc.
-       [TCA_CANPRIO_RULES]      = { .len = sizeof(canprio_rules) }, 
+       [TCA_CANPRIO_RULES]      = { /*.len = (sizeof(struct can_filter))*/ }, //FIXME
+       [TCA_CANPRIO_INV_EN]     = { .type = NLA_U32 },
+};
+
+struct canprio_rules {
+       struct can_filter *rules_raw;   /* Raw rules copied from netlink message; 
+                                       Used for sending information to userspace 
+                                       (when 'tc filter show' is invoked) AND
+                                       when matching EFF frames*/
+#ifdef SFF_BITMAP
+       DECLARE_BITMAP(match_sff, CAN_SFF_MASK + 1); /* For each SFF Can ID (11 bit) 
+                                       there is one record in this bitfield */
+#endif
+       int inv_match_en;               /* Inverted match flag */
+       int rules_count;
+       int eff_rules_count;
+       int sff_rules_count;
+
+       struct rcu_head rcu;
 };
 
 struct canprio_head {
-       u32                  hgenerator;
-       struct list_head     flist;
+       u32 hgenerator;
+       struct list_head flist;
 };
 
 struct canprio_filter {
-       u32                  handle;
-       // For each SFF Can ID (11 bit) there is one record in this bitfield
-       DECLARE_BITMAP(match, CAN_SFF_MASK + 1); 
-       struct tcf_result    res;
-       struct list_head     link;
+       u32 handle;
+       struct canprio_rules *rules;    /* All rules necessary for classification */
+       struct tcf_result res;          /* Class ID (flow id) the instance 
+                                       of a filter is bound to */
+       struct list_head link;
 };
 
 /*
  * ----------------------------------------------------------------------------
  */
 
-static void sff_match_add(struct canprio_filter *f, u32 canid, u32 mask)
+#ifdef SFF_BITMAP
+static void canprio_sff_match_add(struct canprio_rules *rls, u32 can_id, u32 can_mask)
 {
        int i;
-       canid = canid & mask;
 
-       if (mask >= CAN_SFF_MASK) {
-               set_bit(canid, f->match);
+       pr_debug("%s() invoked\n", __FUNCTION__);
+       can_mask &= CAN_SFF_MASK;
+       can_id &= can_mask;
+
+       if (can_mask == CAN_SFF_MASK) {
+               set_bit(can_id, rls->match_sff);
                return;
        }
-
+       /* Add record (set bit to 1) for each ID that conforms particular rule */
        for (i = 0; i <= CAN_SFF_MASK; i++) {
-               if ((i & mask) == canid)
-                       set_bit(i, f->match);
+               if ((i & can_mask) == can_id)
+                       set_bit(i, rls->match_sff);
        }
 }
-
-static void eff_match_add(struct canprio_filter *f, u32 canid, u32 mask)
-{
-
-
-}
+#endif
 
 /* 
  * Extracts Can ID ot ouf the sk_buff structure.
  */
-static u32 canprio_get_id(struct sk_buff *skb)
+static canid_t canprio_get_id(struct sk_buff *skb)
 {
        /* Can ID is inside of data field */
        struct can_frame *cf = (struct can_frame *)skb->data;
-       //canid_t can_id = cf->can_id;
 
-       return (u32)cf->can_id;
+       return cf->can_id;
 }
 
 /*
@@ -115,65 +143,80 @@ static int canprio_classify(struct sk_buff *skb, struct tcf_proto *tp,
 {
        struct canprio_head *head = (struct canprio_head *)tp->root;
        struct canprio_filter *f;
-       u32 canid;
+       struct canprio_rules *r;
+       u32 can_id;
+       int i;
 
-       printk(" canprio_classify() invoked\n");
+       pr_debug(" canprio_classify() invoked\n");
+       can_id = (u32)canprio_get_id(skb);
 
+       rcu_read_lock();
        list_for_each_entry(f, &head->flist, link) {
-               canid = canprio_get_id(skb);
-               printk("  canprio_classify() can ID of received frame = 0x%x\n", canid);
-
-               if (canid & CAN_EFF_FLAG) {
-
+               bool match = false;
+               r = rcu_dereference(f->rules);
+               
+
+               if (can_id & CAN_EFF_FLAG) {
+                       can_id &= CAN_EFF_MASK;
+                       
+                       for (i = 0; i < r->eff_rules_count; i++) {
+                               if ((r->rules_raw[i].can_id & r->rules_raw[i].can_mask & CAN_EFF_MASK) ==
+                                       (can_id & r->rules_raw[i].can_mask & CAN_EFF_MASK)) {
+
+                                       match = true;
+                                       break;
+                               }
+                       }
                } else {
-                       if(test_bit(canid, f->match)) {
-                               printk( "   canprio_classify() match ok: ID 0x%x\n", canid);
-                               *res = f->res;
-                               return TC_POLICE_OK;
+                       can_id &= CAN_SFF_MASK;
+#ifdef SFF_BITMAP
+                       match = test_bit(can_id, r->match_sff);
+#else
+                       for (i = r->eff_rules_count; i < r->eff_rules_count + r->sff_rules_count; i++) {
+                               if ((r->rules_raw[i].can_id & r->rules_raw[i].can_mask & CAN_SFF_MASK) ==
+                                       (can_id & r->rules_raw[i].can_mask & CAN_SFF_MASK)) {
+
+                                       match = true;
+                                       break;
+                               }
                        }
+#endif
                }
-       }
-
-       return TC_POLICE_UNSPEC;
-}
-
-/*
- * Configure filter
- */
-static int canprio_set_parms(struct tcf_proto *tp, struct canprio_filter *f,
-                          unsigned long base, struct nlattr **tb,
-                          struct nlattr *est)
-{ 
-       //int err;
-       printk(" canprio_set_parms invoked\n");
 
-       if (tb[TCA_CANPRIO_CLASSID]) {
-               f->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
-               bitmap_zero(f->match, CAN_SFF_MASK + 1);
+               //if (r->inv_match_en)
+               //      match = !match;
 
-               tcf_bind_filter(tp, &f->res, base);
+               if (match) {
+                       *res = f->res;
+                       pr_debug( "   canprio_classify() match ok: ID 0x%x\n", can_id);
+                       rcu_read_unlock();
+                       return TC_POLICE_OK;
+               }
        }
 
-       return 0;
-} 
+       rcu_read_unlock();
+       return TC_POLICE_UNSPEC;
+}
 
 /*
- * Looks up a filter element by its handle and returns the internal filter ID
+ * Looks up a filter element by its handle and returns the internal 
+ * filter ID (i.e. pointer)
  */
 static unsigned long canprio_get(struct tcf_proto *tp, u32 handle)
 {
-       unsigned long l = 0UL;
-       struct canprio_head *head = (struct canprio_head *) tp->root;
+       struct canprio_head *head = (struct canprio_head *)tp->root;
        struct canprio_filter *f;
 
        if (head == NULL)
                return 0UL;
 
-       list_for_each_entry(f, &head->flist, link)
-               if (f->handle == handle)
-                       l = (unsigned long) f;
+       list_for_each_entry(f, &head->flist, link) {
+               if (f->handle == handle) {
+                       return (unsigned long) f;
+               }
+       }
 
-       return l;
+       return 0UL;
 }
 
 /*
@@ -185,6 +228,119 @@ static void canprio_put(struct tcf_proto *tp, unsigned long f)
 }
 
 
+static unsigned int canprio_gen_handle(struct tcf_proto *tp)
+{
+       struct canprio_head *head = (struct canprio_head *)tp->root;
+       int i = 0xFFFF;
+
+       while (i-- > 0) {
+               u32 h;
+
+               if ((head->hgenerator += 0x10000) == 0)
+                       head->hgenerator = 0x10000;
+
+               h = head->hgenerator;
+               if (canprio_get(tp, h) == 0);
+                       return h;
+       }
+       return 0;
+}
+
+
+static void canprio_rules_free_rcu(struct rcu_head *rcu)
+{
+       kfree(container_of(rcu, struct canprio_rules, rcu));
+}
+
+static int canprio_set_parms(struct tcf_proto *tp, struct canprio_filter *f,
+                               unsigned long base, struct nlattr **tb,
+                               struct nlattr *est)
+{
+       struct can_filter *canprio_nl_rules;
+       struct canprio_rules *rules_tmp;
+       int err;
+       int i;
+       pr_debug("%s() invoked\n", __FUNCTION__);
+
+       rules_tmp = kzalloc(sizeof(*rules_tmp), GFP_KERNEL);
+       if (rules_tmp == NULL)
+               return -ENOBUFS;
+
+       err = -EINVAL;
+       if (tb[TCA_CANPRIO_CLASSID] == NULL)
+               goto errout;
+
+       if (tb[TCA_CANPRIO_RULES]) {
+               canprio_nl_rules = nla_data(tb[TCA_CANPRIO_RULES]);
+               rules_tmp->sff_rules_count = 0;
+               rules_tmp->eff_rules_count = 0;
+               rules_tmp->rules_count = (nla_len(tb[TCA_CANPRIO_RULES]) / sizeof(struct can_filter));
+               pr_debug(" rules_count = %u\n", rules_tmp->rules_count);
+
+               rules_tmp->rules_raw = kzalloc(sizeof(struct can_filter) * rules_tmp->rules_count, GFP_KERNEL);
+               err = -ENOMEM;
+               if (rules_tmp->rules_raw == NULL)
+                       goto errout;
+
+               /* Process EFF frames */
+               for (i = 0; i < rules_tmp->rules_count; i++) {
+                       if ((canprio_nl_rules[i].can_id & CAN_EFF_FLAG) &&
+                               (canprio_nl_rules[i].can_mask & CAN_EFF_FLAG)) {
+
+                               memcpy(rules_tmp->rules_raw + rules_tmp->eff_rules_count, 
+                                       &canprio_nl_rules[i], sizeof(struct can_filter));
+                               rules_tmp->eff_rules_count ++;
+
+                               pr_debug(" can ID to match = 0x%x with mask 0x%x\n",
+                                       canprio_nl_rules[i].can_id, canprio_nl_rules[i].can_mask);
+                       } else {
+                               continue;
+                       }
+               }
+
+               /* Process SFF frames 
+                  We need two for() loops for copying rules into two contiguous areas in rules_raw */
+               for (i = 0; i < rules_tmp->rules_count; i++) {
+                       if ((canprio_nl_rules[i].can_id & CAN_EFF_FLAG) &&
+                               (canprio_nl_rules[i].can_mask & CAN_EFF_FLAG)) {
+                               
+                               continue;
+                       } else {
+                               memcpy(rules_tmp->rules_raw + rules_tmp->eff_rules_count + rules_tmp->sff_rules_count, 
+                                       &canprio_nl_rules[i], sizeof(struct can_filter));
+                               rules_tmp->sff_rules_count ++;
+
+#ifdef SFF_BITMAP
+                               canprio_sff_match_add(rules_tmp, canprio_nl_rules[i].can_id, 
+                                                     canprio_nl_rules[i].can_mask);
+#endif
+
+                               pr_debug(" can ID to match = 0x%x with mask 0x%x\n",
+                                       canprio_nl_rules[i].can_id, canprio_nl_rules[i].can_mask);
+                       }
+               }
+       }
+
+
+       if (tb[TCA_CANPRIO_INV_EN] != NULL)
+               rules_tmp->inv_match_en = nla_get_u32(tb[TCA_CANPRIO_INV_EN]);
+
+       if (f->rules == NULL) { // Setting parameters for newly created filter
+               rcu_assign_pointer(f->rules, rules_tmp);
+       } else { // Changing existing filter
+               struct canprio_rules *rules_old;
+
+               rules_old = xchg(&f->rules, rules_tmp);
+               call_rcu(&rules_old->rcu, canprio_rules_free_rcu);
+       }
+
+       return 0;
+
+errout:
+       kfree(rules_tmp);
+       return err;
+}
+
 /* 
  * Called for changing properties of an existing filter or after addition 
  * of a new filter to a class (by calling bind_tcf which binds an instance
@@ -193,91 +349,62 @@ static void canprio_put(struct tcf_proto *tp, unsigned long f)
  * @tp:     Structure representing instance of a filter. 
  *          Part of a linked list of all filters.
  * @base:
- * @handle:
+ * @handle:  
  * @tca:    Messages passed through the Netlink from userspace.
  * @arg:    ??? FIXME
  */
 static int canprio_change(struct tcf_proto *tp, unsigned long base, u32 handle,
                          struct nlattr **tca, unsigned long *arg)
 {
-       int err;
        struct canprio_head *head = (struct canprio_head *)tp->root;
        struct canprio_filter *f = (struct canprio_filter *)*arg;
        struct nlattr *tb[TCA_CANPRIO_MAX + 1];
-       int i;
+       int err;
        
-       printk(" canprio_change invoked\n");
+       pr_debug("%s() invoked\n", __FUNCTION__);
 
        if (tca[TCA_OPTIONS] == NULL)
                return -EINVAL;
 
-       //Parses a stream of attributes and stores a pointer to each attribute in
-       //the tb array accessible via the attribute type. Policy may be set to NULL 
-       //if no validation is required.
+       /Parses a stream of attributes and stores a pointer to each attribute in
+       the tb array accessible via the attribute type. Policy may be set to NULL 
+       if no validation is required.*/
        err = nla_parse_nested(tb, TCA_CANPRIO_MAX, tca[TCA_OPTIONS], canprio_policy);
        if (err < 0)                
                return err;
-
-       //Change existing filter (?)
+       /* Change existing filter (remove all settings and add 
+       them thereafter as if filter was newly created) */
        if (f != NULL) {
                if (handle && f->handle != handle)
                        return -EINVAL;
+
+               pr_debug("[change existing filter]\n");
                return canprio_set_parms(tp, f, base, tb, tca[TCA_RATE]);
        }
 
-       //Create new filter (?)
+       /* Create new filter */
        err = -ENOBUFS;
        f = kzalloc(sizeof(*f), GFP_KERNEL);
        if (f == NULL)
                goto errout;
 
+       if (tb[TCA_CANPRIO_CLASSID]) {
+               f->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
+               tcf_bind_filter(tp, &f->res, base);
+       }
 
-       //FIXME why?
        err = -EINVAL;
-       if (handle)
+       if (handle) /* handle passed from userspace */
                f->handle = handle;
        else {
-               //FIXME wat?
-               unsigned int i = 0x80000000;
-               do {
-                       if (++head->hgenerator == 0x7FFFFFFF)
-                               head->hgenerator = 1;
-               } while (--i > 0 && canprio_get(tp, head->hgenerator));
-
-               if (i <= 0) {
-                       pr_err("Insufficient number of handles\n");
-                       goto errout;
-               }
-
-               f->handle = head->hgenerator;
+               f->handle = canprio_gen_handle(tp);
        }
 
-
-       //Configure newly created filter
+       //Configure filter
        err = canprio_set_parms(tp, f, base, tb, tca[TCA_RATE]);
        if (err < 0)
                goto errout;    
 
-
-       //Parse arguments
-       if (tb[TCA_CANPRIO_RULES] == NULL)
-               return -EINVAL;
-
-       canprio_rules = nla_data(tb[TCA_CANPRIO_RULES]);
-       canprio_rules_count = (nla_len(tb[TCA_CANPRIO_RULES]) / sizeof(struct canprio_rule));
-       printk(" rules_count = %u\n", canprio_rules_count);
-
-       for (i = 0; i < canprio_rules_count; i++) {
-               if (canprio_rules[i].canid & CAN_EFF_FLAG) {
-                       eff_match_add(f, canprio_rules[i].canid, canprio_rules[i].canid_mask);
-               } else {
-                       sff_match_add(f, canprio_rules[i].canid, canprio_rules[i].canid_mask);
-               }
-
-               printk(" can ID to match = 0x%x with mask 0x%x\n", 
-                       canprio_rules[i].canid, canprio_rules[i].canid_mask);
-       }
-
        //Add newly created filter to list of all filters
        tcf_tree_lock(tp);
        list_add(&f->link, &head->flist);
@@ -287,7 +414,7 @@ static int canprio_change(struct tcf_proto *tp, unsigned long base, u32 handle,
        return 0;
 
 errout:
-       if (*arg == 0UL && f) //FIXME why 0UL?
+       if (*arg == 0UL && f)
                kfree(f);
 
        return err;
@@ -296,9 +423,11 @@ errout:
 
 static void canprio_delete_filter(struct tcf_proto *tp, struct canprio_filter *f)
 {
-        tcf_unbind_filter(tp, &f->res);
-        //tcf_exts_destroy(tp, &f->exts);
-        //tcf_em_tree_destroy(tp, &f->ematches);
+       tcf_unbind_filter(tp, &f->res);
+
+       rcu_barrier();
+       kfree(f->rules->rules_raw);
+       kfree(f->rules);
         kfree(f);
 }
 
@@ -326,6 +455,8 @@ static int canprio_delete(struct tcf_proto *tp, unsigned long arg)
        struct canprio_filter *t;
        struct canprio_filter *f = (struct canprio_filter *)arg;
 
+       rcu_barrier(); /* Wait for completion of call_rcu()'s */
+
        list_for_each_entry(t, &head->flist, link)
                if (t == f) {
                        tcf_tree_lock(tp);
@@ -338,22 +469,90 @@ static int canprio_delete(struct tcf_proto *tp, unsigned long arg)
        return -ENOENT;
 }
 
-//FIXME copied from cls_basic, not sure if ok;
+
+/*
+ * Initialize filter
+ */
 static int canprio_init(struct tcf_proto *tp)
 {
        struct canprio_head *head;
-
-       printk(" canprio_init invoked\n");
+       pr_debug(" canprio_init invoked\n");
 
        head = kzalloc(sizeof(*head), GFP_KERNEL);
        if (head == NULL)
                return -ENOBUFS;
+
        INIT_LIST_HEAD(&head->flist);
        tp->root = head;
+       tp->protocol = htons(ETH_P_CAN); /* Work only on AF_CAN packets - not tested! */
 
        return 0;
 }
 
+/*
+ * Iterates over all elements of a filter and invokes a callback function
+ * for each of them. This is used to obtain diagnostic data
+ */
+static void canprio_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+{
+       struct canprio_head *head = (struct canprio_head *) tp->root;
+       struct canprio_filter *f;
+       
+       pr_debug("%s() invoked\n", __FUNCTION__);
+
+       list_for_each_entry(f, &head->flist, link) {
+               if (arg->count < arg->skip)
+                       goto skip;
+
+               if (arg->fn(tp, (unsigned long) f, arg) < 0) {
+                       arg->stop = 1;
+                       break;
+               }
+skip:   
+               arg->count++;
+       }
+}
+
+/* 
+ * Returns diagnostic data for a filter or one of its elements. 
+ */
+static int canprio_dump(struct tcf_proto *tp, unsigned long fh,
+                    struct sk_buff *skb, struct tcmsg *t)
+{
+       struct canprio_filter *f = (struct canprio_filter *) fh;
+       struct nlattr *nest;
+       struct canprio_rules *r;
+       pr_debug("%s() invoked\n", __FUNCTION__);
+
+       if (f == NULL)
+               return skb->len;
+
+       rcu_read_lock();
+       r = rcu_dereference(f->rules);
+       t->tcm_handle = f->handle;
+
+       nest = nla_nest_start(skb, TCA_OPTIONS);
+       if (nest == NULL)
+               goto nla_put_failure;
+
+       if (f->res.classid)
+               NLA_PUT_U32(skb, TCA_CANPRIO_CLASSID, f->res.classid);
+
+       NLA_PUT(skb, TCA_CANPRIO_RULES, r->rules_count * 
+               sizeof(struct can_filter), r->rules_raw);
+
+
+       nla_nest_end(skb, nest);
+
+       rcu_read_unlock();
+       return skb->len;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nest);
+       rcu_read_unlock();
+       return -1;
+}
+
 
 static struct tcf_proto_ops cls_canprio_ops __read_mostly = {
        .kind           =       "canprio",
@@ -364,22 +563,31 @@ static struct tcf_proto_ops cls_canprio_ops __read_mostly = {
        .put            =       canprio_put,
        .change         =       canprio_change,
        .delete         =       canprio_delete,
+       .walk           =       canprio_walk,
+       .dump           =       canprio_dump,
        .owner          =       THIS_MODULE,
 };
 
 static int __init init_canprio(void)
 {
-       printk("Canprio loaded\n");
+       pr_debug("Canprio loaded\n");
+#ifdef SFF_BITMAP
+       pr_debug("SFF frames stored in bitmap\n");
+#else
+       pr_debug("SFF frames stored in array\n");
+#endif
        return register_tcf_proto_ops(&cls_canprio_ops);
 }
 
 static void __exit exit_canprio(void)
 {
-       printk("Canprio removed\n");
+       pr_debug("Canprio removed\n");
        unregister_tcf_proto_ops(&cls_canprio_ops);
 }
 
 module_init(init_canprio);
 module_exit(exit_canprio);
 MODULE_LICENSE("GPL");
+MODULE_AUTHOR(""); // FIXME
+MODULE_DESCRIPTION("Controller Area Network can_id classifier");