Fix locking issues with loading of rate_control modules. This still doesn't
allow changing of the modules on the fly.

Signed-off-by: Jiri Benc <[EMAIL PROTECTED]>

---

 net/d80211/Makefile         |    1 
 net/d80211/ieee80211.c      |  108 ++++++++++++++-----------------------------
 net/d80211/ieee80211_rate.c |   92 +++++++++++++++++++++++++++++++++++++
 net/d80211/ieee80211_rate.h |    8 ++-
 net/d80211/rate_control.c   |    3 +
 5 files changed, 136 insertions(+), 76 deletions(-)
 create mode 100644 net/d80211/ieee80211_rate.c

1e55cd13b3f20ac9560270a5455d58f0cbd2acd9
diff --git a/net/d80211/Makefile b/net/d80211/Makefile
index 2a2a0c6..27d90e5 100644
--- a/net/d80211/Makefile
+++ b/net/d80211/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_D80211) += 80211.o rate_con
        ieee80211_sta.o \
        ieee80211_dev.o \
        ieee80211_iface.o \
+       ieee80211_rate.o \
        ieee80211_sysfs.o \
        ieee80211_sysfs_sta.o \
        michael.o \
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index b138eb0..8c4a6d6 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -46,16 +46,6 @@ static unsigned char eapol_header[] =
 { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e };
 
 
-struct rate_control_algs {
-       struct rate_control_algs *next;
-       struct rate_control_ops *ops;
-};
-
-static struct rate_control_algs *ieee80211_rate_ctrl_algs;
-
-static int rate_control_initialize(struct ieee80211_local *local);
-
-
 static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len);
 
 static int ieee80211_mgmt_start_xmit(struct sk_buff *skb,
@@ -4312,6 +4302,39 @@ static void ieee80211_precalc_modes(stru
        }
 }
 
+static int rate_control_initialize(struct ieee80211_local *local)
+{
+       struct rate_control_ops *ops;
+
+       ops = ieee80211_rate_control_ops_get(NULL);
+       if (!ops) {
+               printk(KERN_WARNING "%s: Failed to select rate control "
+                      "algorithm\n", local->mdev->name);
+               return -1;
+       }
+       local->rate_ctrl_priv = rate_control_alloc(ops, local);
+       if (!local->rate_ctrl_priv) {
+               ieee80211_rate_control_ops_put(ops);
+               return -1;
+       }
+       local->rate_ctrl = ops;
+
+       printk(KERN_DEBUG "%s: Selected rate control "
+              "algorithm '%s'\n", local->mdev->name,
+              local->rate_ctrl->name);
+
+       return 0;
+}
+
+static void rate_control_deinitialize(struct ieee80211_local *local)
+{
+       struct rate_control_ops *ops;
+
+       rate_control_free(local);
+       ops = local->rate_ctrl;
+       local->rate_ctrl = NULL;
+       ieee80211_rate_control_ops_put(ops);
+}
 
 struct net_device *ieee80211_alloc_hw(size_t priv_data_len,
                                      void (*setup)(struct net_device *))
