summaryrefslogtreecommitdiffstats
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-04-03 16:28:50 +0200
committerJohn W. Linville <linville@tuxdriver.com>2012-04-11 16:23:50 -0400
commit3a25a8c8b75b430c4f4022918e26fa51d557ecde (patch)
treed4863b7f17c2ea44fb523e29951b6bd202ddb1ad /net/mac80211/iface.c
parent4b6f1dd6a6faf4ed8d209bbd548e78b15e55aee8 (diff)
downloadlinux-3a25a8c8b75b430c4f4022918e26fa51d557ecde.tar.bz2
mac80211: add improved HW queue control
mac80211 currently only supports one hardware queue per AC. This is already problematic for off-channel uses since if we go off channel while the BE queue is full and then try to send an off-channel frame the frame will never go out. This will become worse when we support multi-channel since then a queue on one channel might be full, but we have to stop the software queue for all channels. That is obviously not desirable. To address this problem allow drivers to register more hardware queues, and allow them to map them to virtual interfaces. When they stop a hardware queue the corresponding AC software queues on the correct interfaces will be stopped as well. Additionally, there's an off-channel queue to solve that problem and a per-interface after-DTIM beacon queue. This allows drivers to manage software queues closer to how the hardware works. Currently, there's a limit of 16 hardware queues. This may or may not be sufficient, we can adjust it as needed. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 2b88cb278fc4..ed297649c577 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -149,6 +149,34 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
return 0;
}
+static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata)
+{
+ int n_queues = sdata->local->hw.queues;
+ int i;
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ if (WARN_ON_ONCE(sdata->vif.hw_queue[i] ==
+ IEEE80211_INVAL_HW_QUEUE))
+ return -EINVAL;
+ if (WARN_ON_ONCE(sdata->vif.hw_queue[i] >=
+ n_queues))
+ return -EINVAL;
+ }
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP) {
+ sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
+ return 0;
+ }
+
+ if (WARN_ON_ONCE(sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE))
+ return -EINVAL;
+
+ if (WARN_ON_ONCE(sdata->vif.cab_queue >= n_queues))
+ return -EINVAL;
+
+ return 0;
+}
+
void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
const int offset)
{
@@ -169,6 +197,20 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
#undef ADJUST
}
+static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ int i;
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
+ sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE;
+ else
+ sdata->vif.hw_queue[i] = i;
+ }
+ sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
+}
+
static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
@@ -190,6 +232,8 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
wiphy_name(local->hw.wiphy));
+ ieee80211_set_default_queues(sdata);
+
ret = drv_add_interface(local, sdata);
if (WARN_ON(ret)) {
/* ok .. stupid driver, it asked for this! */
@@ -197,6 +241,12 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
return ret;
}
+ ret = ieee80211_check_queues(sdata);
+ if (ret) {
+ kfree(sdata);
+ return ret;
+ }
+
rcu_assign_pointer(local->monitor_sdata, sdata);
return 0;
@@ -344,6 +394,9 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
res = drv_add_interface(local, sdata);
if (res)
goto err_stop;
+ res = ieee80211_check_queues(sdata);
+ if (res)
+ goto err_del_interface;
}
if (sdata->vif.type == NL80211_IFTYPE_AP) {
@@ -1040,6 +1093,13 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
if (ret)
type = sdata->vif.type;
+ /*
+ * Ignore return value here, there's not much we can do since
+ * the driver changed the interface type internally already.
+ * The warnings will hopefully make driver authors fix it :-)
+ */
+ ieee80211_check_queues(sdata);
+
ieee80211_setup_sdata(sdata, type);
err = ieee80211_do_open(sdata->dev, false);
@@ -1266,6 +1326,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sizeof(sdata->rc_rateidx_mcs_mask[i]));
}
+ ieee80211_set_default_queues(sdata);
+
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);