x86: switch to using asm-generic for seccomp.h
[pandora-kernel.git] / net / mac80211 / sta_info.c
index aacaa1a..12971b7 100644 (file)
  * freed before they are done using it.
  */
 
+static const struct rhashtable_params sta_rht_params = {
+       .nelem_hint = 3, /* start small */
+       .head_offset = offsetof(struct sta_info, hash_node),
+       .key_offset = offsetof(struct sta_info, sta.addr),
+       .key_len = ETH_ALEN,
+       .hashfn = sta_addr_hash,
+};
+
 /* Caller must hold local->sta_mtx */
 static int sta_info_hash_del(struct ieee80211_local *local,
                             struct sta_info *sta)
 {
-       struct sta_info *s;
-
-       s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)],
-                                     lockdep_is_held(&local->sta_mtx));
-       if (!s)
-               return -ENOENT;
-       if (s == sta) {
-               rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
-                                  s->hnext);
-               return 0;
-       }
-
-       while (rcu_access_pointer(s->hnext) &&
-              rcu_access_pointer(s->hnext) != sta)
-               s = rcu_dereference_protected(s->hnext,
-                                       lockdep_is_held(&local->sta_mtx));
-       if (rcu_access_pointer(s->hnext)) {
-               rcu_assign_pointer(s->hnext, sta->hnext);
-               return 0;
-       }
-
-       return -ENOENT;
+       return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node,
+                                     sta_rht_params);
 }
 
 static void __cleanup_single_sta(struct sta_info *sta)
@@ -118,6 +106,16 @@ static void __cleanup_single_sta(struct sta_info *sta)
                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]);
+                       int n = skb_queue_len(&txqi->queue);
+
+                       ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+                       atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]);
+               }
+       }
+
        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]);
@@ -159,18 +157,8 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
                              const u8 *addr)
 {
        struct ieee80211_local *local = sdata->local;
-       struct sta_info *sta;
 
-       sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
-                                   lockdep_is_held(&local->sta_mtx));
-       while (sta) {
-               if (sta->sdata == sdata &&
-                   ether_addr_equal(sta->sta.addr, addr))
-                       break;
-               sta = rcu_dereference_check(sta->hnext,
-                                           lockdep_is_held(&local->sta_mtx));
-       }
-       return sta;
+       return rhashtable_lookup_fast(&local->sta_hash, addr, sta_rht_params);
 }
 
 /*
@@ -182,18 +170,24 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
+       struct rhash_head *tmp;
+       const struct bucket_table *tbl;
 
-       sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
-                                   lockdep_is_held(&local->sta_mtx));
-       while (sta) {
-               if ((sta->sdata == sdata ||
-                    (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
-                   ether_addr_equal(sta->sta.addr, addr))
-                       break;
-               sta = rcu_dereference_check(sta->hnext,
-                                           lockdep_is_held(&local->sta_mtx));
+       rcu_read_lock();
+       tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+       for_each_sta_info(local, tbl, 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;
+               }
        }
-       return sta;
+       rcu_read_unlock();
+       return NULL;
 }
 
 struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
@@ -234,6 +228,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *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));
        kfree(sta);
 }
@@ -242,9 +238,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 static void sta_info_hash_add(struct ieee80211_local *local,
                              struct sta_info *sta)
 {
-       lockdep_assert_held(&local->sta_mtx);
-       sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];
-       rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
+       rhashtable_insert_fast(&local->sta_hash, &sta->hash_node,
+                              sta_rht_params);
 }
 
 static void sta_deliver_ps_frames(struct work_struct *wk)
@@ -285,11 +280,12 @@ 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;
        struct timespec uptime;
        int i;
 
-       sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
+       sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
        if (!sta)
                return NULL;
 
@@ -321,11 +317,25 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
                ewma_init(&sta->chain_signal_avg[i], 1024, 8);
 
-       if (sta_prepare_rate_control(local, sta, gfp)) {
-               kfree(sta);
-               return NULL;
+       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_init_tx_queue(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
@@ -346,7 +356,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        if (sdata->vif.type == NL80211_IFTYPE_AP ||
            sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
                struct ieee80211_supported_band *sband =
-                       local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+                       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;
                /*
@@ -371,6 +381,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        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:
+       kfree(sta);
+       return NULL;
 }
 
 static int sta_info_insert_check(struct sta_info *sta)
@@ -640,6 +657,8 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
 
                indicate_tim |=
                        sta->driver_buffered_tids & tids;
+               indicate_tim |=
+                       sta->txq_buffered_tids & tids;
        }
 
  done:
@@ -948,19 +967,32 @@ static void sta_info_cleanup(unsigned long data)
                  round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL));
 }
 
-void sta_info_init(struct ieee80211_local *local)
+u32 sta_addr_hash(const void *key, u32 length, u32 seed)
+{
+       return jhash(key, ETH_ALEN, seed);
+}
+
+int sta_info_init(struct ieee80211_local *local)
 {
+       int err;
+
+       err = rhashtable_init(&local->sta_hash, &sta_rht_params);
+       if (err)
+               return err;
+
        spin_lock_init(&local->tim_lock);
        mutex_init(&local->sta_mtx);
        INIT_LIST_HEAD(&local->sta_list);
 
        setup_timer(&local->sta_cleanup, sta_info_cleanup,
                    (unsigned long)local);
+       return 0;
 }
 
 void sta_info_stop(struct ieee80211_local *local)
 {
        del_timer_sync(&local->sta_cleanup);
+       rhashtable_destroy(&local->sta_hash);
 }
 
 
@@ -1024,16 +1056,21 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
 }
 
 struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
-                                              const u8 *addr,
-                                              const u8 *localaddr)
+                                                  const u8 *addr,
+                                                  const u8 *localaddr)
 {
-       struct sta_info *sta, *nxt;
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct sta_info *sta;
+       struct rhash_head *tmp;
+       const struct bucket_table *tbl;
+
+       tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
 
        /*
         * Just return a random station if localaddr is NULL
         * ... first in list.
         */
