Release 4.11 net/mac80211/sta_info.c
/*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 - 2016 Intel Deutschland GmbH
*
* 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/module.h>
#include <linux/init.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/timer.h>
#include <linux/rtnetlink.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
#include "rate.h"
#include "sta_info.h"
#include "debugfs_sta.h"
#include "mesh.h"
#include "wme.h"
/**
* DOC: STA information lifetime rules
*
* STA info structures (&struct sta_info) are managed in a hash table
* for faster lookup and a list for iteration. They are managed using
* RCU, i.e. access to the list and hash table is protected by RCU.
*
* Upon allocating a STA info structure with sta_info_alloc(), the caller
* owns that structure. It must then insert it into the hash table using
* either sta_info_insert() or sta_info_insert_rcu(); only in the latter
* case (which acquires an rcu read section but must not be called from
* within one) will the pointer still be valid after the call. Note that
* the caller may not do much with the STA info before inserting it, in
* particular, it may not start any mesh peer link management or add
* encryption keys.
*
* When the insertion fails (sta_info_insert()) returns non-zero), the
* structure will have been freed by sta_info_insert()!
*
* Station entries are added by mac80211 when you establish a link with a
* peer. This means different things for the different type of interfaces
* we support. For a regular station this mean we add the AP sta when we
* receive an association response from the AP. For IBSS this occurs when
* get to know about a peer on the same IBSS. For WDS we add the sta for
* the peer immediately upon device open. When using AP mode we add stations
* for each respective station upon request from userspace through nl80211.
*
* In order to remove a STA info structure, various sta_info_destroy_*()
* calls are available.
*
* There is no concept of ownership on a STA entry, each structure is
* owned by the global hash table/list until it is removed. All users of
* the structure need to be RCU protected so that the structure won't be
* freed before they are done using it.
*/
static const struct rhashtable_params sta_rht_params = {
.nelem_hint = 3, /* start small */
.automatic_shrinking = true,
.head_offset = offsetof(struct sta_info, hash_node),
.key_offset = offsetof(struct sta_info, addr),
.key_len = ETH_ALEN,
.max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE,
};
/* Caller must hold local->sta_mtx */
static int sta_info_hash_del(struct ieee80211_local *local,
struct sta_info *sta)
{
return rhltable_remove(&local->sta_hash, &sta->hash_node,
sta_rht_params);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jiri Benc | 20 | 62.50% | 1 | 16.67% |
Johannes Berg | 8 | 25.00% | 3 | 50.00% |
Michael Wu | 3 | 9.38% | 1 | 16.67% |
Herbert Xu | 1 | 3.12% | 1 | 16.67% |
Total | 32 | 100.00% | 6 | 100.00% |
static void __cleanup_single_sta(struct sta_info *sta)
{
int ac, i;
struct tid_ampdu_tx *tid_tx;
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct fq *fq = &local->fq;
struct ps_data *ps;
if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
ps = &sdata->bss->ps;
else if (ieee80211_vif_is_mesh(&sdata->vif))
ps = &sdata->u.mesh.ps;
else
return;
clear_sta_flag(sta, WLAN_STA_PS_STA);
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
atomic_dec(&ps->num_sta_ps);
}
if (sta->sta.txq[0]) {
for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
spin_lock_bh(&fq->lock);
ieee80211_txq_purge(local, txqi);
spin_unlock_bh(&fq->lock);
}
}
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]);
ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]);
}
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_sta_cleanup(sta);
cancel_work_sync(&sta->drv_deliver_wk);
/*
* Destroy aggregation state here. It would be nice to wait for the
* driver to finish aggregation stop and then clean up, but for now
* drivers have to handle aggregation stop being requested, followed
* directly by station destruction.
*/
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
kfree(sta->ampdu_mlme.tid_start_tx[i]);
tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
if (!tid_tx)
continue;
ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);
kfree(tid_tx);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eliad Peller | 182 | 46.79% | 1 | 7.69% |
Felix Fietkau | 85 | 21.85% | 2 | 15.38% |
Marco Porsch | 57 | 14.65% | 2 | 15.38% |
Johannes Berg | 46 | 11.83% | 6 | 46.15% |
Michal Kazior | 18 | 4.63% | 1 | 7.69% |
Thomas Pedersen | 1 | 0.26% | 1 | 7.69% |
Total | 389 | 100.00% | 13 | 100.00% |
static void cleanup_single_sta(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
__cleanup_single_sta(sta);
sta_info_free(local, sta);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 33 | 80.49% | 1 | 50.00% |
Eliad Peller | 8 | 19.51% | 1 | 50.00% |
Total | 41 | 100.00% | 2 | 100.00% |
struct rhlist_head *sta_info_hash_lookup(struct ieee80211_local *local,
const u8 *addr)
{
return rhltable_lookup(&local->sta_hash, addr, sta_rht_params);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Herbert Xu | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
/* protected by RCU */
struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
const u8 *addr)
{
struct ieee80211_local *local = sdata->local;
struct rhlist_head *tmp;
struct sta_info *sta;
rcu_read_lock();
for_each_sta_info(local, addr, sta, tmp) {
if (sta->sdata == sdata) {
rcu_read_unlock();
/* this is safe as the caller must already hold
* another rcu read section or the mutex
*/
return sta;
}
}
rcu_read_unlock();
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 54 | 72.97% | 4 | 57.14% |
Jiri Benc | 14 | 18.92% | 1 | 14.29% |
Herbert Xu | 3 | 4.05% | 1 | 14.29% |
Felix Fietkau | 3 | 4.05% | 1 | 14.29% |
Total | 74 | 100.00% | 7 | 100.00% |
/*
* Get sta info either from the specified interface
* or from one of its vlans
*/
struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
const u8 *addr)
{
struct ieee80211_local *local = sdata->local;
struct rhlist_head *tmp;
struct sta_info *sta;
rcu_read_lock();
for_each_sta_info(local, addr, sta, tmp) {
if (sta->sdata == sdata ||
(sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
rcu_read_unlock();
/* this is safe as the caller must already hold
* another rcu read section or the mutex
*/
return sta;
}
}
rcu_read_unlock();
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Felix Fietkau | 46 | 50.00% | 1 | 16.67% |
Johannes Berg | 42 | 45.65% | 3 | 50.00% |
Herbert Xu | 3 | 3.26% | 1 | 16.67% |
Jiri Benc | 1 | 1.09% | 1 | 16.67% |
Total | 92 | 100.00% | 6 | 100.00% |
struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
int idx)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int i = 0;
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata)
continue;
if (i < idx) {
++i;
continue;
}
return sta;
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis Carlos Cobo Rus | 58 | 81.69% | 2 | 50.00% |
Johannes Berg | 13 | 18.31% | 2 | 50.00% |
Total | 71 | 100.00% | 4 | 100.00% |
/**
* sta_info_free - free STA
*
* @local: pointer to the global information
* @sta: STA info to free
*
* This function must undo everything done by sta_info_alloc()
* that may happen before sta_info_insert(). It may only be
* called when sta_info_insert() has not been attempted (and
* if that fails, the station is freed anyway.)
*/
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
{
if (sta->rate_ctrl)
rate_control_free_sta(sta);
sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
if (sta->sta.txq[0])
kfree(to_txq_info(sta->sta.txq[0]));
kfree(rcu_dereference_raw(sta->sta.rates));
#ifdef CONFIG_MAC80211_MESH
kfree(sta->mesh);
#endif
free_percpu(sta->pcpu_rx_stats);
kfree(sta);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 63 | 61.17% | 7 | 70.00% |
Felix Fietkau | 38 | 36.89% | 2 | 20.00% |
Joe Perches | 2 | 1.94% | 1 | 10.00% |
Total | 103 | 100.00% | 10 | 100.00% |
/* Caller must hold local->sta_mtx */
static int sta_info_hash_add(struct ieee80211_local *local,
struct sta_info *sta)
{
return rhltable_insert(&local->sta_hash, &sta->hash_node,
sta_rht_params);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 24 | 75.00% | 6 | 75.00% |
Jiri Benc | 7 | 21.88% | 1 | 12.50% |
Herbert Xu | 1 | 3.12% | 1 | 12.50% |
Total | 32 | 100.00% | 8 | 100.00% |
static void sta_deliver_ps_frames(struct work_struct *wk)
{
struct sta_info *sta;
sta = container_of(wk, struct sta_info, drv_deliver_wk);
if (sta->dead)
return;
local_bh_disable();
if (!test_sta_flag(sta, WLAN_STA_PS_STA))
ieee80211_sta_ps_deliver_wakeup(sta);
else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL))
ieee80211_sta_ps_deliver_poll_response(sta);
else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD))
ieee80211_sta_ps_deliver_uapsd(sta);
local_bh_enable();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 77 | 89.53% | 6 | 85.71% |
Jiri Benc | 9 | 10.47% | 1 | 14.29% |
Total | 86 | 100.00% | 7 | 100.00% |
static int sta_prepare_rate_control(struct ieee80211_local *local,
struct sta_info *sta, gfp_t gfp)
{
if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))
return 0;
sta->rate_ctrl = local->rate_ctrl;
sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
sta, gfp);
if (!sta->rate_ctrl_priv)
return -ENOMEM;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 60 | 84.51% | 2 | 40.00% |
Ron Rindjunsky | 7 | 9.86% | 2 | 40.00% |
Jiri Benc | 4 | 5.63% | 1 | 20.00% |
Total | 71 | 100.00% | 5 | 100.00% |
struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
const u8 *addr, gfp_t gfp)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_hw *hw = &local->hw;
struct sta_info *sta;
int i;
sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
if (!sta)
return NULL;
if (ieee80211_hw_check(hw, USES_RSS)) {
sta->pcpu_rx_stats =
alloc_percpu(struct ieee80211_sta_rx_stats);
if (!sta->pcpu_rx_stats)
goto free;
}
spin_lock_init(&sta->lock);
spin_lock_init(&sta->ps_lock);
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx);
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&sdata->vif)) {
sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
if (!sta->mesh)
goto free;
spin_lock_init(&sta->mesh->plink_lock);
if (ieee80211_vif_is_mesh(&sdata->vif) &&
!sdata->u.mesh.user_mpm)
init_timer(&sta->mesh->plink_timer);
sta->mesh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
}
#endif
memcpy(sta->addr, addr, ETH_ALEN);
memcpy(sta->sta.addr, addr, ETH_ALEN);
sta->sta.max_rx_aggregation_subframes =
local->hw.max_rx_aggregation_subframes;
sta->local = local;
sta->sdata = sdata;
sta->rx_stats.last_rx = jiffies;
u64_stats_init(&sta->rx_stats.syncp);
sta->sta_state = IEEE80211_STA_NONE;
/* Mark TID as unreserved */
sta->reserved_tid = IEEE80211_TID_UNRESERVED;
sta->last_connected = ktime_get_seconds();
ewma_signal_init(&sta->rx_stats_avg.signal);
for (i = 0; i < ARRAY_SIZE(sta->rx_stats_avg.chain_signal); i++)
ewma_signal_init(&sta->rx_stats_avg.chain_signal[i]);
if (local->ops->wake_tx_queue) {
void *txq_data;
int size = sizeof(struct txq_info) +
ALIGN(hw->txq_data_size, sizeof(void *));
txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
if (!txq_data)
goto free;
for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
struct txq_info *txq = txq_data + i * size;
ieee80211_txq_init(sdata, sta, txq, i);
}
}
if (sta_prepare_rate_control(local, sta, gfp))
goto free_txq;
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/*
* timer_to_tid must be initialized with identity mapping
* to enable session_timer's data differentiation. See
* sta_rx_agg_session_timer_expired for usage.
*/
sta->timer_to_tid[i] = i;
}
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
skb_queue_head_init(&sta->ps_tx_buf[i]);
skb_queue_head_init(&sta->tx_filtered[i]);
}
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
sta->sta.smps_mode = IEEE80211_SMPS_OFF;
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
struct ieee80211_supported_band *sband =
hw->wiphy->bands[ieee80211_get_sdata_band(sdata)];
u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
IEEE80211_HT_CAP_SM_PS_SHIFT;
/*
* Assume that hostapd advertises our caps in the beacon and
* this is the known_smps_mode for a station that just assciated
*/
switch (smps) {
case WLAN_HT_SMPS_CONTROL_DISABLED:
sta->known_smps_mode = IEEE80211_SMPS_OFF;
break;
case WLAN_HT_SMPS_CONTROL_STATIC:
sta->known_smps_mode = IEEE80211_SMPS_STATIC;
break;
case WLAN_HT_SMPS_CONTROL_DYNAMIC:
sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC;
break;
default:
WARN_ON(1);
}
}
sta->sta.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
return sta;
free_txq:
if (sta->sta.txq[0])
kfree(to_txq_info(sta->sta.txq[0]));
free:
#ifdef CONFIG_MAC80211_MESH
kfree(sta->mesh);
#endif
kfree(sta);
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 360 | 47.68% | 25 | 54.35% |
Felix Fietkau | 194 | 25.70% | 4 | 8.70% |
Emmanuel Grumbach | 102 | 13.51% | 2 | 4.35% |
Thomas Pedersen | 38 | 5.03% | 2 | 4.35% |
Maxim Altshul | 12 | 1.59% | 1 | 2.17% |
Ron Rindjunsky | 11 | 1.46% | 3 | 6.52% |
Bob Copeland | 8 | 1.06% | 1 | 2.17% |
Liad Kaufman | 7 | 0.93% | 1 | 2.17% |
Bruno Randolf | 6 | 0.79% | 1 | 2.17% |
Mohammed Shafi Shajakhan | 5 | 0.66% | 1 | 2.17% |
Matti Gottlieb | 5 | 0.66% | 1 | 2.17% |
Jiri Benc | 3 | 0.40% | 1 | 2.17% |
Arnd Bergmann | 2 | 0.26% | 1 | 2.17% |
Alexey Dobriyan | 1 | 0.13% | 1 | 2.17% |
Michal Kazior | 1 | 0.13% | 1 | 2.17% |
Total | 755 | 100.00% | 46 | 100.00% |
static int sta_info_insert_check(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
/*
* Can't be a WARN_ON because it can be triggered through a race:
* something inserts a STA (on one CPU) without holding the RTNL
* and another CPU turns off the net device.
*/
if (unlikely(!ieee80211_sdata_running(sdata)))
return -ENETDOWN;
if (WARN_ON(ether_addr_equal(sta->sta.addr, sdata->vif.addr) ||
is_multicast_ether_addr(sta->sta.addr)))
return -EINVAL;
/* The RCU read lock is required by rhashtable due to
* asynchronous resize/rehash. We also require the mutex
* for correctness.
*/
rcu_read_lock();
lockdep_assert_held(&sdata->local->sta_mtx);
if (ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR) &&
ieee80211_find_sta_by_ifaddr(&sdata->local->hw, sta->addr, NULL)) {
rcu_read_unlock();
return -ENOTUNIQ;
}
rcu_read_unlock();
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 126 | 98.44% | 3 | 60.00% |
Herbert Xu | 1 | 0.78% | 1 | 20.00% |
Joe Perches | 1 | 0.78% | 1 | 20.00% |
Total | 128 | 100.00% | 5 | 100.00% |
static int sta_info_insert_drv_state(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct sta_info *sta)
{
enum ieee80211_sta_state state;
int err = 0;
for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) {
err = drv_sta_state(local, sdata, sta, state, state + 1);
if (err)
break;
}
if (!err) {
/*
* Drivers using legacy sta_add/sta_remove callbacks only
* get uploaded set to true after sta_add is called.
*/
if (!local->ops->sta_add)
sta->uploaded = true;
return 0;
}
if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
sdata_info(sdata,
"failed to move IBSS STA %pM to state %d (%d) - keeping it anyway\n",
sta->sta.addr, state + 1, err);
err = 0;
}
/* unwind on error */
for (; state > IEEE80211_STA_NOTEXIST; state--)
WARN_ON(drv_sta_state(local, sdata, sta, state, state - 1));
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 162 | 100.00% | 3 | 100.00% |
Total | 162 | 100.00% | 3 | 100.00% |
/*
* should be called with sta_mtx locked
* this function replaces the mutex lock
* with a RCU lock
*/
static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct station_info *sinfo = NULL;
int err = 0;
lockdep_assert_held(&local->sta_mtx);
/* check if STA exists already */
if (sta_info_get_bss(sdata, sta->sta.addr)) {
err = -EEXIST;
goto out_err;
}
sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
if (!sinfo) {
err = -ENOMEM;
goto out_err;
}
local->num_sta++;
local->sta_generation++;
smp_mb();
/* simplify things and don't accept BA sessions yet */
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
/* make the station visible */
err = sta_info_hash_add(local, sta);
if (err)
goto out_drop_sta;
list_add_tail_rcu(&sta->list, &local->sta_list);
/* notify driver */
err = sta_info_insert_drv_state(local, sdata, sta);
if (err)
goto out_remove;
set_sta_flag(sta, WLAN_STA_INSERTED);
/* accept BA sessions now */
clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
sinfo->generation = local->sta_generation;
cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
kfree(sinfo);
sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr);
/* move reference to rcu-protected */
rcu_read_lock();
mutex_unlock(&local->sta_mtx);
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_accept_plinks_update(sdata);
return 0;
out_remove:
sta_info_hash_del(local, sta);
list_del_rcu(&sta->list);
out_drop_sta:
local->num_sta--;
synchronize_net();
__cleanup_single_sta(sta);
out_err:
mutex_unlock(&local->sta_mtx);
kfree(sinfo);
rcu_read_lock();
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 236 | 72.39% | 14 | 60.87% |
Arnd Bergmann | 35 | 10.74% | 1 | 4.35% |
Koen Vandeputte | 26 | 7.98% | 1 | 4.35% |
Jiri Benc | 18 | 5.52% | 2 | 8.70% |
Sudip Mukherjee | 5 | 1.53% | 1 | 4.35% |
Jouni Malinen | 3 | 0.92% | 1 | 4.35% |
Guy Eilam | 1 | 0.31% | 1 | 4.35% |
Joe Perches | 1 | 0.31% | 1 | 4.35% |
Arik Nemtsov | 1 | 0.31% | 1 | 4.35% |
Total | 326 | 100.00% | 23 | 100.00% |
int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
{
struct ieee80211_local *local = sta->local;
int err;
might_sleep();
mutex_lock(&local->sta_mtx);
err = sta_info_insert_check(sta);
if (err) {
mutex_unlock(&local->sta_mtx);
rcu_read_lock();
goto out_free;
}
err = sta_info_insert_finish(sta);
if (err)
goto out_free;
return 0;
out_free:
sta_info_free(local, sta);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Guy Eilam | 60 | 64.52% | 1 | 16.67% |
Johannes Berg | 32 | 34.41% | 4 | 66.67% |
Jiri Benc | 1 | 1.08% | 1 | 16.67% |
Total | 93 | 100.00% | 6 | 100.00% |
int sta_info_insert(struct sta_info *sta)
{
int err = sta_info_insert_rcu(sta);
rcu_read_unlock();
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
static inline void __bss_tim_set(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __set_bit() format.
*/
tim[id / 8] |= (1 << (id % 8));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 27 | 84.38% | 1 | 50.00% |
Marco Porsch | 5 | 15.62% | 1 | 50.00% |
Total | 32 | 100.00% | 2 | 100.00% |
static inline void __bss_tim_clear(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __clear_bit() format.
*/
tim[id / 8] &= ~(1 << (id % 8));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 28 | 84.85% | 1 | 50.00% |
Marco Porsch | 5 | 15.15% | 1 | 50.00% |
Total | 33 | 100.00% | 2 | 100.00% |
static inline bool __bss_tim_get(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the test_bit() format.
*/
return tim[id / 8] & (1 << (id % 8));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ilan Peer | 33 | 100.00% | 1 | 100.00% |
Total | 33 | 100.00% | 1 | 100.00% |
static unsigned long ieee80211_tids_for_ac(int ac)
{
/* If we ever support TIDs > 7, this obviously needs to be adjusted */
switch (ac) {
case IEEE80211_AC_VO:
return BIT(6) | BIT(7);
case IEEE80211_AC_VI:
return BIT(4) | BIT(5);
case IEEE80211_AC_BE:
return BIT(0) | BIT(3);
case IEEE80211_AC_BK:
return BIT(1) | BIT(2);
default:
WARN_ON(1);
return 0;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 82 | 100.00% | 2 | 100.00% |
Total | 82 | 100.00% | 2 | 100.00% |
static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
{
struct ieee80211_local *local = sta->local;
struct ps_data *ps;
bool indicate_tim = false;
u8 ignore_for_tim = sta->sta.uapsd_queues;
int ac;
u16 id = sta->sta.aid;
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
if (WARN_ON_ONCE(!sta->sdata->bss))
return;
ps = &sta->sdata->bss->ps;
#ifdef CONFIG_MAC80211_MESH
} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
ps = &sta->sdata->u.mesh.ps;
#endif
} else {
return;
}
/* No need to do anything if the driver does all */
if (ieee80211_hw_check(&local->hw, AP_LINK_PS) && !local->ops->set_tim)
return;
if (sta->dead)
goto done;
/*
* If all ACs are delivery-enabled then we should build
* the TIM bit for all ACs anyway; if only some are then
* we ignore those and build the TIM bit using only the
* non-enabled ones.
*/
if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1)
ignore_for_tim = 0;
if (ignore_pending)
ignore_for_tim = BIT(IEEE80211_NUM_ACS) - 1