aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-04-03 10:28:50 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-11 16:23:50 -0400
commit3a25a8c8b75b430c4f4022918e26fa51d557ecde (patch)
treed4863b7f17c2ea44fb523e29951b6bd202ddb1ad /net/mac80211/iface.c
parent4b6f1dd6a6faf4ed8d209bbd548e78b15e55aee8 (diff)
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,
149 return 0; 149 return 0;
150} 150}
151 151
152static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata)
153{
154 int n_queues = sdata->local->hw.queues;
155 int i;
156
157 for (i = 0; i < IEEE80211_NUM_ACS; i++) {
158 if (WARN_ON_ONCE(sdata->vif.hw_queue[i] ==
159 IEEE80211_INVAL_HW_QUEUE))
160 return -EINVAL;
161 if (WARN_ON_ONCE(sdata->vif.hw_queue[i] >=
162 n_queues))
163 return -EINVAL;
164 }
165
166 if (sdata->vif.type != NL80211_IFTYPE_AP) {
167 sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
168 return 0;
169 }
170
171 if (WARN_ON_ONCE(sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE))
172 return -EINVAL;
173
174 if (WARN_ON_ONCE(sdata->vif.cab_queue >= n_queues))
175 return -EINVAL;
176
177 return 0;
178}
179
152void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, 180void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
153 const int offset) 181 const int offset)
154{ 182{
@@ -169,6 +197,20 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
169#undef ADJUST 197#undef ADJUST
170} 198}
171 199
200static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
201{
202 struct ieee80211_local *local = sdata->local;
203 int i;
204
205 for (i = 0; i < IEEE80211_NUM_ACS; i++) {
206 if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
207 sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE;
208 else
209 sdata->vif.hw_queue[i] = i;
210 }
211 sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
212}
213
172static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) 214static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
173{ 215{
174 struct ieee80211_sub_if_data *sdata; 216 struct ieee80211_sub_if_data *sdata;
@@ -190,6 +232,8 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
190 snprintf(sdata->name, IFNAMSIZ, "%s-monitor", 232 snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
191 wiphy_name(local->hw.wiphy)); 233 wiphy_name(local->hw.wiphy));
192 234
235 ieee80211_set_default_queues(sdata);
236
193 ret = drv_add_interface(local, sdata); 237 ret = drv_add_interface(local, sdata);
194 if (WARN_ON(ret)) { 238 if (WARN_ON(ret)) {
195 /* ok .. stupid driver, it asked for this! */ 239 /* ok .. stupid driver, it asked for this! */
@@ -197,6 +241,12 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
197 return ret; 241 return ret;
198 } 242 }
199 243
244 ret = ieee80211_check_queues(sdata);
245 if (ret) {
246 kfree(sdata);
247 return ret;
248 }
249
200 rcu_assign_pointer(local->monitor_sdata, sdata); 250 rcu_assign_pointer(local->monitor_sdata, sdata);
201 251
202 return 0; 252 return 0;
@@ -344,6 +394,9 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
344 res = drv_add_interface(local, sdata); 394 res = drv_add_interface(local, sdata);
345 if (res) 395 if (res)
346 goto err_stop; 396 goto err_stop;
397 res = ieee80211_check_queues(sdata);
398 if (res)
399 goto err_del_interface;
347 } 400 }
348 401
349 if (sdata->vif.type == NL80211_IFTYPE_AP) { 402 if (sdata->vif.type == NL80211_IFTYPE_AP) {
@@ -1040,6 +1093,13 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
1040 if (ret) 1093 if (ret)
1041 type = sdata->vif.type; 1094 type = sdata->vif.type;
1042 1095
1096 /*
1097 * Ignore return value here, there's not much we can do since
1098 * the driver changed the interface type internally already.
1099 * The warnings will hopefully make driver authors fix it :-)
1100 */
1101 ieee80211_check_queues(sdata);
1102
1043 ieee80211_setup_sdata(sdata, type); 1103 ieee80211_setup_sdata(sdata, type);
1044 1104
1045 err = ieee80211_do_open(sdata->dev, false); 1105 err = ieee80211_do_open(sdata->dev, false);
@@ -1266,6 +1326,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
1266 sizeof(sdata->rc_rateidx_mcs_mask[i])); 1326 sizeof(sdata->rc_rateidx_mcs_mask[i]));
1267 } 1327 }
1268 1328
1329 ieee80211_set_default_queues(sdata);
1330
1269 /* setup type-dependent data */ 1331 /* setup type-dependent data */
1270 ieee80211_setup_sdata(sdata, type); 1332 ieee80211_setup_sdata(sdata, type);
1271 1333