-       for_each_sta_info(hw_to_local(hw), addr, sta, nxt) {
+       for_each_sta_info(local, tbl, addr, sta, tmp) {
                if (localaddr &&
                    !ether_addr_equal(sta->sdata->vif.addr, localaddr))
                        continue;
@@ -1071,7 +1108,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
        struct sk_buff_head pending;
-       int filtered = 0, buffered = 0, ac;
+       int filtered = 0, buffered = 0, ac, i;
        unsigned long flags;
        struct ps_data *ps;
 
@@ -1090,10 +1127,22 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
        BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
        sta->driver_buffered_tids = 0;
+       sta->txq_buffered_tids = 0;
 
        if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
                drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
 
+       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]);
+
+                       if (!skb_queue_len(&txqi->queue))
+                               continue;
+
+                       drv_wake_tx_queue(local, txqi);
+               }
+       }
+
        skb_queue_head_init(&pending);
 
        /* sync with ieee80211_tx_h_unicast_ps_buf */
@@ -1275,8 +1324,10 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                /* if we already have frames from software, then we can't also
                 * release from hardware queues
                 */
-               if (skb_queue_empty(&frames))
+               if (skb_queue_empty(&frames)) {
                        driver_release_tids |= sta->driver_buffered_tids & tids;
+                       driver_release_tids |= sta->txq_buffered_tids & tids;
+               }
 
                if (driver_release_tids) {
                        /* If the driver has data on more than one TID then
@@ -1447,6 +1498,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 
                sta_info_recalc_tim(sta);
        } else {
+               unsigned long tids = sta->txq_buffered_tids & driver_release_tids;
+               int tid;
+
                /*
                 * We need to release a frame that is buffered somewhere in the
                 * driver ... it'll have to handle that.
@@ -1466,8 +1520,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                 * that the TID(s) became empty before returning here from the
                 * release function.
                 * Either way, however, when the driver tells us that the TID(s)
-                * became empty we'll do the TIM recalculation.
+                * became empty or we find that a txq became empty, we'll do the
+                * TIM recalculation.
                 */
+
+               if (!sta->sta.txq[0])
+                       return;
+
+               for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+                       struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+                       if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue))
+                               continue;
+
+                       sta_info_recalc_tim(sta);
+                       break;
+               }
        }
 }