iwlwifi: mvm: implement driver RX queues sync command
authorSara Sharon <sara.sharon@intel.com>
Thu, 18 Feb 2016 12:21:12 +0000 (14:21 +0200)
committerLuca Coelho <luciano.coelho@intel.com>
Tue, 10 May 2016 19:14:39 +0000 (22:14 +0300)
mac80211 will call the driver whenever there is a race between
RSS queues and control path that requires a processing of all
pending frames in RSS queues.
Implement that by utilizing the internal notification mechanism:
queue a message to all queues. When the message is received on
a queue it decrements the atomic counter. This guarantees that
all pending frames in the RX queue were processed since the message
is in order inside the queue.

Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/ops.c
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c

index 4c086d04809765be7fbffe81ad9361ec5a854241..ade170bd8683fa7e3d66a0ee3ef58875aa78e3b0 100644 (file)
@@ -437,9 +437,11 @@ struct iwl_rxq_sync_notification {
 /**
 * Internal message identifier
 *
+* @IWL_MVM_RXQ_SYNC: sync RSS queues
 * @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA
 */
 enum iwl_mvm_rxq_notif_type {
+       IWL_MVM_RXQ_SYNC,
        IWL_MVM_RXQ_NOTIF_DEL_BA,
 };
 
@@ -448,10 +450,12 @@ enum iwl_mvm_rxq_notif_type {
 * in &iwl_rxq_sync_cmd. Should be DWORD aligned.
 *
 * @type: value from &iwl_mvm_rxq_notif_type
+* @cookie: internal cookie to identify old notifications
 * @data: payload
 */
 struct iwl_mvm_internal_rxq_notif {
        u32 type;
+       u32 cookie;
        u8 data[];
 } __packed;
 
index 5ace468070cbe4721ecb2fd3a3c2a0e9bc48e8c6..d6ad77a2c767115b4aa3fc702b47b71a27acb0b5 100644 (file)
@@ -4037,6 +4037,47 @@ static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
        }
 }
 
+static void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_internal_rxq_notif data = {
+               .type = IWL_MVM_RXQ_SYNC,
+               .cookie = mvm->queue_sync_cookie,
+       };
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(notif_waitq);
+       u32 qmask = BIT(mvm->trans->num_rx_queues) - 1;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (!iwl_mvm_has_new_rx_api(mvm))
+               return;
+
+       atomic_set(&mvm->queue_sync_counter, mvm->trans->num_rx_queues);
+
+       ret = iwl_mvm_notify_rx_queue(mvm, qmask, (u8 *)&data, sizeof(data));
+       if (ret) {
+               IWL_ERR(mvm, "Failed to trigger RX queues sync (%d)\n", ret);
+               goto out;
+       }
+       ret = wait_event_timeout(notif_waitq,
+                                atomic_read(&mvm->queue_sync_counter) == 0,
+                                HZ);
+       WARN_ON_ONCE(!ret);
+
+out:
+       atomic_set(&mvm->queue_sync_counter, 0);
+       mvm->queue_sync_cookie++;
+}
+
+static void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_sync_rx_queues_internal(mvm);
+       mutex_unlock(&mvm->mutex);
+}
+
 const struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
@@ -4093,6 +4134,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
 
        .event_callback = iwl_mvm_mac_event_callback,
 
+       .sync_rx_queues = iwl_mvm_sync_rx_queues,
+
        CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
 
 #ifdef CONFIG_PM_SLEEP
index 85800ba0c667d39969aa7efab620fb13fd67d1db..c7c8a6205eaae78ebe379701cf3dc04df4febaab 100644 (file)
@@ -633,6 +633,8 @@ struct iwl_mvm {
 
        unsigned long status;
 
+       u32 queue_sync_cookie;
+       atomic_t queue_sync_counter;
        /*
         * for beacon filtering -
         * currently only one interface can be supported
index 8bfb8e06a90c4dd24765ca56aaf7a16abaa0f25b..7c6a5984105f2cea68fcd7ed25d2b23382fd4216 100644 (file)
@@ -586,6 +586,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        skb_queue_head_init(&mvm->d0i3_tx);
        init_waitqueue_head(&mvm->d0i3_exit_waitq);
 
+       atomic_set(&mvm->queue_sync_counter, 0);
+
        SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
 
        /*
index 651604d18a3252af432d396bff7e923bd1439c2c..b4d9c42d411afd53a869e5f393f7f1a703d912a5 100644 (file)
@@ -406,6 +406,13 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        internal_notif = (void *)notif->payload;
 
        switch (internal_notif->type) {
+       case IWL_MVM_RXQ_SYNC:
+               if (mvm->queue_sync_cookie == internal_notif->cookie)
+                       atomic_dec(&mvm->queue_sync_counter);
+               else
+                       WARN_ONCE(1,
+                                 "Received expired RX queue sync message\n");
+               break;
        case IWL_MVM_RXQ_NOTIF_DEL_BA:
                /* TODO */
                break;