Commit 3c6a041b authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge tag 'wireless-2025-03-04' of...

Merge tag 'wireless-2025-03-04' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless

Johannes Berg says:

====================
bugfixes for 6.14:

 * regressions from this cycle:
   - mac80211: fix sparse warning for monitor
   - nl80211: disable multi-link reconfiguration (needs fixing)
 * older issues:
   - cfg80211: reject badly combined cooked monitor,
               fix regulatory hint validity checks
   - mac80211: handle TXQ flush w/o driver per-sta flush,
               fix debugfs for monitor, fix element inheritance
   - iwlwifi: fix rfkill, dead firmware handling, rate API
              version, free A-MSDU handling, avoid large
              allocations, fix string format
   - brcmfmac: fix power handling on some boards

* tag 'wireless-2025-03-04' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless:
  wifi: nl80211: disable multi-link reconfiguration
  wifi: cfg80211: regulatory: improve invalid hints checking
  wifi: brcmfmac: keep power during suspend if board requires it
  wifi: mac80211: Fix sparse warning for monitor_sdata
  wifi: mac80211: fix vendor-specific inheritance
  wifi: mac80211: fix MLE non-inheritance parsing
  wifi: iwlwifi: Fix A-MSDU TSO preparation
  wifi: iwlwifi: Free pages allocated when failing to build A-MSDU
  wifi: iwlwifi: limit printed string from FW file
  wifi: iwlwifi: mvm: use the right version of the rate API
  wifi: iwlwifi: mvm: don't try to talk to a dead firmware
  wifi: iwlwifi: mvm: don't dump the firmware state upon RFKILL while suspend
  wifi: iwlwifi: mvm: clean up ROC on failure
  wifi: iwlwifi: fw: avoid using an uninitialized variable
  wifi: iwlwifi: fw: allocate chained SG tables for dump
  wifi: mac80211: remove debugfs dir for virtual monitor
  wifi: mac80211: Cleanup sta TXQs on flush
  wifi: nl80211: reject cooked mode if it is set along with other flags
====================

Link: https://patch.msgid.link/20250304124435.126272-3-johannes@sipsolutions.net


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents b7365eab 1f860eb4
Loading
Loading
Loading
Loading
+13 −7
Original line number Diff line number Diff line
@@ -1172,6 +1172,7 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
	struct brcmf_bus *bus_if;
	struct brcmf_sdio_dev *sdiodev;
	mmc_pm_flag_t sdio_flags;
	bool cap_power_off;
	int ret = 0;

	func = container_of(dev, struct sdio_func, dev);
@@ -1179,19 +1180,23 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
	if (func->num != 1)
		return 0;

	cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD);

	bus_if = dev_get_drvdata(dev);
	sdiodev = bus_if->bus_priv.sdio;

	if (sdiodev->wowl_enabled) {
	if (sdiodev->wowl_enabled || !cap_power_off) {
		brcmf_sdiod_freezer_on(sdiodev);
		brcmf_sdio_wd_timer(sdiodev->bus, 0);

		sdio_flags = MMC_PM_KEEP_POWER;

		if (sdiodev->wowl_enabled) {
			if (sdiodev->settings->bus.sdio.oob_irq_supported)
				enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
			else
				sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
		}

		if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags))
			brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
@@ -1213,18 +1218,19 @@ static int brcmf_ops_sdio_resume(struct device *dev)
	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
	struct sdio_func *func = container_of(dev, struct sdio_func, dev);
	int ret = 0;
	bool cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD);

	brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
	if (func->num != 2)
		return 0;

	if (!sdiodev->wowl_enabled) {
	if (!sdiodev->wowl_enabled && cap_power_off) {
		/* bus was powered off and device removed, probe again */
		ret = brcmf_sdiod_probe(sdiodev);
		if (ret)
			brcmf_err("Failed to probe device on resume\n");
	} else {
		if (sdiodev->settings->bus.sdio.oob_irq_supported)
		if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
			disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);

		brcmf_sdiod_freezer_off(sdiodev);
+58 −28
Original line number Diff line number Diff line
@@ -558,41 +558,71 @@ static void iwl_dump_prph(struct iwl_fw_runtime *fwrt,
}

/*
 * alloc_sgtable - allocates scallerlist table in the given size,
 * alloc_sgtable - allocates (chained) scatterlist in the given size,
 *	fills it with pages and returns it
 * @size: the size (in bytes) of the table
 */
static struct scatterlist *alloc_sgtable(int size)
static struct scatterlist *alloc_sgtable(ssize_t size)
{
	int alloc_size, nents, i;
	struct page *new_page;
	struct scatterlist *iter;
	struct scatterlist *table;
	struct scatterlist *result = NULL, *prev;
	int nents, i, n_prev;

	nents = DIV_ROUND_UP(size, PAGE_SIZE);
	table = kcalloc(nents, sizeof(*table), GFP_KERNEL);
	if (!table)

#define N_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(*result))
	/*
	 * We need an additional entry for table chaining,
	 * this ensures the loop can finish i.e. we can
	 * fit at least two entries per page (obviously,
	 * many more really fit.)
	 */
	BUILD_BUG_ON(N_ENTRIES_PER_PAGE < 2);

	while (nents > 0) {
		struct scatterlist *new, *iter;
		int n_fill, n_alloc;

		if (nents <= N_ENTRIES_PER_PAGE) {
			/* last needed table */
			n_fill = nents;
			n_alloc = nents;
			nents = 0;
		} else {
			/* fill a page with entries */
			n_alloc = N_ENTRIES_PER_PAGE;
			/* reserve one for chaining */
			n_fill = n_alloc - 1;
			nents -= n_fill;
		}

		new = kcalloc(n_alloc, sizeof(*new), GFP_KERNEL);
		if (!new) {
			if (result)
				_devcd_free_sgtable(result);
			return NULL;
	sg_init_table(table, nents);
	iter = table;
	for_each_sg(table, iter, sg_nents(table), i) {
		new_page = alloc_page(GFP_KERNEL);
		}
		sg_init_table(new, n_alloc);

		if (!result)
			result = new;
		else
			sg_chain(prev, n_prev, new);
		prev = new;
		n_prev = n_alloc;

		for_each_sg(new, iter, n_fill, i) {
			struct page *new_page = alloc_page(GFP_KERNEL);

			if (!new_page) {
			/* release all previous allocated pages in the table */
			iter = table;
			for_each_sg(table, iter, sg_nents(table), i) {
				new_page = sg_page(iter);
				if (new_page)
					__free_page(new_page);
			}
			kfree(table);
				_devcd_free_sgtable(result);
				return NULL;
			}
		alloc_size = min_t(int, size, PAGE_SIZE);
		size -= PAGE_SIZE;
		sg_set_page(iter, new_page, alloc_size, 0);

			sg_set_page(iter, new_page, PAGE_SIZE, 0);
		}
	}
	return table;

	return result;
}

static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt,
+3 −0
Original line number Diff line number Diff line
@@ -540,6 +540,9 @@ bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id)
	} err_info = {};
	int ret;

	if (err_id)
		*err_id = 0;

	if (!base)
		return false;

