Message ID | iwlwifi.20200403112332.0f49448c133d.I17fd308bc4a9491859c9b112f4eb5d2c3fc18d7d@changeid (mailing list archive) |
---|---|
State | Accepted |
Commit | fbb1461ad1d6eacca9beb69a2f3ce1b5398d399b |
Delegated to: | Kalle Valo |
Headers | show |
Series | iwlwifi: fixes intended for v5.7 2020-04-03 | expand |
I was looking into this as part of https://bugzilla.kernel.org/show_bug.cgi?id=206971 and had a similar fix in flight. My concern was that queue_owner being used outside of the RCU might be an issue as now you have no guaranty that the eventual use of sta->txq[tid] in iwl_mvm_free_inactive_queue() is going to be valid. The only way to work around this is instead of storing queue_owner, store mvmtxq = iwl_mvm_txq_from_tid(sta, i), then adjust iwl_mvm_free_inactive_queue(), iwl_mvm_disable_txq() and whatnot to take struct iwl_mvm_txq * instead of struct ieee80211_sta *. If you open the bug you will see the latest version of my work as the attached patch. I am not an RCU expert so I am curious to hear your thoughts. On Fri, Apr 3, 2020 at 4:31 AM Luca Coelho <luca@coelho.fi> wrote: > > From: Johannes Berg <johannes.berg@intel.com> > > iwl_mvm_free_inactive_queue() will sleep in synchronize_net() under > some circumstances, so don't call it under RCU. There doesn't appear > to be a need for RCU protection around this particular call. > > Cc: stable@vger.kernel.org # v5.4+ > Signed-off-by: Johannes Berg <johannes.berg@intel.com> > Signed-off-by: Luca Coelho <luciano.coelho@intel.com> > --- > drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 8 +++----- > 1 file changed, 3 insertions(+), 5 deletions(-) > > diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c > index 56ae72debb96..9ca433fdf634 100644 > --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c > +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c > @@ -1184,17 +1184,15 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta) > for_each_set_bit(i, &changetid_queues, IWL_MAX_HW_QUEUES) > iwl_mvm_change_queue_tid(mvm, i); > > + rcu_read_unlock(); > + > if (free_queue >= 0 && alloc_for_sta != IWL_MVM_INVALID_STA) { > ret = iwl_mvm_free_inactive_queue(mvm, free_queue, queue_owner, > alloc_for_sta); > - if (ret) { > - rcu_read_unlock(); > + if (ret) > return ret; > - } > } > > - rcu_read_unlock(); > - > return free_queue; > } > > -- > 2.25.1 >
On Fri, 2020-04-03 at 10:28 -0400, Mark Asselstine wrote: > I was looking into this as part of > https://bugzilla.kernel.org/show_bug.cgi?id=206971 and had a similar > fix in flight. My concern was that queue_owner being used outside of > the RCU might be an issue as now you have no guaranty that the > eventual use of sta->txq[tid] in iwl_mvm_free_inactive_queue() is > going to be valid. The only way to work around this is instead of > storing queue_owner, store mvmtxq = iwl_mvm_txq_from_tid(sta, i), then > adjust iwl_mvm_free_inactive_queue(), iwl_mvm_disable_txq() and > whatnot to take struct iwl_mvm_txq * instead of struct ieee80211_sta > *. If you open the bug you will see the latest version of my work as > the attached patch. I am not an RCU expert so I am curious to hear > your thoughts. I asked Johannes to check your comment. For now, I'm going to drop it from v2 of this patchset, so we can go ahead with the other patches. -- Cheers, Luca.
Sorry, missed your comment. On Fri, 2020-04-03 at 10:28 -0400, Mark Asselstine wrote: > I was looking into this as part of > https://bugzilla.kernel.org/show_bug.cgi?id=206971 and had a similar > fix in flight. My concern was that queue_owner being used outside of > the RCU might be an issue Yes, that does *look* questionable, and I missed it completely. However, that's only because the code makes weak assumptions when it's under much stronger guarantees. There's no reason for it to use RCU here for the station lookup, since it's holding the write-side lock (which is the mvm->mutex). IOW, we could just change sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); to sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], ...); and then it's clear that there's no issue. > as now you have no guaranty that the > eventual use of sta->txq[tid] in iwl_mvm_free_inactive_queue() is > going to be valid. The only way to work around this is instead of > storing queue_owner, store mvmtxq = iwl_mvm_txq_from_tid(sta, i), then > adjust iwl_mvm_free_inactive_queue(), iwl_mvm_disable_txq() and > whatnot to take struct iwl_mvm_txq * instead of struct ieee80211_sta > *. If you open the bug you will see the latest version of my work as > the attached patch. I am not an RCU expert so I am curious to hear > your thoughts. You could do that too, but it seems overly complex to me. johannes
Luca Coelho <luca@coelho.fi> wrote: > From: Johannes Berg <johannes.berg@intel.com> > > iwl_mvm_free_inactive_queue() will sleep in synchronize_net() under > some circumstances, so don't call it under RCU. There doesn't appear > to be a need for RCU protection around this particular call. > > Cc: stable@vger.kernel.org # v5.4+ > Signed-off-by: Johannes Berg <johannes.berg@intel.com> > Signed-off-by: Luca Coelho <luciano.coelho@intel.com> I'll queue this for v5.8.
Luca Coelho <luca@coelho.fi> wrote: > From: Johannes Berg <johannes.berg@intel.com> > > iwl_mvm_free_inactive_queue() will sleep in synchronize_net() under > some circumstances, so don't call it under RCU. There doesn't appear > to be a need for RCU protection around this particular call. > > Cc: stable@vger.kernel.org # v5.4+ > Signed-off-by: Johannes Berg <johannes.berg@intel.com> > Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Patch applied to wireless-drivers.git, thanks. fbb1461ad1d6 iwlwifi: mvm: don't call iwl_mvm_free_inactive_queue() under RCU
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 56ae72debb96..9ca433fdf634 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1184,17 +1184,15 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta) for_each_set_bit(i, &changetid_queues, IWL_MAX_HW_QUEUES) iwl_mvm_change_queue_tid(mvm, i); + rcu_read_unlock(); + if (free_queue >= 0 && alloc_for_sta != IWL_MVM_INVALID_STA) { ret = iwl_mvm_free_inactive_queue(mvm, free_queue, queue_owner, alloc_for_sta); - if (ret) { - rcu_read_unlock(); + if (ret) return ret; - } } - rcu_read_unlock(); - return free_queue; }