Commit 7da7cc1d authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: per interface idle notification

Sometimes we don't just need to know whether or
not the device is idle, but also per interface.
This adds that reporting capability to mac80211.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 1fdaa46e
...@@ -149,6 +149,7 @@ struct ieee80211_low_level_stats { ...@@ -149,6 +149,7 @@ struct ieee80211_low_level_stats {
* @BSS_CHANGED_ARP_FILTER: Hardware ARP filter address list or state changed. * @BSS_CHANGED_ARP_FILTER: Hardware ARP filter address list or state changed.
* @BSS_CHANGED_QOS: QoS for this association was enabled/disabled. Note * @BSS_CHANGED_QOS: QoS for this association was enabled/disabled. Note
* that it is only ever disabled for station mode. * that it is only ever disabled for station mode.
* @BSS_CHANGED_IDLE: Idle changed for this BSS/interface.
*/ */
enum ieee80211_bss_change { enum ieee80211_bss_change {
BSS_CHANGED_ASSOC = 1<<0, BSS_CHANGED_ASSOC = 1<<0,
...@@ -165,6 +166,7 @@ enum ieee80211_bss_change { ...@@ -165,6 +166,7 @@ enum ieee80211_bss_change {
BSS_CHANGED_IBSS = 1<<11, BSS_CHANGED_IBSS = 1<<11,
BSS_CHANGED_ARP_FILTER = 1<<12, BSS_CHANGED_ARP_FILTER = 1<<12,
BSS_CHANGED_QOS = 1<<13, BSS_CHANGED_QOS = 1<<13,
BSS_CHANGED_IDLE = 1<<14,
/* when adding here, make sure to change ieee80211_reconfig */ /* when adding here, make sure to change ieee80211_reconfig */
}; };
...@@ -223,6 +225,9 @@ enum ieee80211_bss_change { ...@@ -223,6 +225,9 @@ enum ieee80211_bss_change {
* hardware must not perform any ARP filtering. Note, that the filter will * hardware must not perform any ARP filtering. Note, that the filter will
* be enabled also in promiscuous mode. * be enabled also in promiscuous mode.
* @qos: This is a QoS-enabled BSS. * @qos: This is a QoS-enabled BSS.
* @idle: This interface is idle. There's also a global idle flag in the
* hardware config which may be more appropriate depending on what
* your driver/device needs to do.
*/ */
struct ieee80211_bss_conf { struct ieee80211_bss_conf {
const u8 *bssid; const u8 *bssid;
...@@ -247,6 +252,7 @@ struct ieee80211_bss_conf { ...@@ -247,6 +252,7 @@ struct ieee80211_bss_conf {
u8 arp_addr_cnt; u8 arp_addr_cnt;
bool arp_filter_enabled; bool arp_filter_enabled;
bool qos; bool qos;
bool idle;
}; };
/** /**
......
...@@ -920,12 +920,14 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, ...@@ -920,12 +920,14 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN); memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN);
sdata->u.ibss.ssid_len = params->ssid_len; sdata->u.ibss.ssid_len = params->ssid_len;
mutex_unlock(&sdata->u.ibss.mtx);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local); ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
ieee80211_queue_work(&sdata->local->hw, &sdata->work); ieee80211_queue_work(&sdata->local->hw, &sdata->work);
mutex_unlock(&sdata->u.ibss.mtx);
return 0; return 0;
} }
...@@ -980,7 +982,9 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) ...@@ -980,7 +982,9 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&sdata->u.ibss.mtx); mutex_unlock(&sdata->u.ibss.mtx);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(sdata->local); ieee80211_recalc_idle(sdata->local);
mutex_unlock(&local->mtx);
return 0; return 0;
} }
...@@ -497,6 +497,9 @@ struct ieee80211_sub_if_data { ...@@ -497,6 +497,9 @@ struct ieee80211_sub_if_data {
*/ */
bool ht_opmode_valid; bool ht_opmode_valid;
/* to detect idle changes */
bool old_idle;
/* Fragment table for host-based reassembly */ /* Fragment table for host-based reassembly */
struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
unsigned int fragment_next; unsigned int fragment_next;
......
...@@ -309,7 +309,9 @@ static int ieee80211_open(struct net_device *dev) ...@@ -309,7 +309,9 @@ static int ieee80211_open(struct net_device *dev)
if (sdata->flags & IEEE80211_SDATA_PROMISC) if (sdata->flags & IEEE80211_SDATA_PROMISC)
atomic_inc(&local->iff_promiscs); atomic_inc(&local->iff_promiscs);
mutex_lock(&local->mtx);
hw_reconf_flags |= __ieee80211_recalc_idle(local); hw_reconf_flags |= __ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
local->open_count++; local->open_count++;
if (hw_reconf_flags) { if (hw_reconf_flags) {
...@@ -516,7 +518,9 @@ static int ieee80211_stop(struct net_device *dev) ...@@ -516,7 +518,9 @@ static int ieee80211_stop(struct net_device *dev)
sdata->bss = NULL; sdata->bss = NULL;
mutex_lock(&local->mtx);
hw_reconf_flags |= __ieee80211_recalc_idle(local); hw_reconf_flags |= __ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
ieee80211_recalc_ps(local, -1); ieee80211_recalc_ps(local, -1);
...@@ -1199,28 +1203,61 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) ...@@ -1199,28 +1203,61 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
int count = 0; int count = 0;
bool working = false, scanning = false;
struct ieee80211_work *wk;
if (!list_empty(&local->work_list)) #ifdef CONFIG_PROVE_LOCKING
return ieee80211_idle_off(local, "working"); WARN_ON(debug_locks && !lockdep_rtnl_is_held() &&
!lockdep_is_held(&local->iflist_mtx));
if (local->scanning) #endif
return ieee80211_idle_off(local, "scanning"); lockdep_assert_held(&local->mtx);
list_for_each_entry(sdata, &local->interfaces, list) { list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata)) {
sdata->vif.bss_conf.idle = true;
continue; continue;
}
sdata->old_idle = sdata->vif.bss_conf.idle;
/* do not count disabled managed interfaces */ /* do not count disabled managed interfaces */
if (sdata->vif.type == NL80211_IFTYPE_STATION && if (sdata->vif.type == NL80211_IFTYPE_STATION &&
!sdata->u.mgd.associated) !sdata->u.mgd.associated) {
sdata->vif.bss_conf.idle = true;
continue; continue;
}
/* do not count unused IBSS interfaces */ /* do not count unused IBSS interfaces */
if (sdata->vif.type == NL80211_IFTYPE_ADHOC && if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
!sdata->u.ibss.ssid_len) !sdata->u.ibss.ssid_len) {
sdata->vif.bss_conf.idle = true;
continue; continue;
}
/* count everything else */ /* count everything else */
count++; count++;
} }
list_for_each_entry(wk, &local->work_list, list) {
working = true;
wk->sdata->vif.bss_conf.idle = false;
}
if (local->scan_sdata) {
scanning = true;
local->scan_sdata->vif.bss_conf.idle = false;
}
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->old_idle == sdata->vif.bss_conf.idle)
continue;
if (!ieee80211_sdata_running(sdata))
continue;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
}
if (working)
return ieee80211_idle_off(local, "working");
if (scanning)
return ieee80211_idle_off(local, "scanning");
if (!count) if (!count)
return ieee80211_idle_on(local); return ieee80211_idle_on(local);
else else
......
...@@ -1103,8 +1103,11 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) ...@@ -1103,8 +1103,11 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid); printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
ieee80211_set_disassoc(sdata, true); ieee80211_set_disassoc(sdata, true);
ieee80211_recalc_idle(local);
mutex_unlock(&ifmgd->mtx); mutex_unlock(&ifmgd->mtx);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
/* /*
* must be outside lock due to cfg80211, * must be outside lock due to cfg80211,
* but that's not a problem. * but that's not a problem.
...@@ -1173,7 +1176,9 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ...@@ -1173,7 +1176,9 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
sdata->name, bssid, reason_code); sdata->name, bssid, reason_code);
ieee80211_set_disassoc(sdata, true); ieee80211_set_disassoc(sdata, true);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local); ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
return RX_MGMT_CFG80211_DEAUTH; return RX_MGMT_CFG80211_DEAUTH;
} }
...@@ -1203,7 +1208,9 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, ...@@ -1203,7 +1208,9 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
sdata->name, mgmt->sa, reason_code); sdata->name, mgmt->sa, reason_code);
ieee80211_set_disassoc(sdata, true); ieee80211_set_disassoc(sdata, true);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local); ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
return RX_MGMT_CFG80211_DISASSOC; return RX_MGMT_CFG80211_DISASSOC;
} }
...@@ -1840,8 +1847,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) ...@@ -1840,8 +1847,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
" after %dms, disconnecting.\n", " after %dms, disconnecting.\n",
bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
ieee80211_set_disassoc(sdata, true); ieee80211_set_disassoc(sdata, true);
ieee80211_recalc_idle(local);
mutex_unlock(&ifmgd->mtx); mutex_unlock(&ifmgd->mtx);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
/* /*
* must be outside lock due to cfg80211, * must be outside lock due to cfg80211,
* but that's not a problem. * but that's not a problem.
...@@ -2319,7 +2328,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, ...@@ -2319,7 +2328,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
if (assoc_bss) if (assoc_bss)
sta_info_destroy_addr(sdata, bssid); sta_info_destroy_addr(sdata, bssid);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local); ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
return 0; return 0;
} }
...@@ -2357,7 +2368,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, ...@@ -2357,7 +2368,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
cookie, !req->local_state_change); cookie, !req->local_state_change);
sta_info_destroy_addr(sdata, bssid); sta_info_destroy_addr(sdata, bssid);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local); ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
return 0; return 0;
} }
......
...@@ -304,7 +304,9 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) ...@@ -304,7 +304,9 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
ieee80211_offchannel_return(local, true); ieee80211_offchannel_return(local, true);
done: done:
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local); ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
ieee80211_mlme_notify_scan_completed(local); ieee80211_mlme_notify_scan_completed(local);
ieee80211_ibss_notify_scan_completed(local); ieee80211_ibss_notify_scan_completed(local);
ieee80211_mesh_notify_scan_completed(local); ieee80211_mesh_notify_scan_completed(local);
......
...@@ -888,10 +888,10 @@ static void ieee80211_work_work(struct work_struct *work) ...@@ -888,10 +888,10 @@ static void ieee80211_work_work(struct work_struct *work)
while ((skb = skb_dequeue(&local->work_skb_queue))) while ((skb = skb_dequeue(&local->work_skb_queue)))
ieee80211_work_rx_queued_mgmt(local, skb); ieee80211_work_rx_queued_mgmt(local, skb);
ieee80211_recalc_idle(local);
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
list_for_each_entry_safe(wk, tmp, &local->work_list, list) { list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
bool started = wk->started; bool started = wk->started;
...@@ -1001,10 +1001,10 @@ static void ieee80211_work_work(struct work_struct *work) ...@@ -1001,10 +1001,10 @@ static void ieee80211_work_work(struct work_struct *work)
&local->scan_work, &local->scan_work,
round_jiffies_relative(0)); round_jiffies_relative(0));
mutex_unlock(&local->mtx);
ieee80211_recalc_idle(local); ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
list_for_each_entry_safe(wk, tmp, &free_work, list) { list_for_each_entry_safe(wk, tmp, &free_work, list) {
wk->done(wk, NULL); wk->done(wk, NULL);
list_del(&wk->list); list_del(&wk->list);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment