Merge remote-tracking branch 'net-next/master' into mac80211-next
[cascardo/linux.git] / net / mac80211 / cfg.c
index 3469bbd..1d01190 100644 (file)
@@ -2,7 +2,7 @@
  * mac80211 configuration hooks for cfg80211
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright 2013-2015  Intel Mobile Communications GmbH
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -361,66 +361,25 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_CCMP_256:
-               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
-                   !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
-                       drv_get_key_seq(sdata->local, key, &kseq);
-                       memcpy(seq, kseq.ccmp.pn, 6);
-               } else {
-                       pn64 = atomic64_read(&key->u.ccmp.tx_pn);
-                       seq[0] = pn64;
-                       seq[1] = pn64 >> 8;
-                       seq[2] = pn64 >> 16;
-                       seq[3] = pn64 >> 24;
-                       seq[4] = pn64 >> 32;
-                       seq[5] = pn64 >> 40;
-               }
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
        case WLAN_CIPHER_SUITE_BIP_CMAC_256:
-               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
-                   !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
-                       drv_get_key_seq(sdata->local, key, &kseq);
-                       memcpy(seq, kseq.aes_cmac.pn, 6);
-               } else {
-                       pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
-                       seq[0] = pn64;
-                       seq[1] = pn64 >> 8;
-                       seq[2] = pn64 >> 16;
-                       seq[3] = pn64 >> 24;
-                       seq[4] = pn64 >> 32;
-                       seq[5] = pn64 >> 40;
-               }
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), aes_cmac));
        case WLAN_CIPHER_SUITE_BIP_GMAC_128:
        case WLAN_CIPHER_SUITE_BIP_GMAC_256:
-               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
-                   !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
-                       drv_get_key_seq(sdata->local, key, &kseq);
-                       memcpy(seq, kseq.aes_gmac.pn, 6);
-               } else {
-                       pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
-                       seq[0] = pn64;
-                       seq[1] = pn64 >> 8;
-                       seq[2] = pn64 >> 16;
-                       seq[3] = pn64 >> 24;
-                       seq[4] = pn64 >> 32;
-                       seq[5] = pn64 >> 40;
-               }
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), aes_gmac));
        case WLAN_CIPHER_SUITE_GCMP:
        case WLAN_CIPHER_SUITE_GCMP_256:
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), gcmp));
+
                if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
                    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
                        drv_get_key_seq(sdata->local, key, &kseq);
-                       memcpy(seq, kseq.gcmp.pn, 6);
+                       memcpy(seq, kseq.ccmp.pn, 6);
                } else {
-                       pn64 = atomic64_read(&key->u.gcmp.tx_pn);
+                       pn64 = atomic64_read(&key->conf.tx_pn);
                        seq[0] = pn64;
                        seq[1] = pn64 >> 8;
                        seq[2] = pn64 >> 16;
@@ -1411,6 +1370,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                }
 
                sta->sdata = vlansdata;
+               ieee80211_check_fast_xmit(sta);
 
                if (sta->sta_state == IEEE80211_STA_AUTHORIZED &&
                    prev_4addr != new_4addr) {
@@ -2538,52 +2498,36 @@ static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
                                           struct ieee80211_roc_work *new_roc,
                                           struct ieee80211_roc_work *cur_roc)
 {
-       unsigned long j = jiffies;
-       unsigned long cur_roc_end = cur_roc->hw_start_time +
-                                   msecs_to_jiffies(cur_roc->duration);
-       struct ieee80211_roc_work *next_roc;
-       int new_dur;
+       unsigned long now = jiffies;
+       unsigned long remaining = cur_roc->hw_start_time +
+                                 msecs_to_jiffies(cur_roc->duration) -
+                                 now;
 
        if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun))
                return false;
 
-       if (time_after(j + IEEE80211_ROC_MIN_LEFT, cur_roc_end))
+       /* if it doesn't fit entirely, schedule a new one */
+       if (new_roc->duration > jiffies_to_msecs(remaining))
                return false;
 
        ieee80211_handle_roc_started(new_roc);
 
-       new_dur = new_roc->duration - jiffies_to_msecs(cur_roc_end - j);
+       /* add to dependents so we send the expired event properly */
+       list_add_tail(&new_roc->list, &cur_roc->dependents);
+       return true;
+}
 
