Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5e7aa8e authored by Emmanuel Grumbach's avatar Emmanuel Grumbach
Browse files

iwlwifi: mvm: refactor power code



The main complexity of the power code is that it needs to
take into account the firmware limitations.
These limitations state that we need to have a global
picture of the vifs present in the system to be able to
decide if we can enable power management on a specific vif.

Even device power save (as opposed to vif power management)
must be disabled in certain circumstances (monitor vif).

Refactor the current code to make this clearer by defining
a function that explicitely computes these constraints.

Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 6345061f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1195,7 +1195,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
	if (ret)
		goto out;

	ret = iwl_mvm_power_mac_update_mode(mvm, vif);
	ret = iwl_mvm_power_update_mac(mvm, vif);
	if (ret)
		goto out;

+1 −1
Original line number Diff line number Diff line
@@ -185,7 +185,7 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,

	mutex_lock(&mvm->mutex);
	iwl_dbgfs_update_pm(mvm, vif, param, val);
	ret = iwl_mvm_power_mac_update_mode(mvm, vif);
	ret = iwl_mvm_power_update_mac(mvm, vif);
	mutex_unlock(&mvm->mutex);

	return ret ?: count;
+11 −8
Original line number Diff line number Diff line
@@ -717,7 +717,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
	if (ret)
		goto out_release;

	iwl_mvm_power_mac_update_mode(mvm, vif);
	ret = iwl_mvm_power_update_mac(mvm, vif);
	if (ret)
		goto out_release;

	/* beacon filtering */
	ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
@@ -867,6 +869,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
	if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
		mvm->vif_count--;

	iwl_mvm_power_update_mac(mvm, vif);
	iwl_mvm_mac_ctxt_remove(mvm, vif);

out_release:
@@ -1231,7 +1234,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
					  &mvmvif->time_event_data);
	} else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
			      BSS_CHANGED_QOS)) {
		ret = iwl_mvm_power_mac_update_mode(mvm, vif);
		ret = iwl_mvm_power_update_mac(mvm, vif);
		if (ret)
			IWL_ERR(mvm, "failed to update power mode\n");
	}
@@ -1298,7 +1301,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,

	/* power updated needs to be done before quotas */
	mvm->bound_vif_cnt++;
	iwl_mvm_power_update_binding(mvm, vif, true);
	iwl_mvm_power_update_mac(mvm, vif);

	ret = iwl_mvm_update_quotas(mvm, vif);
	if (ret)
@@ -1317,7 +1320,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,

out_quota_failed:
	mvm->bound_vif_cnt--;
	iwl_mvm_power_update_binding(mvm, vif, false);
	iwl_mvm_power_update_mac(mvm, vif);
	mvmvif->ap_ibss_active = false;
	iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
out_unbind:
@@ -1354,7 +1357,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
	iwl_mvm_binding_remove_vif(mvm, vif);

	mvm->bound_vif_cnt--;
	iwl_mvm_power_update_binding(mvm, vif, false);
	iwl_mvm_power_update_mac(mvm, vif);

	iwl_mvm_mac_ctxt_remove(mvm, vif);

@@ -2088,7 +2091,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
	 * otherwise fw will complain.
	 */
	mvm->bound_vif_cnt++;
	iwl_mvm_power_update_binding(mvm, vif, true);
	iwl_mvm_power_update_mac(mvm, vif);

	/* Setting the quota at this stage is only required for monitor
	 * interfaces. For the other types, the bss_info changed flow
@@ -2106,7 +2109,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
 out_remove_binding:
	iwl_mvm_binding_remove_vif(mvm, vif);
	mvm->bound_vif_cnt--;
	iwl_mvm_power_update_binding(mvm, vif, false);
	iwl_mvm_power_update_mac(mvm, vif);
 out_unlock:
	mutex_unlock(&mvm->mutex);
	if (ret)
@@ -2139,7 +2142,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,

	iwl_mvm_binding_remove_vif(mvm, vif);
	mvm->bound_vif_cnt--;
	iwl_mvm_power_update_binding(mvm, vif, false);
	iwl_mvm_power_update_mac(mvm, vif);

out_unlock:
	mvmvif->phy_ctxt = NULL;
+4 −5
Original line number Diff line number Diff line
@@ -588,7 +588,9 @@ struct iwl_mvm {
	u8 bound_vif_cnt;

	/* Indicate if device power save is allowed */
	bool ps_prevented;
	bool ps_disabled;
	/* Indicate if device power management is allowed */
	bool pm_disabled;
};

/* Extract MVM priv from op_mode and _hw */
@@ -812,11 +814,8 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init);
/* power management */
int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm);

int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
				  struct ieee80211_vif *vif);
int iwl_mvm_power_update_device(struct iwl_mvm *mvm);
void iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
				  struct ieee80211_vif *vif, bool assign);
int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
				 char *buf, int bufsz);

+99 −70
Original line number Diff line number Diff line
@@ -298,8 +298,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
	keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
	cmd->keep_alive_seconds = cpu_to_le16(keep_alive);

	if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
	    mvm->ps_prevented)
	if (mvm->ps_disabled)
		return;

	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
@@ -309,8 +308,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
	    mvmvif->dbgfs_pm.disable_power_off)
		cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
#endif
	if (!vif->bss_conf.ps || mvm->bound_vif_cnt > 1 ||
	    iwl_mvm_vif_low_latency(mvmvif))
	if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
	    mvm->pm_disabled)
		return;

	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -417,7 +416,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
#endif /* CONFIG_IWLWIFI_DEBUGFS */
}

static int _iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
					 struct ieee80211_vif *vif)
{
	struct iwl_mac_power_cmd cmd = {};
@@ -439,39 +438,22 @@ static int _iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
				    sizeof(cmd), &cmd);
}

