cfg80211: do not replace BSS structs
authorJohannes Berg <johannes@sipsolutions.net>
Thu, 16 Apr 2009 13:00:58 +0000 (15:00 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 17 Apr 2009 19:27:13 +0000 (15:27 -0400)
Instead, allocate extra IE memory if necessary. Normally,
this isn't even necessary since there's enough space.

This is a better way of correcting the "held BSS can
disappear" issue, but also a lot more code. It is also
necessary for proper auth/assoc BSS handling in the
future.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/wireless/core.h
net/wireless/scan.c

index d43daa2..0a592e4 100644 (file)
@@ -90,7 +90,7 @@ struct cfg80211_internal_bss {
        struct rb_node rbn;
        unsigned long ts;
        struct kref ref;
-       bool hold;
+       bool hold, ies_allocated;
 
        /* must be last because of priv member */
        struct cfg80211_bss pub;
index 4c77669..2ae65b3 100644 (file)
@@ -58,6 +58,10 @@ static void bss_release(struct kref *ref)
        bss = container_of(ref, struct cfg80211_internal_bss, ref);
        if (bss->pub.free_priv)
                bss->pub.free_priv(&bss->pub);
+
+       if (bss->ies_allocated)
+               kfree(bss->pub.information_elements);
+
        kfree(bss);
 }
 
@@ -360,21 +364,41 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
 
        found = rb_find_bss(dev, res);
 
-       if (found && overwrite) {
-               list_replace(&found->list, &res->list);
-               rb_replace_node(&found->rbn, &res->rbn,
-                               &dev->bss_tree);
-               /* XXX: workaround */
-               res->hold = found->hold;
-               kref_put(&found->ref, bss_release);
-               found = res;
-       } else if (found) {
+       if (found) {
                kref_get(&found->ref);
                found->pub.beacon_interval = res->pub.beacon_interval;
                found->pub.tsf = res->pub.tsf;
                found->pub.signal = res->pub.signal;
                found->pub.capability = res->pub.capability;
                found->ts = res->ts;
+
+               /* overwrite IEs */
+               if (overwrite) {
+                       size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
+                       size_t ielen = res->pub.len_information_elements;
+
+                       if (ksize(found) >= used + ielen) {
+                               memcpy(found->pub.information_elements,
+                                      res->pub.information_elements, ielen);
+                               found->pub.len_information_elements = ielen;
+                       } else {
+                               u8 *ies = found->pub.information_elements;
+
+                               if (found->ies_allocated) {
+                                       if (ksize(ies) < ielen)
+                                               ies = krealloc(ies, ielen,
+                                                              GFP_ATOMIC);
+                               } else
+                                       ies = kmalloc(ielen, GFP_ATOMIC);
+
+                               if (ies) {
+                                       memcpy(ies, res->pub.information_elements, ielen);
+                                       found->ies_allocated = true;
+                                       found->pub.information_elements = ies;
+                               }
+                       }
+               }
+
                kref_put(&res->ref, bss_release);
        } else {
                /* this "consumes" the reference */