@@ -4520,7 +4543,7 @@ int ieee80211_register_hw(struct net_dev
        return 0;
 
 fail_rate_attrs:
-       rate_control_free(local);
+       rate_control_deinitialize(local);
 fail_rate:
        ieee80211_sysfs_remove_netdevice(dev);
 fail_if_sysfs:
@@ -4639,7 +4662,7 @@ EXPORT_SYMBOL(ieee80211_free_hw);
 
 void ieee80211_release_hw(struct ieee80211_local *local)
 {
-       rate_control_free(local);
+       rate_control_deinitialize(local);
        kfree(local);
 }
 
@@ -4742,67 +4765,6 @@ void * ieee80211_dev_stats(struct net_de
 }
 EXPORT_SYMBOL(ieee80211_dev_stats);
 
-int ieee80211_rate_control_register(struct rate_control_ops *ops)
-{
-       struct rate_control_algs *alg;
-
-       alg = kzalloc(sizeof(*alg), GFP_KERNEL);
-       if (!alg)
-               return -1;
-
-       alg->next = ieee80211_rate_ctrl_algs;
-       alg->ops = ops;
-       ieee80211_rate_ctrl_algs = alg;
-
-       return 0;
-}
-EXPORT_SYMBOL(ieee80211_rate_control_register);
-
-void ieee80211_rate_control_unregister(struct rate_control_ops *ops)
-{
-       struct rate_control_algs *alg, *prev;
-
-       prev = NULL;
-       alg = ieee80211_rate_ctrl_algs;
-       while (alg) {
-               if (alg->ops == ops) {
-                       if (prev)
-                               prev->next = alg->next;
-                       else
-                               ieee80211_rate_ctrl_algs = alg->next;
-                       kfree(alg);
-                       break;
-               }
-               prev = alg;
-               alg = alg->next;
-       }
-}
-EXPORT_SYMBOL(ieee80211_rate_control_unregister);
-
-static int rate_control_initialize(struct ieee80211_local *local)
-{
-       struct rate_control_algs *algs;
-
-       if (!ieee80211_rate_ctrl_algs)
-               request_module("ieee80211_rate_control");
-
-       for (algs = ieee80211_rate_ctrl_algs; algs; algs = algs->next) {
-               local->rate_ctrl = algs->ops;
-               local->rate_ctrl_priv = rate_control_alloc(local);
-               if (local->rate_ctrl_priv) {
-                       printk(KERN_DEBUG "%s: Selected rate control "
-                              "algorithm '%s'\n", local->mdev->name,
-                              local->rate_ctrl->name);
-                       return 0;
-               }
-       }
-
-       printk(KERN_WARNING "%s: Failed to select rate control algorithm\n",
-              local->mdev->name);
-       return -1;
-}
-
-
 static int __init ieee80211_init(void)
 {
        struct sk_buff *skb;
diff --git a/net/d80211/ieee80211_rate.c b/net/d80211/ieee80211_rate.c
new file mode 100644
index 0000000..3ec370f
--- /dev/null
+++ b/net/d80211/ieee80211_rate.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2006 Jiri Benc <[EMAIL PROTECTED]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include "ieee80211_rate.h"
+#include "ieee80211_i.h"
+
+struct rate_control_alg {
+       struct list_head list;
+       struct rate_control_ops *ops;
+};
+
+static LIST_HEAD(rate_ctrl_algs);
+static DEFINE_MUTEX(rate_ctrl_mutex);
+
+int ieee80211_rate_control_register(struct rate_control_ops *ops)
+{
+       struct rate_control_alg *alg;
+
+       alg = kmalloc(sizeof(*alg), GFP_KERNEL);
+       if (alg == NULL) {
+               return -ENOMEM;
+       }
+       memset(alg, 0, sizeof(*alg));
+       alg->ops = ops;
+
+       mutex_lock(&rate_ctrl_mutex);
+       list_add_tail(&alg->list, &rate_ctrl_algs);
+       mutex_unlock(&rate_ctrl_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(ieee80211_rate_control_register);
+
+void ieee80211_rate_control_unregister(struct rate_control_ops *ops)
+{
+       struct rate_control_alg *alg;
+
+       mutex_lock(&rate_ctrl_mutex);
+       list_for_each_entry(alg, &rate_ctrl_algs, list) {
+               if (alg->ops == ops) {
+                       list_del(&alg->list);
+                       break;
+               }
+       }
+       mutex_unlock(&rate_ctrl_mutex);
+       kfree(alg);
+}
+EXPORT_SYMBOL(ieee80211_rate_control_unregister);
+
+static struct rate_control_ops *ieee80211_try_rate_control_ops_get(char *name)
+{
+       struct rate_control_alg *alg;
+       struct rate_control_ops *ops = NULL;
+
+       mutex_lock(&rate_ctrl_mutex);
+       list_for_each_entry(alg, &rate_ctrl_algs, list) {
+               if (!name || !strcmp(alg->ops->name, name))
+                       if (try_module_get(alg->ops->module)) {
+                               ops = alg->ops;
+                               break;
+                       }
+       }
+       mutex_unlock(&rate_ctrl_mutex);
+       return ops;
+}
+
+/* Get the rate control algorithm. If `name' is NULL, get the first
+ * available algorithm. */
+struct rate_control_ops *ieee80211_rate_control_ops_get(char *name)
+{
+       struct rate_control_ops *ops;
+
+       ops = ieee80211_try_rate_control_ops_get(name);
+       if (!ops) {
+               request_module("rc80211_%s", name ? name : "default");
+               ops = ieee80211_try_rate_control_ops_get(name);
+       }
+       return ops;
+}
+
+void ieee80211_rate_control_ops_put(struct rate_control_ops *ops)
+{
+       module_put(ops->module);
+}
diff --git a/net/d80211/ieee80211_rate.h b/net/d80211/ieee80211_rate.h
index e1c9e05..2a4c662 100644
--- a/net/d80211/ieee80211_rate.h
+++ b/net/d80211/ieee80211_rate.h
@@ -37,6 +37,7 @@ struct rate_control_extra {
 
 
 struct rate_control_ops {
+       struct module *module;
        const char *name;
        void (*tx_status)(struct net_device *dev, struct sk_buff *skb,
                          struct ieee80211_tx_status *status);
@@ -61,6 +62,8 @@ struct rate_control_ops {
 int ieee80211_rate_control_register(struct rate_control_ops *ops);
 void ieee80211_rate_control_unregister(struct rate_control_ops *ops);
 
+struct rate_control_ops *ieee80211_rate_control_ops_get(char *name);
+void ieee80211_rate_control_ops_put(struct rate_control_ops *ops);
 
 static inline void rate_control_tx_status(struct net_device *dev,
                                          struct sk_buff *skb,
@@ -93,9 +96,10 @@ static inline void rate_control_clear(st
 }
 
 
-static inline void * rate_control_alloc(struct ieee80211_local *local)
+static inline void *rate_control_alloc(struct rate_control_ops *ops,
+                                      struct ieee80211_local *local)
 {
-       return local->rate_ctrl->alloc(local);
+       return ops->alloc(local);
 }
 
 
diff --git a/net/d80211/rate_control.c b/net/d80211/rate_control.c
index 90326a8..6703931 100644
--- a/net/d80211/rate_control.c
+++ b/net/d80211/rate_control.c
@@ -28,7 +28,7 @@ #define RATE_CONTROL_EMERG_DEC 2
 #define RATE_CONTROL_INTERVAL (HZ / 20)
 #define RATE_CONTROL_MIN_TX 10
 
-MODULE_ALIAS("ieee80211_rate_control");
+MODULE_ALIAS("rc80211_default");
 
 static void rate_control_rate_inc(struct ieee80211_local *local,
                                  struct sta_info *sta)
@@ -361,6 +361,7 @@ static void rate_control_simple_remove_s
 }
 
 static struct rate_control_ops rate_control_simple = {
+       .module = THIS_MODULE,
        .name = "simple",
        .tx_status = rate_control_simple_tx_status,
        .get_rate = rate_control_simple_get_rate,
-- 
1.3.0

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to