-       /* cur_roc is long enough - add new_roc to the dependents list. */
-       if (new_dur <= 0) {
-               list_add_tail(&new_roc->list, &cur_roc->dependents);
-               return true;
-       }
+static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
+{
+       lockdep_assert_held(&local->mtx);
 
-       new_roc->duration = new_dur;
+       local->roc_cookie_counter++;
 
-       /*
-        * if cur_roc was already coalesced before, we might
-        * want to extend the next roc instead of adding
-        * a new one.
-        */
-       next_roc = list_entry(cur_roc->list.next,
-                             struct ieee80211_roc_work, list);
-       if (&next_roc->list != &local->roc_list &&
-           next_roc->chan == new_roc->chan &&
-           next_roc->sdata == new_roc->sdata &&
-           !WARN_ON(next_roc->started)) {
-               list_add_tail(&new_roc->list, &next_roc->dependents);
-               next_roc->duration = max(next_roc->duration,
-                                        new_roc->duration);
-               next_roc->type = max(next_roc->type, new_roc->type);
-               return true;
-       }
-
-       /* add right after cur_roc */
-       list_add(&new_roc->list, &cur_roc->list);
+       /* wow, you wrapped 64 bits ... more likely a bug */
+       if (WARN_ON(local->roc_cookie_counter == 0))
+               local->roc_cookie_counter++;
 
-       return true;
+       return local->roc_cookie_counter;
 }
 
 static int ieee80211_start_roc_work(struct ieee80211_local *local,
@@ -2623,7 +2567,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        roc->req_duration = duration;
        roc->frame = txskb;
        roc->type = type;
-       roc->mgmt_tx_cookie = (unsigned long)txskb;
        roc->sdata = sdata;
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
        INIT_LIST_HEAD(&roc->dependents);
@@ -2633,17 +2576,10 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
         * or the SKB (for mgmt TX)
         */
        if (!txskb) {
-               /* local->mtx protects this */
-               local->roc_cookie_counter++;
-               roc->cookie = local->roc_cookie_counter;
-               /* wow, you wrapped 64 bits ... more likely a bug */
-               if (WARN_ON(roc->cookie == 0)) {
-                       roc->cookie = 1;
-                       local->roc_cookie_counter++;
-               }
+               roc->cookie = ieee80211_mgmt_tx_cookie(local);
                *cookie = roc->cookie;
        } else {
-               *cookie = (unsigned long)txskb;
+               roc->mgmt_tx_cookie = *cookie;
        }
 
        /* if there's one pending or we're scanning, queue this one */
@@ -2695,17 +2631,9 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                         * In the offloaded ROC case, if it hasn't begun, add
                         * this new one to the dependent list to be handled
                         * when the master one begins. If it has begun,
-                        * check that there's still a minimum time left and
-                        * if so, start this one, transmitting the frame, but
-                        * add it to the list directly after this one with
-                        * a reduced time so we'll ask the driver to execute
-                        * it right after finishing the previous one, in the
-                        * hope that it'll also be executed right afterwards,
-                        * effectively extending the old one.
-                        * If there's no minimum time left, just add it to the
-                        * normal list.
-                        * TODO: the ROC type is ignored here, assuming that it
-                        * is better to immediately use the current ROC.
+                        * check if it fits entirely within the existing one,
+                        * in which case it will just be dependent as well.
+                        * Otherwise, schedule it by itself.
                         */
                        if (!tmp->hw_begun) {
                                list_add_tail(&roc->list, &tmp->dependents);
@@ -3324,13 +3252,43 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        return err;
 }
 
+static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
+                                             struct sk_buff *skb, u64 *cookie,
+                                             gfp_t gfp)
+{
+       unsigned long spin_flags;
+       struct sk_buff *ack_skb;
+       int id;
+
+       ack_skb = skb_copy(skb, gfp);
+       if (!ack_skb)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_irqsave(&local->ack_status_lock, spin_flags);
+       id = idr_alloc(&local->ack_status_frames, ack_skb,
+                      1, 0x10000, GFP_ATOMIC);
+       spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
+
+       if (id < 0) {
+               kfree_skb(ack_skb);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       IEEE80211_SKB_CB(skb)->ack_frame_id = id;
+
+       *cookie = ieee80211_mgmt_tx_cookie(local);
+       IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
+
+       return ack_skb;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct cfg80211_mgmt_tx_params *params,
                             u64 *cookie)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *ack_skb;
        struct sta_info *sta;
        const struct ieee80211_mgmt *mgmt = (void *)params->buf;
        bool need_offchan = false;
@@ -3442,6 +3400,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        /* Update CSA counters */
        if (sdata->vif.csa_active &&
            (sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
             sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            params->n_csa_offsets) {
                int i;
@@ -3468,8 +3427,23 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        skb->dev = sdata->dev;
 
+       if (!params->dont_wait_for_ack) {
+               /* make a copy to preserve the frame contents
+                * in case of encryption.
+                */
+               ack_skb = ieee80211_make_ack_skb(local, skb, cookie,
+                                                GFP_KERNEL);
+               if (IS_ERR(ack_skb)) {
+                       ret = PTR_ERR(ack_skb);
+                       kfree_skb(skb);
+                       goto out_unlock;
+               }
+       } else {
+               /* for cookie below */
+               ack_skb = skb;
+       }
+
        if (!need_offchan) {
-               *cookie = (unsigned long) skb;
                ieee80211_tx_skb(sdata, skb);
                ret = 0;
                goto out_unlock;
@@ -3562,7 +3536,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_qos_hdr *nullfunc;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *ack_skb;
        int size = sizeof(*nullfunc);
        __le16 fc;
        bool qos;
@@ -3570,20 +3544,24 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum ieee80211_band band;
+       int ret;
+
+       /* the lock is needed to assign the cookie later */
+       mutex_lock(&local->mtx);
 
        rcu_read_lock();
        chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
        if (WARN_ON(!chanctx_conf)) {
-               rcu_read_unlock();
-               return -EINVAL;
+               ret = -EINVAL;
+               goto unlock;
        }
        band = chanctx_conf->def.chan->band;
        sta = sta_info_get_bss(sdata, peer);
        if (sta) {
                qos = sta->sta.wme;
        } else {
-               rcu_read_unlock();
-               return -ENOLINK;
+               ret = -ENOLINK;
+               goto unlock;
        }
 
        if (qos) {
@@ -3599,8 +3577,8 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
        if (!skb) {
-               rcu_read_unlock();
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto unlock;
        }
 
        skb->dev = dev;
@@ -3626,13 +3604,23 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        if (qos)
                nullfunc->qos_ctrl = cpu_to_le16(7);
 
+       ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC);
+       if (IS_ERR(ack_skb)) {
+               kfree_skb(skb);
+               ret = PTR_ERR(ack_skb);
+               goto unlock;
+       }
+
        local_bh_disable();
        ieee80211_xmit(sdata, sta, skb);
        local_bh_enable();
+
+       ret = 0;
+unlock:
        rcu_read_unlock();
+       mutex_unlock(&local->mtx);
 
-       *cookie = (unsigned long) skb;
-       return 0;
+       return ret;
 }
 
 static int ieee80211_cfg_get_channel(struct wiphy *wiphy,