+1 −1
Original line number Diff line number Diff line
@@ -1181,7 +1181,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,

			if (tlv_len != sizeof(*fseq_ver))
				goto invalid_tlv_len;
			IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %s\n",
			IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n",
				 fseq_ver->version);
			}
			break;
+51 −26
Original line number Diff line number Diff line
@@ -3092,7 +3092,13 @@ static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac,
		ieee80211_resume_disconnect(vif);
}

static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm,
enum rt_status {
	FW_ALIVE,
	FW_NEEDS_RESET,
	FW_ERROR,
};

static enum rt_status iwl_mvm_check_rt_status(struct iwl_mvm *mvm,
					      struct ieee80211_vif *vif)
{
	u32 err_id;
@@ -3101,29 +3107,35 @@ static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm,
	if (iwl_fwrt_read_err_table(mvm->trans,
				    mvm->trans->dbg.lmac_error_event_table[0],
				    &err_id)) {
		if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) {
		if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
			IWL_WARN(mvm, "Rfkill was toggled during suspend\n");
			if (vif) {
				struct cfg80211_wowlan_wakeup wakeup = {
					.rfkill_release = true,
				};

				ieee80211_report_wowlan_wakeup(vif, &wakeup,
							       GFP_KERNEL);
			}
		return true;

			return FW_NEEDS_RESET;
		}
		return FW_ERROR;
	}

	/* check if we have lmac2 set and check for error */
	if (iwl_fwrt_read_err_table(mvm->trans,
				    mvm->trans->dbg.lmac_error_event_table[1],
				    NULL))
		return true;
		return FW_ERROR;

	/* check for umac error */
	if (iwl_fwrt_read_err_table(mvm->trans,
				    mvm->trans->dbg.umac_error_event_table,
				    NULL))
		return true;
		return FW_ERROR;

	return false;
	return FW_ALIVE;
}

/*
@@ -3492,6 +3504,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
	bool d0i3_first = fw_has_capa(&mvm->fw->ucode_capa,
				      IWL_UCODE_TLV_CAPA_D0I3_END_FIRST);
	bool resume_notif_based = iwl_mvm_d3_resume_notif_based(mvm);
	enum rt_status rt_status;
	bool keep = false;

	mutex_lock(&mvm->mutex);
@@ -3515,14 +3528,19 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)

	iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt);

	if (iwl_mvm_check_rt_status(mvm, vif)) {
		IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n");
	rt_status = iwl_mvm_check_rt_status(mvm, vif);
	if (rt_status != FW_ALIVE) {
		set_bit(STATUS_FW_ERROR, &mvm->trans->status);
		if (rt_status == FW_ERROR) {
			IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n");
			iwl_mvm_dump_nic_error_log(mvm);
			iwl_dbg_tlv_time_point(&mvm->fwrt,
				       IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL);
		iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
					       IWL_FW_INI_TIME_POINT_FW_ASSERT,
					       NULL);
			iwl_fw_dbg_collect_desc(&mvm->fwrt,
						&iwl_dump_desc_assert,
						false, 0);
		}
		ret = 1;
		goto err;
	}
@@ -3679,6 +3697,7 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
		.notif_expected =
			IWL_D3_NOTIF_D3_END_NOTIF,
	};
	enum rt_status rt_status;
	int ret;

	lockdep_assert_held(&mvm->mutex);
@@ -3688,14 +3707,20 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
	mvm->last_reset_or_resume_time_jiffies = jiffies;
	iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt);

	if (iwl_mvm_check_rt_status(mvm, NULL)) {
		IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n");
	rt_status = iwl_mvm_check_rt_status(mvm, NULL);
	if (rt_status != FW_ALIVE) {
		set_bit(STATUS_FW_ERROR, &mvm->trans->status);
		if (rt_status == FW_ERROR) {
			IWL_ERR(mvm,
				"iwl_mvm_check_rt_status failed, device is gone during suspend\n");
			iwl_mvm_dump_nic_error_log(mvm);
			iwl_dbg_tlv_time_point(&mvm->fwrt,
				       IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL);
		iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
					       IWL_FW_INI_TIME_POINT_FW_ASSERT,
					       NULL);
			iwl_fw_dbg_collect_desc(&mvm->fwrt,
						&iwl_dump_desc_assert,
						false, 0);
		}
		mvm->trans->state = IWL_TRANS_NO_FW;
		ret = -ENODEV;

Loading