iwlwifi: mvm: Loosen the channel context/phy context coupling
authorIlan Peer <ilan.peer@intel.com>
Thu, 21 Mar 2013 08:23:52 +0000 (10:23 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 13 May 2013 16:15:18 +0000 (18:15 +0200)
In current implementation, the phy context is tightly coupled
with the channel context. This complicates the possibility of
using the same phy context for both netdev interfaces and the P2P
Device interface. To loosen this coupling:

1. Manage all the phy contexts in the mvm op mode, and only save
   the phy context id in the chanctx memory.
2. Reference count the phy contexts and free them only when they
   are not longer used (both by mac80211 and P2P Device).

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c

index e2cf6d92217b43ad73ecd3cb83f8d2459a559306..c1ed5c36bf3a495a0faf935be98ad6725404bf7c 100644 (file)
@@ -127,6 +127,17 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
 };
 #endif
 
+static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
+{
+       int i;
+
+       memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts));
+       for (i = 0; i < NUM_PHY_CTX; i++) {
+               mvm->phy_ctxts[i].id = i;
+               mvm->phy_ctxts[i].ref = 0;
+       }
+}
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
@@ -158,7 +169,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
-       hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt);
+       hw->chanctx_data_size = sizeof(u16);
 
        hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -193,6 +204,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->wiphy->n_addresses++;
        }
 
+       iwl_mvm_reset_phy_ctxts(mvm);
+
        /* we create the 802.11 header and a max-length SSID element */
        hw->wiphy->max_scan_ie_len =
                mvm->fw->ucode_capa.max_probe_length - 24 - 34;
@@ -345,8 +358,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
        iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
        spin_unlock_bh(&mvm->time_event_lock);
 
-       if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
-               mvmvif->phy_ctxt = NULL;
+       mvmvif->phy_ctxt = NULL;
 }
 
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
@@ -363,6 +375,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
                mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
                iwl_mvm_cleanup_iterator, mvm);
 
+       mvm->p2p_device_vif = NULL;
+
+       iwl_mvm_reset_phy_ctxts(mvm);
        memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
        memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
 
@@ -456,6 +471,20 @@ static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
        iwl_mvm_power_update_mode(mvm, vif);
 }
 
+static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
+{
+       u16 i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       for (i = 0; i < NUM_PHY_CTX; i++)
+               if (!mvm->phy_ctxts[i].ref)
+                       return &mvm->phy_ctxts[i];
+
+       IWL_ERR(mvm, "No available PHY context\n");
+       return NULL;
+}
+
 static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
@@ -550,7 +579,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                struct ieee80211_channel *chan;
                struct cfg80211_chan_def chandef;
 
-               mvmvif->phy_ctxt = &mvm->phy_ctxt_roc;
+               mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+               if (!mvmvif->phy_ctxt) {
+                       ret = -ENOSPC;
+                       goto out_remove_mac;
+               }
 
                /*
                 * The channel used here isn't relevant as it's
@@ -583,7 +616,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
  out_unbind:
        iwl_mvm_binding_remove_vif(mvm, vif);
  out_remove_phy:
-       iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+       iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
  out_remove_mac:
        mvmvif->phy_ctxt = NULL;
        iwl_mvm_mac_ctxt_remove(mvm, vif);
@@ -677,7 +710,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
                mvm->p2p_device_vif = NULL;
                iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
                iwl_mvm_binding_remove_vif(mvm, vif);
-               iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+               iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
                mvmvif->phy_ctxt = NULL;
        }
 
@@ -1172,6 +1205,7 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
                       enum ieee80211_roc_type type)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct cfg80211_chan_def chandef;
        int ret;
 
@@ -1186,7 +1220,7 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
        mutex_lock(&mvm->mutex);
 
        cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
-       ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc,
+       ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
                                       &chandef, 1, 1);
 
        /* Schedule the time events */
@@ -1216,15 +1250,29 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
                               struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt;
        int ret;
 
+       IWL_DEBUG_MAC80211(mvm, "Add PHY context\n");
+
        mutex_lock(&mvm->mutex);
+       phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+       if (!phy_ctxt) {
+               ret = -ENOSPC;
+               goto out;
+       }
 
-       IWL_DEBUG_MAC80211(mvm, "Add PHY context\n");
        ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def,
                                   ctx->rx_chains_static,
                                   ctx->rx_chains_dynamic);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to add PHY context\n");
+               goto out;
+       }
+
+       *phy_ctxt_id = phy_ctxt->id;
+out:
        mutex_unlock(&mvm->mutex);
        return ret;
 }
@@ -1233,10 +1281,11 @@ static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
                                   struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt);