int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
				  struct ieee80211_vif *vif)

{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	bool ba_enable;
	int ret;

	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
		return 0;

	ret = _iwl_mvm_power_mac_update_mode(mvm, vif);
	if (ret)
		return ret;

	ba_enable = !(iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
		      mvm->ps_prevented || mvm->bound_vif_cnt > 1 ||
		      !vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif));

	return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
}

static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
{
	struct iwl_device_power_cmd cmd = {
		.flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
	};

	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
		return 0;

	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
		return 0;

	if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
	    force_disable)
	if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
		mvm->ps_disabled = true;

	if (mvm->ps_disabled)
		cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);

#ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -488,14 +470,6 @@ static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
				    &cmd);
}

int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
{
	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
		return 0;

	return _iwl_mvm_power_update_device(mvm, false);
}

void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -534,64 +508,119 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
	return 0;
}

struct iwl_mvm_power_iterator {
	struct iwl_mvm *mvm;
struct iwl_power_constraint {
	struct ieee80211_vif *bf_vif;
	struct ieee80211_vif *bss_vif;
	bool pm_disabled;
	bool ps_disabled;
};

static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac,
static void iwl_mvm_power_iterator(void *_data, u8 *mac,
				   struct ieee80211_vif *vif)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	struct iwl_mvm_power_iterator *power_iterator = _data;
	struct iwl_mvm *mvm = power_iterator->mvm;
	int ret;
	struct iwl_power_constraint *power_iterator = _data;

	switch (ieee80211_vif_type_p2p(vif)) {
	case NL80211_IFTYPE_P2P_DEVICE:
		break;

	case NL80211_IFTYPE_P2P_GO:
	case NL80211_IFTYPE_AP:
		/* no BSS power mgmt if we have an active AP */
		if (mvmvif->ap_ibss_active)
			power_iterator->pm_disabled = true;
		break;

	ret = _iwl_mvm_power_mac_update_mode(mvm, vif);
	case NL80211_IFTYPE_MONITOR:
		/* no BSS power mgmt and no device power save */
		power_iterator->pm_disabled = true;
		power_iterator->ps_disabled = true;
		break;

	case NL80211_IFTYPE_P2P_CLIENT:
		/* no BSS power mgmt if we have a P2P client*/
		power_iterator->pm_disabled = true;
		break;

	if (mvmvif->bf_data.bf_enabled && !WARN_ON(power_iterator->bf_vif))
	case NL80211_IFTYPE_STATION:
		/* we should have only one BSS vif */
		WARN_ON(power_iterator->bss_vif);
		power_iterator->bss_vif = vif;

		if (mvmvif->bf_data.bf_enabled &&
		    !WARN_ON(power_iterator->bf_vif))
			power_iterator->bf_vif = vif;
		break;

	WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
	default:
		break;
	}
}

void iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
				  struct ieee80211_vif *vif, bool assign)
static void
iwl_mvm_power_get_global_constraint(struct iwl_mvm *mvm,
				    struct iwl_power_constraint *constraint)
{
	lockdep_assert_held(&mvm->mutex);

	if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
		constraint->pm_disabled = true;
		constraint->ps_disabled = true;
	}

	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
					    IEEE80211_IFACE_ITER_NORMAL,
					    iwl_mvm_power_iterator, constraint);

	/* TODO: remove this and determine this variable in the iterator */
	if (mvm->bound_vif_cnt > 1)
		constraint->pm_disabled = true;
}

int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
	bool ba_enable;
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	struct iwl_mvm_power_iterator power_it = {
		.mvm = mvm,
	};
	struct iwl_power_constraint constraint = {};
	bool ba_enable;
	int ret;

	lockdep_assert_held(&mvm->mutex);

	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
		return;
		return 0;

	iwl_mvm_power_get_global_constraint(mvm, &constraint);
	mvm->ps_disabled = constraint.ps_disabled;
	mvm->pm_disabled = constraint.pm_disabled;

	/* don't update device power state unless we add / remove monitor */
	if (vif->type == NL80211_IFTYPE_MONITOR) {
		int ret = _iwl_mvm_power_update_device(mvm, assign);
		mvm->ps_prevented = assign;
		WARN_ONCE(ret, "Failed to update power device state\n");
		ret = iwl_mvm_power_update_device(mvm);
		if (ret)
			return ret;
	}

	ieee80211_iterate_active_interfaces(mvm->hw,
					    IEEE80211_IFACE_ITER_NORMAL,
					    iwl_mvm_power_binding_iterator,
					    &power_it);
	ret = iwl_mvm_power_send_cmd(mvm, vif);
	if (ret)
		return ret;

	if (!power_it.bf_vif)
		return;
	if (constraint.bss_vif && vif != constraint.bss_vif) {
		ret = iwl_mvm_power_send_cmd(mvm, constraint.bss_vif);
		if (ret)
			return ret;
	}

	if (!constraint.bf_vif)
		return 0;

	vif = power_it.bf_vif;
	vif = constraint.bf_vif;
	mvmvif = iwl_mvm_vif_from_mac80211(vif);

	ba_enable = !(iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
		      mvm->ps_prevented || mvm->bound_vif_cnt > 1 ||
	ba_enable = !(constraint.pm_disabled || constraint.ps_disabled ||
		      !vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif));

	WARN_ON_ONCE(iwl_mvm_update_beacon_abort(mvm, power_it.bf_vif,
						 ba_enable));
	return iwl_mvm_update_beacon_abort(mvm, constraint.bf_vif, ba_enable);
}

#ifdef CONFIG_IWLWIFI_DEBUGFS
Loading