cfg80211: avoid duplicate entries on regdomain intersection
authorEliad Peller <eliad@wizery.com>
Wed, 3 Sep 2014 12:25:03 +0000 (15:25 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 5 Sep 2014 11:52:08 +0000 (13:52 +0200)
The regdom intersection code simply tries intersecting
each rule of the source with each rule of the target.

Since the resulting intersections are not observed
as a whole, this can result in multiple overlapping/duplicate
entries.

Make the rule addition a bit more smarter, by looking
for rules that can be contained within other rules,
and adding only extended ones.

Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/wireless/reg.c

index 8bfc9b172a49b10c8f22589821f690e435b827ae..b725a31a475178ec99de0e9aabc4d89e2ebb7d40 100644 (file)
@@ -799,6 +799,57 @@ static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
        return 0;
 }
 
+/* check whether old rule contains new rule */
+static bool rule_contains(struct ieee80211_reg_rule *r1,
+                         struct ieee80211_reg_rule *r2)
+{
+       /* for simplicity, currently consider only same flags */
+       if (r1->flags != r2->flags)
+               return false;
+
+       /* verify r1 is more restrictive */
+       if ((r1->power_rule.max_antenna_gain >
+            r2->power_rule.max_antenna_gain) ||
+           r1->power_rule.max_eirp > r2->power_rule.max_eirp)
+               return false;
+
+       /* make sure r2's range is contained within r1 */
+       if (r1->freq_range.start_freq_khz > r2->freq_range.start_freq_khz ||
+           r1->freq_range.end_freq_khz < r2->freq_range.end_freq_khz)
+               return false;
+
+       /* and finally verify that r1.max_bw >= r2.max_bw */
+       if (r1->freq_range.max_bandwidth_khz <
+           r2->freq_range.max_bandwidth_khz)
+               return false;
+
+       return true;
+}
+
+/* add or extend current rules. do nothing if rule is already contained */
+static void add_rule(struct ieee80211_reg_rule *rule,
+                    struct ieee80211_reg_rule *reg_rules, u32 *n_rules)
+{
+       struct ieee80211_reg_rule *tmp_rule;
+       int i;
+
+       for (i = 0; i < *n_rules; i++) {
+               tmp_rule = &reg_rules[i];
+               /* rule is already contained - do nothing */
+               if (rule_contains(tmp_rule, rule))
+                       return;
+
+               /* extend rule if possible */
+               if (rule_contains(rule, tmp_rule)) {
+                       memcpy(tmp_rule, rule, sizeof(*rule));
+                       return;
+               }
+       }
+
+       memcpy(&reg_rules[*n_rules], rule, sizeof(*rule));
+       (*n_rules)++;
+}
+
 /**
  * regdom_intersect - do the intersection between two regulatory domains
  * @rd1: first regulatory domain
@@ -818,12 +869,10 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
 {
        int r, size_of_regd;
        unsigned int x, y;
-       unsigned int num_rules = 0, rule_idx = 0;
+       unsigned int num_rules = 0;
        const struct ieee80211_reg_rule *rule1, *rule2;
-       struct ieee80211_reg_rule *intersected_rule;
+       struct ieee80211_reg_rule intersected_rule;
        struct ieee80211_regdomain *rd;
-       /* This is just a dummy holder to help us count */
-       struct ieee80211_reg_rule dummy_rule;
 
        if (!rd1 || !rd2)
                return NULL;
@@ -841,7 +890,7 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
                for (y = 0; y < rd2->n_reg_rules; y++) {
                        rule2 = &rd2->reg_rules[y];
                        if (!reg_rules_intersect(rd1, rd2, rule1, rule2,
-                                                &dummy_rule))
+                                                &intersected_rule))
                                num_rules++;
                }
        }
@@ -856,34 +905,24 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
        if (!rd)
                return NULL;
 
-       for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) {
+       for (x = 0; x < rd1->n_reg_rules; x++) {
                rule1 = &rd1->reg_rules[x];
-               for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) {
+               for (y = 0; y < rd2->n_reg_rules; y++) {
                        rule2 = &rd2->reg_rules[y];
-                       /*
-                        * This time around instead of using the stack lets
-                        * write to the target rule directly saving ourselves
-                        * a memcpy()
-                        */
-                       intersected_rule = &rd->reg_rules[rule_idx];
                        r = reg_rules_intersect(rd1, rd2, rule1, rule2,
-                                               intersected_rule);
+                                               &intersected_rule);
                        /*
                         * No need to memset here the intersected rule here as
                         * we're not using the stack anymore
                         */
                        if (r)
                                continue;
-                       rule_idx++;
-               }
-       }
 
-       if (rule_idx != num_rules) {
-               kfree(rd);
-               return NULL;
+                       add_rule(&intersected_rule, rd->reg_rules,
+                                &rd->n_reg_rules);
+               }
        }
 
-       rd->n_reg_rules = num_rules;
        rd->alpha2[0] = '9';
        rd->alpha2[1] = '8';
        rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region,