+       iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1245,7 +1294,8 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
                                   u32 changed)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
 
        mutex_lock(&mvm->mutex);
        iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
@@ -1259,13 +1309,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
                                      struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv;
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
        mutex_lock(&mvm->mutex);
 
-       mvmvif->phy_ctxt = phyctx;
+       mvmvif->phy_ctxt = phy_ctxt;
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
index 4fc64d50dc50234807d1332090b679932db821eb..3505fcafde6635cfe190dffd48deda6a9a0cb3bc 100644 (file)
@@ -109,6 +109,7 @@ extern struct iwl_mvm_mod_params iwlmvm_mod_params;
 struct iwl_mvm_phy_ctxt {
        u16 id;
        u16 color;
+       u32 ref;
 
        /*
         * TODO: This should probably be removed. Currently here only for rate
@@ -318,7 +319,7 @@ struct iwl_mvm {
        bool prevent_power_down_d3;
 #endif
 
-       struct iwl_mvm_phy_ctxt phy_ctxt_roc;
+       struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
 
        struct list_head time_event_list;
        spinlock_t time_event_lock;
@@ -448,8 +449,10 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
 int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
                             struct cfg80211_chan_def *chandef,
                             u8 chains_static, u8 chains_dynamic);
-void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm,
-                            struct iwl_mvm_phy_ctxt *ctxt);
+void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
+                         struct iwl_mvm_phy_ctxt *ctxt);
+void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
+                           struct iwl_mvm_phy_ctxt *ctxt);
 
 /* MAC (virtual interface) programming */
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
index a28a1d1f23eb8caba29ff4501c1962a527f6e50d..f80e721eb5d0df287786e273e0764f5e50f03c8f 100644 (file)
@@ -195,21 +195,6 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
        return ret;
 }
 
-
-struct phy_ctx_used_data {
-       unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)];
-};
-
-static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw,
-                                     struct ieee80211_chanctx_conf *ctx,
-                                     void *_data)
-{
-       struct phy_ctx_used_data *data = _data;
-       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
-
-       __set_bit(phy_ctxt->id, data->used);
-}
-
 /*
  * Send a command to add a PHY context based on the current HW configuration.
  */
@@ -217,34 +202,31 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
                         struct cfg80211_chan_def *chandef,
                         u8 chains_static, u8 chains_dynamic)
 {
-       struct phy_ctx_used_data data = {
-               .used = { },
-       };
-
-       /*
-        * If this is a regular PHY context (not the ROC one)
-        * skip the ROC PHY context's ID.
-        */
-       if (ctxt != &mvm->phy_ctxt_roc)
-               __set_bit(mvm->phy_ctxt_roc.id, data.used);
+       int ret;
 
+       WARN_ON(ctxt->ref);
        lockdep_assert_held(&mvm->mutex);
-       ctxt->color++;
 
-       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
-               ieee80211_iter_chan_contexts_atomic(
-                       mvm->hw, iwl_mvm_phy_ctx_used_iter, &data);
+       ctxt->channel = chandef->chan;
+       ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+                                    chains_static, chains_dynamic,
+                                    FW_CTXT_ACTION_ADD, 0);
 
-               ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX);
-               if (WARN_ONCE(ctxt->id == NUM_PHY_CTX,
-                             "Failed to init PHY context - no free ID!\n"))
-                       return -EIO;
-       }
+       if (!ret)
+               ctxt->ref = 1;
+       return ret;
+}
 
-       ctxt->channel = chandef->chan;
-       return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
-                                     chains_static, chains_dynamic,
-                                     FW_CTXT_ACTION_ADD, 0);
+/*
+ * Update the number of references to the given PHY context. This is valid only
+ * in case the PHY context was already created, i.e., its reference count > 0.
+ */
+void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+{
+       WARN_ON(!ctxt->ref);
+       lockdep_assert_held(&mvm->mutex);
+
+       ctxt->ref++;
 }
 
 /*
@@ -269,7 +251,8 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
  * Once the command is sent, regardless of success or failure, the context is
  * marked as invalid
  */
-void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+static void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm,
+                                   struct iwl_mvm_phy_ctxt *ctxt)
 {
        struct iwl_phy_context_cmd cmd;
        int ret;
@@ -280,7 +263,19 @@ void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
        ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
                                   sizeof(struct iwl_phy_context_cmd),
                                   &cmd);
+       ctxt->channel = NULL;
        if (ret)
                IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n",
                        ctxt->id);
 }
+
+void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+{
+       lockdep_assert_held(&mvm->mutex);
+
+       ctxt->ref--;
+       if (ctxt->ref != 0)
+               return;
+
+       return iwl_mvm_phy_ctxt_remove(mvm, ctxt);
+}