--- /dev/null
+From 3300295395b57a0789f405cfd36f6de6bb740275 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 18:51:06 +0100
+Subject: ALSA: hda/realtek: Remove (revert) duplicate Ally X config
+
+From: Antheas Kapenekakis <lkml@antheas.dev>
+
+[ Upstream commit 3414cda9d41f41703832d0abd01063dd8de82b89 ]
+
+In commit 1e9c708dc3ae ("ALSA: hda/tas2781: Add new quirk for Lenovo,
+ASUS, Dell projects") Baojun adds a bunch of projects to the file,
+including for the Ally X. Turns out the initial Ally X was not sorted
+properly, so the kernel had 2 quirks for it.
+
+The previous quirk overrode the new one due to being earlier and they
+are different. When AB testing, the normal pin fixup seems to work ok
+but causes a bit of a minor popping. Given the other config is more
+complicated and may cause undefined behavior, revert it.
+
+Fixes: 1e9c708dc3ae ("ALSA: hda/tas2781: Add new quirk for Lenovo, ASUS, Dell projects")
+Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
+Link: https://patch.msgid.link/20250227175107.33432-2-lkml@antheas.dev
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/pci/hda/patch_realtek.c | 8 --------
+ 1 file changed, 8 deletions(-)
+
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index 9997bf0150576..d3a81d3638872 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -7741,7 +7741,6 @@ enum {
+ ALC285_FIXUP_THINKPAD_X1_GEN7,
+ ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ ALC294_FIXUP_ASUS_ALLY,
+- ALC294_FIXUP_ASUS_ALLY_X,
+ ALC294_FIXUP_ASUS_ALLY_PINS,
+ ALC294_FIXUP_ASUS_ALLY_VERBS,
+ ALC294_FIXUP_ASUS_ALLY_SPEAKER,
+@@ -9184,12 +9183,6 @@ static const struct hda_fixup alc269_fixups[] = {
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS
+ },
+- [ALC294_FIXUP_ASUS_ALLY_X] = {
+- .type = HDA_FIXUP_FUNC,
+- .v.func = tas2781_fixup_i2c,
+- .chained = true,
+- .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS
+- },
+ [ALC294_FIXUP_ASUS_ALLY_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+@@ -10674,7 +10667,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS),
+ SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x17f3, "ROG Ally NR2301L/X", ALC294_FIXUP_ASUS_ALLY),
+- SND_PCI_QUIRK(0x1043, 0x1eb3, "ROG Ally X RC72LA", ALC294_FIXUP_ASUS_ALLY_X),
+ SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS),
+ SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),
+--
+2.39.5
+
--- /dev/null
+From e263df37d56a8b062b8798990e88520275558cf2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 3 Mar 2025 13:04:13 +0300
+Subject: ALSA: usx2y: validate nrpacks module parameter on probe
+
+From: Murad Masimov <m.masimov@mt-integration.ru>
+
+[ Upstream commit 172a0f509723fe4741d4b8e9190cf434b18320d8 ]
+
+The module parameter defines number of iso packets per one URB. User is
+allowed to set any value to the parameter of type int, which can lead to
+various kinds of weird and incorrect behavior like integer overflows,
+truncations, etc. Number of packets should be a small non-negative number.
+
+Since this parameter is read-only, its value can be validated on driver
+probe.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Murad Masimov <m.masimov@mt-integration.ru>
+Link: https://patch.msgid.link/20250303100413.835-1-m.masimov@mt-integration.ru
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/usb/usx2y/usbusx2y.c | 11 +++++++++++
+ sound/usb/usx2y/usbusx2y.h | 26 ++++++++++++++++++++++++++
+ sound/usb/usx2y/usbusx2yaudio.c | 27 ---------------------------
+ 3 files changed, 37 insertions(+), 27 deletions(-)
+
+diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
+index 5f81c68fd42b6..5756ff3528a2d 100644
+--- a/sound/usb/usx2y/usbusx2y.c
++++ b/sound/usb/usx2y/usbusx2y.c
+@@ -151,6 +151,12 @@ static int snd_usx2y_card_used[SNDRV_CARDS];
+ static void snd_usx2y_card_private_free(struct snd_card *card);
+ static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s);
+
++#ifdef USX2Y_NRPACKS_VARIABLE
++int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */
++module_param(nrpacks, int, 0444);
++MODULE_PARM_DESC(nrpacks, "Number of packets per URB.");
++#endif
++
+ /*
+ * pipe 4 is used for switching the lamps, setting samplerate, volumes ....
+ */
+@@ -432,6 +438,11 @@ static int snd_usx2y_probe(struct usb_interface *intf,
+ struct snd_card *card;
+ int err;
+
++#ifdef USX2Y_NRPACKS_VARIABLE
++ if (nrpacks < 0 || nrpacks > USX2Y_NRPACKS_MAX)
++ return -EINVAL;
++#endif
++
+ if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 ||
+ (le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 &&
+ le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 &&
+diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h
+index 391fd7b4ed5ef..6a76d04bf1c7d 100644
+--- a/sound/usb/usx2y/usbusx2y.h
++++ b/sound/usb/usx2y/usbusx2y.h
+@@ -7,6 +7,32 @@
+
+ #define NRURBS 2
+
++/* Default value used for nr of packs per urb.
++ * 1 to 4 have been tested ok on uhci.
++ * To use 3 on ohci, you'd need a patch:
++ * look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on
++ * "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425"
++ *
++ * 1, 2 and 4 work out of the box on ohci, if I recall correctly.
++ * Bigger is safer operation, smaller gives lower latencies.
++ */
++#define USX2Y_NRPACKS 4
++
++#define USX2Y_NRPACKS_MAX 1024
++
++/* If your system works ok with this module's parameter
++ * nrpacks set to 1, you might as well comment
++ * this define out, and thereby produce smaller, faster code.
++ * You'd also set USX2Y_NRPACKS to 1 then.
++ */
++#define USX2Y_NRPACKS_VARIABLE 1
++
++#ifdef USX2Y_NRPACKS_VARIABLE
++extern int nrpacks;
++#define nr_of_packs() nrpacks
++#else
++#define nr_of_packs() USX2Y_NRPACKS
++#endif
+
+ #define URBS_ASYNC_SEQ 10
+ #define URB_DATA_LEN_ASYNC_SEQ 32
+diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
+index f540f46a0b143..acca8bead82e5 100644
+--- a/sound/usb/usx2y/usbusx2yaudio.c
++++ b/sound/usb/usx2y/usbusx2yaudio.c
+@@ -28,33 +28,6 @@
+ #include "usx2y.h"
+ #include "usbusx2y.h"
+
+-/* Default value used for nr of packs per urb.
+- * 1 to 4 have been tested ok on uhci.
+- * To use 3 on ohci, you'd need a patch:
+- * look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on
+- * "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425"
+- *
+- * 1, 2 and 4 work out of the box on ohci, if I recall correctly.
+- * Bigger is safer operation, smaller gives lower latencies.
+- */
+-#define USX2Y_NRPACKS 4
+-
+-/* If your system works ok with this module's parameter
+- * nrpacks set to 1, you might as well comment
+- * this define out, and thereby produce smaller, faster code.
+- * You'd also set USX2Y_NRPACKS to 1 then.
+- */
+-#define USX2Y_NRPACKS_VARIABLE 1
+-
+-#ifdef USX2Y_NRPACKS_VARIABLE
+-static int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */
+-#define nr_of_packs() nrpacks
+-module_param(nrpacks, int, 0444);
+-MODULE_PARM_DESC(nrpacks, "Number of packets per URB.");
+-#else
+-#define nr_of_packs() USX2Y_NRPACKS
+-#endif
+-
+ static int usx2y_urb_capt_retire(struct snd_usx2y_substream *subs)
+ {
+ struct urb *urb = subs->completed_urb;
+--
+2.39.5
+
--- /dev/null
+From 94a6ef1ee3bfecb5a6b3a5df6d7bd00c33f4e43d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 18:41:29 +0200
+Subject: be2net: fix sleeping while atomic bugs in be_ndo_bridge_getlink
+
+From: Nikolay Aleksandrov <razor@blackwall.org>
+
+[ Upstream commit 1a82d19ca2d6835904ee71e2d40fd331098f94a0 ]
+
+Partially revert commit b71724147e73 ("be2net: replace polling with
+sleeping in the FW completion path") w.r.t mcc mutex it introduces and the
+use of usleep_range. The be2net be_ndo_bridge_getlink() callback is
+called with rcu_read_lock, so this code has been broken for a long time.
+Both the mutex_lock and the usleep_range can cause the issue Ian Kumlien
+reported[1]. The call path is:
+be_ndo_bridge_getlink -> be_cmd_get_hsw_config -> be_mcc_notify_wait ->
+be_mcc_wait_compl -> usleep_range()
+
+[1] https://lore.kernel.org/netdev/CAA85sZveppNgEVa_FD+qhOMtG_AavK9_mFiU+jWrMtXmwqefGA@mail.gmail.com/
+
+Tested-by: Ian Kumlien <ian.kumlien@gmail.com>
+Fixes: b71724147e73 ("be2net: replace polling with sleeping in the FW completion path")
+Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/20250227164129.1201164-1-razor@blackwall.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/emulex/benet/be.h | 2 +-
+ drivers/net/ethernet/emulex/benet/be_cmds.c | 197 ++++++++++----------
+ drivers/net/ethernet/emulex/benet/be_main.c | 2 +-
+ 3 files changed, 100 insertions(+), 101 deletions(-)
+
+diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h
+index e48b861e4ce15..270ff9aab3352 100644
+--- a/drivers/net/ethernet/emulex/benet/be.h
++++ b/drivers/net/ethernet/emulex/benet/be.h
+@@ -562,7 +562,7 @@ struct be_adapter {
+ struct be_dma_mem mbox_mem_alloced;
+
+ struct be_mcc_obj mcc_obj;
+- struct mutex mcc_lock; /* For serializing mcc cmds to BE card */
++ spinlock_t mcc_lock; /* For serializing mcc cmds to BE card */
+ spinlock_t mcc_cq_lock;
+
+ u16 cfg_num_rx_irqs; /* configured via set-channels */
+diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
+index 61adcebeef010..51b8377edd1d0 100644
+--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
++++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
+@@ -575,7 +575,7 @@ int be_process_mcc(struct be_adapter *adapter)
+ /* Wait till no more pending mcc requests are present */
+ static int be_mcc_wait_compl(struct be_adapter *adapter)
+ {
+-#define mcc_timeout 12000 /* 12s timeout */
++#define mcc_timeout 120000 /* 12s timeout */
+ int i, status = 0;
+ struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
+
+@@ -589,7 +589,7 @@ static int be_mcc_wait_compl(struct be_adapter *adapter)
+
+ if (atomic_read(&mcc_obj->q.used) == 0)
+ break;
+- usleep_range(500, 1000);
++ udelay(100);
+ }
+ if (i == mcc_timeout) {
+ dev_err(&adapter->pdev->dev, "FW not responding\n");
+@@ -866,7 +866,7 @@ static bool use_mcc(struct be_adapter *adapter)
+ static int be_cmd_lock(struct be_adapter *adapter)
+ {
+ if (use_mcc(adapter)) {
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+ return 0;
+ } else {
+ return mutex_lock_interruptible(&adapter->mbox_lock);
+@@ -877,7 +877,7 @@ static int be_cmd_lock(struct be_adapter *adapter)
+ static void be_cmd_unlock(struct be_adapter *adapter)
+ {
+ if (use_mcc(adapter))
+- return mutex_unlock(&adapter->mcc_lock);
++ return spin_unlock_bh(&adapter->mcc_lock);
+ else
+ return mutex_unlock(&adapter->mbox_lock);
+ }
+@@ -1047,7 +1047,7 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
+ struct be_cmd_req_mac_query *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1076,7 +1076,7 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1088,7 +1088,7 @@ int be_cmd_pmac_add(struct be_adapter *adapter, const u8 *mac_addr,
+ struct be_cmd_req_pmac_add *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1113,7 +1113,7 @@ int be_cmd_pmac_add(struct be_adapter *adapter, const u8 *mac_addr,
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+
+ if (base_status(status) == MCC_STATUS_UNAUTHORIZED_REQUEST)
+ status = -EPERM;
+@@ -1131,7 +1131,7 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, int pmac_id, u32 dom)
+ if (pmac_id == -1)
+ return 0;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1151,7 +1151,7 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, int pmac_id, u32 dom)
+ status = be_mcc_notify_wait(adapter);
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1414,7 +1414,7 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
+ struct be_dma_mem *q_mem = &rxq->dma_mem;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1444,7 +1444,7 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1508,7 +1508,7 @@ int be_cmd_rxq_destroy(struct be_adapter *adapter, struct be_queue_info *q)
+ struct be_cmd_req_q_destroy *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1525,7 +1525,7 @@ int be_cmd_rxq_destroy(struct be_adapter *adapter, struct be_queue_info *q)
+ q->created = false;
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1593,7 +1593,7 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
+ struct be_cmd_req_hdr *hdr;
+ int status = 0;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1621,7 +1621,7 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
+ adapter->stats_cmd_sent = true;
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1637,7 +1637,7 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
+ CMD_SUBSYSTEM_ETH))
+ return -EPERM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1660,7 +1660,7 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
+ adapter->stats_cmd_sent = true;
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1697,7 +1697,7 @@ int be_cmd_link_status_query(struct be_adapter *adapter, u16 *link_speed,
+ struct be_cmd_req_link_status *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ if (link_status)
+ *link_status = LINK_DOWN;
+@@ -1736,7 +1736,7 @@ int be_cmd_link_status_query(struct be_adapter *adapter, u16 *link_speed,
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1747,7 +1747,7 @@ int be_cmd_get_die_temperature(struct be_adapter *adapter)
+ struct be_cmd_req_get_cntl_addnl_attribs *req;
+ int status = 0;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1762,7 +1762,7 @@ int be_cmd_get_die_temperature(struct be_adapter *adapter)
+
+ status = be_mcc_notify(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1811,7 +1811,7 @@ int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf)
+ if (!get_fat_cmd.va)
+ return -ENOMEM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ while (total_size) {
+ buf_size = min(total_size, (u32)60 * 1024);
+@@ -1849,9 +1849,9 @@ int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf)
+ log_offset += buf_size;
+ }
+ err:
++ spin_unlock_bh(&adapter->mcc_lock);
+ dma_free_coherent(&adapter->pdev->dev, get_fat_cmd.size,
+ get_fat_cmd.va, get_fat_cmd.dma);
+- mutex_unlock(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1862,7 +1862,7 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter)
+ struct be_cmd_req_get_fw_version *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1885,7 +1885,7 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter)
+ sizeof(adapter->fw_on_flash));
+ }
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1899,7 +1899,7 @@ static int __be_cmd_modify_eqd(struct be_adapter *adapter,
+ struct be_cmd_req_modify_eq_delay *req;
+ int status = 0, i;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1922,7 +1922,7 @@ static int __be_cmd_modify_eqd(struct be_adapter *adapter,
+
+ status = be_mcc_notify(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1949,7 +1949,7 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
+ struct be_cmd_req_vlan_config *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -1971,7 +1971,7 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
+
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -1982,7 +1982,7 @@ static int __be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
+ struct be_cmd_req_rx_filter *req = mem->va;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2015,7 +2015,7 @@ static int __be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
+
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2046,7 +2046,7 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc)
+ CMD_SUBSYSTEM_COMMON))
+ return -EPERM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2066,7 +2066,7 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc)
+ status = be_mcc_notify_wait(adapter);
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+
+ if (base_status(status) == MCC_STATUS_FEATURE_NOT_SUPPORTED)
+ return -EOPNOTSUPP;
+@@ -2085,7 +2085,7 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc)
+ CMD_SUBSYSTEM_COMMON))
+ return -EPERM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2108,7 +2108,7 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc)
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2189,7 +2189,7 @@ int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable,
+ if (!(be_if_cap_flags(adapter) & BE_IF_FLAGS_RSS))
+ return 0;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2214,7 +2214,7 @@ int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable,
+
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2226,7 +2226,7 @@ int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
+ struct be_cmd_req_enable_disable_beacon *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2247,7 +2247,7 @@ int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
+ status = be_mcc_notify_wait(adapter);
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2258,7 +2258,7 @@ int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, u32 *state)
+ struct be_cmd_req_get_beacon_state *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2282,7 +2282,7 @@ int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, u32 *state)
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2306,7 +2306,7 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
+ return -ENOMEM;
+ }
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2328,7 +2328,7 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
+ memcpy(data, resp->page_data + off, len);
+ }
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
+ return status;
+ }
+@@ -2345,7 +2345,7 @@ static int lancer_cmd_write_object(struct be_adapter *adapter,
+ void *ctxt = NULL;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+ adapter->flash_status = 0;
+
+ wrb = wrb_from_mccq(adapter);
+@@ -2387,7 +2387,7 @@ static int lancer_cmd_write_object(struct be_adapter *adapter,
+ if (status)
+ goto err_unlock;
+
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+
+ if (!wait_for_completion_timeout(&adapter->et_cmd_compl,
+ msecs_to_jiffies(60000)))
+@@ -2406,7 +2406,7 @@ static int lancer_cmd_write_object(struct be_adapter *adapter,
+ return status;
+
+ err_unlock:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2460,7 +2460,7 @@ static int lancer_cmd_delete_object(struct be_adapter *adapter,
+ struct be_mcc_wrb *wrb;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2478,7 +2478,7 @@ static int lancer_cmd_delete_object(struct be_adapter *adapter,
+
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2491,7 +2491,7 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
+ struct lancer_cmd_resp_read_object *resp;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2525,7 +2525,7 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
+ }
+
+ err_unlock:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2537,7 +2537,7 @@ static int be_cmd_write_flashrom(struct be_adapter *adapter,
+ struct be_cmd_write_flashrom *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+ adapter->flash_status = 0;
+
+ wrb = wrb_from_mccq(adapter);
+@@ -2562,7 +2562,7 @@ static int be_cmd_write_flashrom(struct be_adapter *adapter,
+ if (status)
+ goto err_unlock;
+
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+
+ if (!wait_for_completion_timeout(&adapter->et_cmd_compl,
+ msecs_to_jiffies(40000)))
+@@ -2573,7 +2573,7 @@ static int be_cmd_write_flashrom(struct be_adapter *adapter,
+ return status;
+
+ err_unlock:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -2584,7 +2584,7 @@ static int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
+ struct be_mcc_wrb *wrb;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -2611,7 +2611,7 @@ static int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
+ memcpy(flashed_crc, req->crc, 4);
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3217,7 +3217,7 @@ int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
+ struct be_cmd_req_acpi_wol_magic_config *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3234,7 +3234,7 @@ int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
+ status = be_mcc_notify_wait(adapter);
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3249,7 +3249,7 @@ int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
+ CMD_SUBSYSTEM_LOWLEVEL))
+ return -EPERM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3272,7 +3272,7 @@ int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
+ if (status)
+ goto err_unlock;
+
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+
+ if (!wait_for_completion_timeout(&adapter->et_cmd_compl,
+ msecs_to_jiffies(SET_LB_MODE_TIMEOUT)))
+@@ -3281,7 +3281,7 @@ int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
+ return status;
+
+ err_unlock:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3298,7 +3298,7 @@ int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
+ CMD_SUBSYSTEM_LOWLEVEL))
+ return -EPERM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3324,7 +3324,7 @@ int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
+ if (status)
+ goto err;
+
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+
+ wait_for_completion(&adapter->et_cmd_compl);
+ resp = embedded_payload(wrb);
+@@ -3332,7 +3332,7 @@ int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
+
+ return status;
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3348,7 +3348,7 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
+ CMD_SUBSYSTEM_LOWLEVEL))
+ return -EPERM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3382,7 +3382,7 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3393,7 +3393,7 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
+ struct be_cmd_req_seeprom_read *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3409,7 +3409,7 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
+ status = be_mcc_notify_wait(adapter);
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3424,7 +3424,7 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
+ CMD_SUBSYSTEM_COMMON))
+ return -EPERM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3469,7 +3469,7 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
+ }
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3479,7 +3479,7 @@ static int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain)
+ struct be_cmd_req_set_qos *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3499,7 +3499,7 @@ static int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain)
+ status = be_mcc_notify_wait(adapter);
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3611,7 +3611,7 @@ int be_cmd_get_fn_privileges(struct be_adapter *adapter, u32 *privilege,
+ struct be_cmd_req_get_fn_privileges *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3643,7 +3643,7 @@ int be_cmd_get_fn_privileges(struct be_adapter *adapter, u32 *privilege,
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3655,7 +3655,7 @@ int be_cmd_set_fn_privileges(struct be_adapter *adapter, u32 privileges,
+ struct be_cmd_req_set_fn_privileges *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3675,7 +3675,7 @@ int be_cmd_set_fn_privileges(struct be_adapter *adapter, u32 privileges,
+
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3707,7 +3707,7 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
+ return -ENOMEM;
+ }
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3771,7 +3771,7 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
+ }
+
+ out:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ dma_free_coherent(&adapter->pdev->dev, get_mac_list_cmd.size,
+ get_mac_list_cmd.va, get_mac_list_cmd.dma);
+ return status;
+@@ -3831,7 +3831,7 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
+ if (!cmd.va)
+ return -ENOMEM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3853,7 +3853,7 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
+
+ err:
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3889,7 +3889,7 @@ int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid,
+ CMD_SUBSYSTEM_COMMON))
+ return -EPERM;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3930,7 +3930,7 @@ int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid,
+ status = be_mcc_notify_wait(adapter);
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -3944,7 +3944,7 @@ int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid,
+ int status;
+ u16 vid;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -3991,7 +3991,7 @@ int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid,
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -4190,7 +4190,7 @@ int be_cmd_set_ext_fat_capabilites(struct be_adapter *adapter,
+ struct be_cmd_req_set_ext_fat_caps *req;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -4206,7 +4206,7 @@ int be_cmd_set_ext_fat_capabilites(struct be_adapter *adapter,
+
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -4684,7 +4684,7 @@ int be_cmd_manage_iface(struct be_adapter *adapter, u32 iface, u8 op)
+ if (iface == 0xFFFFFFFF)
+ return -1;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -4701,7 +4701,7 @@ int be_cmd_manage_iface(struct be_adapter *adapter, u32 iface, u8 op)
+
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -4735,7 +4735,7 @@ int be_cmd_get_if_id(struct be_adapter *adapter, struct be_vf_cfg *vf_cfg,
+ struct be_cmd_resp_get_iface_list *resp;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -4756,7 +4756,7 @@ int be_cmd_get_if_id(struct be_adapter *adapter, struct be_vf_cfg *vf_cfg,
+ }
+
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -4850,7 +4850,7 @@ int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain)
+ if (BEx_chip(adapter))
+ return 0;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -4868,7 +4868,7 @@ int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain)
+ req->enable = 1;
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -4941,7 +4941,7 @@ __be_cmd_set_logical_link_config(struct be_adapter *adapter,
+ u32 link_config = 0;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -4969,7 +4969,7 @@ __be_cmd_set_logical_link_config(struct be_adapter *adapter,
+
+ status = be_mcc_notify_wait(adapter);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -5000,8 +5000,7 @@ int be_cmd_set_features(struct be_adapter *adapter)
+ struct be_mcc_wrb *wrb;
+ int status;
+
+- if (mutex_lock_interruptible(&adapter->mcc_lock))
+- return -1;
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -5039,7 +5038,7 @@ int be_cmd_set_features(struct be_adapter *adapter)
+ dev_info(&adapter->pdev->dev,
+ "Adapter does not support HW error recovery\n");
+
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+
+@@ -5053,7 +5052,7 @@ int be_roce_mcc_cmd(void *netdev_handle, void *wrb_payload,
+ struct be_cmd_resp_hdr *resp;
+ int status;
+
+- mutex_lock(&adapter->mcc_lock);
++ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+@@ -5076,7 +5075,7 @@ int be_roce_mcc_cmd(void *netdev_handle, void *wrb_payload,
+ memcpy(wrb_payload, resp, sizeof(*resp) + resp->response_length);
+ be_dws_le_to_cpu(wrb_payload, sizeof(*resp) + resp->response_length);
+ err:
+- mutex_unlock(&adapter->mcc_lock);
++ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+ }
+ EXPORT_SYMBOL(be_roce_mcc_cmd);
+diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
+index 875fe379eea21..3d2e215921191 100644
+--- a/drivers/net/ethernet/emulex/benet/be_main.c
++++ b/drivers/net/ethernet/emulex/benet/be_main.c
+@@ -5667,8 +5667,8 @@ static int be_drv_init(struct be_adapter *adapter)
+ }
+
+ mutex_init(&adapter->mbox_lock);
+- mutex_init(&adapter->mcc_lock);
+ mutex_init(&adapter->rx_filter_lock);
++ spin_lock_init(&adapter->mcc_lock);
+ spin_lock_init(&adapter->mcc_cq_lock);
+ init_completion(&adapter->et_cmd_compl);
+
+--
+2.39.5
+
--- /dev/null
+From ed3ef458e79061125c75fe43be120623421ae184 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 21 Feb 2025 22:32:59 +0100
+Subject: bluetooth: btusb: Initialize .owner field of force_poll_sync_fops
+
+From: Salah Triki <salah.triki@gmail.com>
+
+[ Upstream commit cbf85b9cb80bec6345ffe0368dfff98386f4714f ]
+
+Initialize .owner field of force_poll_sync_fops to THIS_MODULE in order to
+prevent btusb from being unloaded while its operations are in use.
+
+Fixes: 800fe5ec302e ("Bluetooth: btusb: Add support for queuing during polling interval")
+Signed-off-by: Salah Triki <salah.triki@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/bluetooth/btusb.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
+index 6bc6dd417adf6..3a0b9dc98707f 100644
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -3644,6 +3644,7 @@ static ssize_t force_poll_sync_write(struct file *file,
+ }
+
+ static const struct file_operations force_poll_sync_fops = {
++ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = force_poll_sync_read,
+ .write = force_poll_sync_write,
+--
+2.39.5
+
--- /dev/null
+From dedf690f7f3eb1cf9c13836bbe90b60433709a85 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 23:46:27 +0500
+Subject: caif_virtio: fix wrong pointer check in cfv_probe()
+
+From: Vitaliy Shevtsov <v.shevtsov@mt-integration.ru>
+
+[ Upstream commit a466fd7e9fafd975949e5945e2f70c33a94b1a70 ]
+
+del_vqs() frees virtqueues, therefore cfv->vq_tx pointer should be checked
+for NULL before calling it, not cfv->vdev. Also the current implementation
+is redundant because the pointer cfv->vdev is dereferenced before it is
+checked for NULL.
+
+Fix this by checking cfv->vq_tx for NULL instead of cfv->vdev before
+calling del_vqs().
+
+Fixes: 0d2e1a2926b1 ("caif_virtio: Introduce caif over virtio")
+Signed-off-by: Vitaliy Shevtsov <v.shevtsov@mt-integration.ru>
+Reviewed-by: Gerhard Engleder <gerhard@engleder-embedded.com>
+Link: https://patch.msgid.link/20250227184716.4715-1-v.shevtsov@mt-integration.ru
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/caif/caif_virtio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c
+index 7fea00c7ca8a6..c60386bf2d1a4 100644
+--- a/drivers/net/caif/caif_virtio.c
++++ b/drivers/net/caif/caif_virtio.c
+@@ -745,7 +745,7 @@ static int cfv_probe(struct virtio_device *vdev)
+
+ if (cfv->vr_rx)
+ vdev->vringh_config->del_vrhs(cfv->vdev);
+- if (cfv->vdev)
++ if (cfv->vq_tx)
+ vdev->config->del_vqs(cfv->vdev);
+ free_netdev(netdev);
+ return err;
+--
+2.39.5
+
--- /dev/null
+From f29a95314ca4fe41d86d1f96758ad27f280374d9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 19 Feb 2025 11:53:16 -0800
+Subject: coredump: Only sort VMAs when core_sort_vma sysctl is set
+
+From: Kees Cook <kees@kernel.org>
+
+[ Upstream commit 39ec9eaaa165d297d008d1fa385748430bd18e4d ]
+
+The sorting of VMAs by size in commit 7d442a33bfe8 ("binfmt_elf: Dump
+smaller VMAs first in ELF cores") breaks elfutils[1]. Instead, sort
+based on the setting of the new sysctl, core_sort_vma, which defaults
+to 0, no sorting.
+
+Reported-by: Michael Stapelberg <michael@stapelberg.ch>
+Closes: https://lore.kernel.org/all/20250218085407.61126-1-michael@stapelberg.de/ [1]
+Fixes: 7d442a33bfe8 ("binfmt_elf: Dump smaller VMAs first in ELF cores")
+Signed-off-by: Kees Cook <kees@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/admin-guide/sysctl/kernel.rst | 11 +++++++++++
+ fs/coredump.c | 15 +++++++++++++--
+ 2 files changed, 24 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
+index f8bc1630eba05..fa21cdd610b21 100644
+--- a/Documentation/admin-guide/sysctl/kernel.rst
++++ b/Documentation/admin-guide/sysctl/kernel.rst
+@@ -212,6 +212,17 @@ pid>/``).
+ This value defaults to 0.
+
+
++core_sort_vma
++=============
++
++The default coredump writes VMAs in address order. By setting
++``core_sort_vma`` to 1, VMAs will be written from smallest size
++to largest size. This is known to break at least elfutils, but
++can be handy when dealing with very large (and truncated)
++coredumps where the more useful debugging details are included
++in the smaller VMAs.
++
++
+ core_uses_pid
+ =============
+
+diff --git a/fs/coredump.c b/fs/coredump.c
+index 45737b43dda5c..2b8c36c9660c5 100644
+--- a/fs/coredump.c
++++ b/fs/coredump.c
+@@ -63,6 +63,7 @@ static void free_vma_snapshot(struct coredump_params *cprm);
+
+ static int core_uses_pid;
+ static unsigned int core_pipe_limit;
++static unsigned int core_sort_vma;
+ static char core_pattern[CORENAME_MAX_SIZE] = "core";
+ static int core_name_size = CORENAME_MAX_SIZE;
+ unsigned int core_file_note_size_limit = CORE_FILE_NOTE_SIZE_DEFAULT;
+@@ -1025,6 +1026,15 @@ static struct ctl_table coredump_sysctls[] = {
+ .extra1 = (unsigned int *)&core_file_note_size_min,
+ .extra2 = (unsigned int *)&core_file_note_size_max,
+ },
++ {
++ .procname = "core_sort_vma",
++ .data = &core_sort_vma,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_douintvec_minmax,
++ .extra1 = SYSCTL_ZERO,
++ .extra2 = SYSCTL_ONE,
++ },
+ };
+
+ static int __init init_fs_coredump_sysctls(void)
+@@ -1255,8 +1265,9 @@ static bool dump_vma_snapshot(struct coredump_params *cprm)
+ cprm->vma_data_size += m->dump_size;
+ }
+
+- sort(cprm->vma_meta, cprm->vma_count, sizeof(*cprm->vma_meta),
+- cmp_vma_size, NULL);
++ if (core_sort_vma)
++ sort(cprm->vma_meta, cprm->vma_count, sizeof(*cprm->vma_meta),
++ cmp_vma_size, NULL);
+
+ return true;
+ }
+--
+2.39.5
+
--- /dev/null
+From 01d9e3170ca37461c27fd01c433a6ec7450d5842 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2024 09:12:02 +0200
+Subject: drm: Add client-agnostic setup helper
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit d07fdf9225922d3e36ebd13ccab3df62b1ccdab3 ]
+
+DRM may support multiple in-kernel clients that run as soon as a DRM
+driver has been registered. To select the client(s) in a single place,
+introduce drm_client_setup().
+
+Drivers that call the new helper automatically instantiate the kernel's
+configured default clients. Only fbdev emulation is currently supported.
+Later versions can add support for DRM-based logging, a boot logo or even
+a console.
+
+Some drivers handle the color mode for clients internally. Provide the
+helper drm_client_setup_with_color_mode() for them.
+
+Using the new interface requires the driver to select
+DRM_CLIENT_SELECTION in its Kconfig. For now this only enables the
+client-setup helpers if the fbdev client has been configured by the
+user. A future patchset will further modularize client support and
+rework DRM_CLIENT_SELECTION to select the correct dependencies for
+all its clients.
+
+v5:
+- add CONFIG_DRM_CLIENT_SELECTION und DRM_CLIENT_SETUP
+v4:
+- fix docs for drm_client_setup_with_fourcc() (Geert)
+v3:
+- fix build error
+v2:
+- add drm_client_setup_with_fourcc() (Laurent)
+- push default-format handling into actual clients
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240924071734.98201-5-tzimmermann@suse.de
+Stable-dep-of: 6b481ab0e685 ("drm/nouveau: select FW caching")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/Kconfig | 12 ++++++
+ drivers/gpu/drm/Makefile | 2 +
+ drivers/gpu/drm/drm_client_setup.c | 66 ++++++++++++++++++++++++++++++
+ include/drm/drm_client_setup.h | 26 ++++++++++++
+ 4 files changed, 106 insertions(+)
+ create mode 100644 drivers/gpu/drm/drm_client_setup.c
+ create mode 100644 include/drm/drm_client_setup.h
+
+diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
+index 7408ea8caacc3..ae53f26da945f 100644
+--- a/drivers/gpu/drm/Kconfig
++++ b/drivers/gpu/drm/Kconfig
+@@ -211,6 +211,18 @@ config DRM_DEBUG_MODESET_LOCK
+
+ If in doubt, say "N".
+
++config DRM_CLIENT_SELECTION
++ bool
++ depends on DRM
++ select DRM_CLIENT_SETUP if DRM_FBDEV_EMULATION
++ help
++ Drivers that support in-kernel DRM clients have to select this
++ option.
++
++config DRM_CLIENT_SETUP
++ bool
++ depends on DRM_CLIENT_SELECTION
++
+ config DRM_FBDEV_EMULATION
+ bool "Enable legacy fbdev support for your modesetting driver"
+ depends on DRM
+diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
+index a1dd0909fa38b..1ec44529447a7 100644
+--- a/drivers/gpu/drm/Makefile
++++ b/drivers/gpu/drm/Makefile
+@@ -144,6 +144,8 @@ drm_kms_helper-y := \
+ drm_rect.o \
+ drm_self_refresh_helper.o \
+ drm_simple_kms_helper.o
++drm_kms_helper-$(CONFIG_DRM_CLIENT_SETUP) += \
++ drm_client_setup.o
+ drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
+ drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += \
+ drm_fbdev_client.o \
+diff --git a/drivers/gpu/drm/drm_client_setup.c b/drivers/gpu/drm/drm_client_setup.c
+new file mode 100644
+index 0000000000000..5969c4ffe31ba
+--- /dev/null
++++ b/drivers/gpu/drm/drm_client_setup.c
+@@ -0,0 +1,66 @@
++// SPDX-License-Identifier: MIT
++
++#include <drm/drm_client_setup.h>
++#include <drm/drm_device.h>
++#include <drm/drm_fbdev_client.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_print.h>
++
++/**
++ * drm_client_setup() - Setup in-kernel DRM clients
++ * @dev: DRM device
++ * @format: Preferred pixel format for the device. Use NULL, unless
++ * there is clearly a driver-preferred format.
++ *
++ * This function sets up the in-kernel DRM clients. Restore, hotplug
++ * events and teardown are all taken care of.
++ *
++ * Drivers should call drm_client_setup() after registering the new
++ * DRM device with drm_dev_register(). This function is safe to call
++ * even when there are no connectors present. Setup will be retried
++ * on the next hotplug event.
++ *
++ * The clients are destroyed by drm_dev_unregister().
++ */
++void drm_client_setup(struct drm_device *dev, const struct drm_format_info *format)
++{
++ int ret;
++
++ ret = drm_fbdev_client_setup(dev, format);
++ if (ret)
++ drm_warn(dev, "Failed to set up DRM client; error %d\n", ret);
++}
++EXPORT_SYMBOL(drm_client_setup);
++
++/**
++ * drm_client_setup_with_fourcc() - Setup in-kernel DRM clients for color mode
++ * @dev: DRM device
++ * @fourcc: Preferred pixel format as 4CC code for the device
++ *
++ * This function sets up the in-kernel DRM clients. It is equivalent
++ * to drm_client_setup(), but expects a 4CC code as second argument.
++ */
++void drm_client_setup_with_fourcc(struct drm_device *dev, u32 fourcc)
++{
++ drm_client_setup(dev, drm_format_info(fourcc));
++}
++EXPORT_SYMBOL(drm_client_setup_with_fourcc);
++
++/**
++ * drm_client_setup_with_color_mode() - Setup in-kernel DRM clients for color mode
++ * @dev: DRM device
++ * @color_mode: Preferred color mode for the device
++ *
++ * This function sets up the in-kernel DRM clients. It is equivalent
++ * to drm_client_setup(), but expects a color mode as second argument.
++ *
++ * Do not use this function in new drivers. Prefer drm_client_setup() with a
++ * format of NULL.
++ */
++void drm_client_setup_with_color_mode(struct drm_device *dev, unsigned int color_mode)
++{
++ u32 fourcc = drm_driver_color_mode_format(dev, color_mode);
++
++ drm_client_setup_with_fourcc(dev, fourcc);
++}
++EXPORT_SYMBOL(drm_client_setup_with_color_mode);
+diff --git a/include/drm/drm_client_setup.h b/include/drm/drm_client_setup.h
+new file mode 100644
+index 0000000000000..46aab3fb46be5
+--- /dev/null
++++ b/include/drm/drm_client_setup.h
+@@ -0,0 +1,26 @@
++/* SPDX-License-Identifier: MIT */
++
++#ifndef DRM_CLIENT_SETUP_H
++#define DRM_CLIENT_SETUP_H
++
++#include <linux/types.h>
++
++struct drm_device;
++struct drm_format_info;
++
++#if defined(CONFIG_DRM_CLIENT_SETUP)
++void drm_client_setup(struct drm_device *dev, const struct drm_format_info *format);
++void drm_client_setup_with_fourcc(struct drm_device *dev, u32 fourcc);
++void drm_client_setup_with_color_mode(struct drm_device *dev, unsigned int color_mode);
++#else
++static inline void drm_client_setup(struct drm_device *dev,
++ const struct drm_format_info *format)
++{ }
++static inline void drm_client_setup_with_fourcc(struct drm_device *dev, u32 fourcc)
++{ }
++static inline void drm_client_setup_with_color_mode(struct drm_device *dev,
++ unsigned int color_mode)
++{ }
++#endif
++
++#endif
+--
+2.39.5
+
--- /dev/null
+From 6c844463318f5b6ee6ed99921ab946635394bb38 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2024 09:12:01 +0200
+Subject: drm/fbdev: Add memory-agnostic fbdev client
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 5d08c44e47b9d41366714552bdd374ac4b595591 ]
+
+Add an fbdev client that can work with any memory manager. The
+client implementation is the same as existing code in fbdev-dma or
+fbdev-shmem.
+
+Provide struct drm_driver.fbdev_probe for the new client to allocate
+the surface GEM buffer. The new callback replaces fb_probe of struct
+drm_fb_helper_funcs, which does the same.
+
+To use the new client, DRM drivers set fbdev_probe in their struct
+drm_driver instance and call drm_fbdev_client_setup(). Probing and
+creating the fbdev surface buffer is now independent from the other
+operations in struct drm_fb_helper. For the pixel format, the fbdev
+client either uses a specified format, the value in preferred_depth
+or 32-bit RGB.
+
+v2:
+- test for struct drm_fb_helper.funcs for NULL (Sui)
+- respect struct drm_mode_config.preferred_depth for default format
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240924071734.98201-4-tzimmermann@suse.de
+Stable-dep-of: 6b481ab0e685 ("drm/nouveau: select FW caching")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/Makefile | 4 +-
+ drivers/gpu/drm/drm_fb_helper.c | 15 +--
+ drivers/gpu/drm/drm_fbdev_client.c | 141 +++++++++++++++++++++++++++++
+ include/drm/drm_drv.h | 18 ++++
+ include/drm/drm_fbdev_client.h | 19 ++++
+ 5 files changed, 190 insertions(+), 7 deletions(-)
+ create mode 100644 drivers/gpu/drm/drm_fbdev_client.c
+ create mode 100644 include/drm/drm_fbdev_client.h
+
+diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
+index 84746054c721a..a1dd0909fa38b 100644
+--- a/drivers/gpu/drm/Makefile
++++ b/drivers/gpu/drm/Makefile
+@@ -145,7 +145,9 @@ drm_kms_helper-y := \
+ drm_self_refresh_helper.o \
+ drm_simple_kms_helper.o
+ drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
+-drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
++drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += \
++ drm_fbdev_client.o \
++ drm_fb_helper.o
+ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
+
+ #
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index 29b0cf3a3fb19..b15ddbd65e7b5 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -492,8 +492,8 @@ EXPORT_SYMBOL(drm_fb_helper_init);
+ * @fb_helper: driver-allocated fbdev helper
+ *
+ * A helper to alloc fb_info and the member cmap. Called by the driver
+- * within the fb_probe fb_helper callback function. Drivers do not
+- * need to release the allocated fb_info structure themselves, this is
++ * within the struct &drm_driver.fbdev_probe callback function. Drivers do
++ * not need to release the allocated fb_info structure themselves, this is
+ * automatically done when calling drm_fb_helper_fini().
+ *
+ * RETURNS:
+@@ -1610,7 +1610,7 @@ static int drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
+
+ /*
+ * Allocates the backing storage and sets up the fbdev info structure through
+- * the ->fb_probe callback.
++ * the ->fbdev_probe callback.
+ */
+ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
+ {
+@@ -1628,7 +1628,10 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
+ }
+
+ /* push down into drivers */
+- ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
++ if (dev->driver->fbdev_probe)
++ ret = dev->driver->fbdev_probe(fb_helper, &sizes);
++ else if (fb_helper->funcs)
++ ret = fb_helper->funcs->fb_probe(fb_helper, &sizes);
+ if (ret < 0)
+ return ret;
+
+@@ -1700,7 +1703,7 @@ static void drm_fb_helper_fill_var(struct fb_info *info,
+ * instance and the drm framebuffer allocated in &drm_fb_helper.fb.
+ *
+ * Drivers should call this (or their equivalent setup code) from their
+- * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev
++ * &drm_driver.fbdev_probe callback after having allocated the fbdev
+ * backing storage framebuffer.
+ */
+ void drm_fb_helper_fill_info(struct fb_info *info,
+@@ -1856,7 +1859,7 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)
+ * Note that this also registers the fbdev and so allows userspace to call into
+ * the driver through the fbdev interfaces.
+ *
+- * This function will call down into the &drm_fb_helper_funcs.fb_probe callback
++ * This function will call down into the &drm_driver.fbdev_probe callback
+ * to let the driver allocate and initialize the fbdev info structure and the
+ * drm framebuffer used to back the fbdev. drm_fb_helper_fill_info() is provided
+ * as a helper to setup simple default values for the fbdev info structure.
+diff --git a/drivers/gpu/drm/drm_fbdev_client.c b/drivers/gpu/drm/drm_fbdev_client.c
+new file mode 100644
+index 0000000000000..a09382afe2fb6
+--- /dev/null
++++ b/drivers/gpu/drm/drm_fbdev_client.c
+@@ -0,0 +1,141 @@
++// SPDX-License-Identifier: MIT
++
++#include <drm/drm_client.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_fbdev_client.h>
++#include <drm/drm_fb_helper.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_print.h>
++
++/*
++ * struct drm_client_funcs
++ */
++
++static void drm_fbdev_client_unregister(struct drm_client_dev *client)
++{
++ struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
++
++ if (fb_helper->info) {
++ drm_fb_helper_unregister_info(fb_helper);
++ } else {
++ drm_client_release(&fb_helper->client);
++ drm_fb_helper_unprepare(fb_helper);
++ kfree(fb_helper);
++ }
++}
++
++static int drm_fbdev_client_restore(struct drm_client_dev *client)
++{
++ drm_fb_helper_lastclose(client->dev);
++
++ return 0;
++}
++
++static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
++{
++ struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
++ struct drm_device *dev = client->dev;
++ int ret;
++
++ if (dev->fb_helper)
++ return drm_fb_helper_hotplug_event(dev->fb_helper);
++
++ ret = drm_fb_helper_init(dev, fb_helper);
++ if (ret)
++ goto err_drm_err;
++
++ if (!drm_drv_uses_atomic_modeset(dev))
++ drm_helper_disable_unused_functions(dev);
++
++ ret = drm_fb_helper_initial_config(fb_helper);
++ if (ret)
++ goto err_drm_fb_helper_fini;
++
++ return 0;
++
++err_drm_fb_helper_fini:
++ drm_fb_helper_fini(fb_helper);
++err_drm_err:
++ drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret);
++ return ret;
++}
++
++static const struct drm_client_funcs drm_fbdev_client_funcs = {
++ .owner = THIS_MODULE,
++ .unregister = drm_fbdev_client_unregister,
++ .restore = drm_fbdev_client_restore,
++ .hotplug = drm_fbdev_client_hotplug,
++};
++
++/**
++ * drm_fbdev_client_setup() - Setup fbdev emulation
++ * @dev: DRM device
++ * @format: Preferred color format for the device. DRM_FORMAT_XRGB8888
++ * is used if this is zero.
++ *
++ * This function sets up fbdev emulation. Restore, hotplug events and
++ * teardown are all taken care of. Drivers that do suspend/resume need
++ * to call drm_fb_helper_set_suspend_unlocked() themselves. Simple
++ * drivers might use drm_mode_config_helper_suspend().
++ *
++ * This function is safe to call even when there are no connectors present.
++ * Setup will be retried on the next hotplug event.
++ *
++ * The fbdev client is destroyed by drm_dev_unregister().
++ *
++ * Returns:
++ * 0 on success, or a negative errno code otherwise.
++ */
++int drm_fbdev_client_setup(struct drm_device *dev, const struct drm_format_info *format)
++{
++ struct drm_fb_helper *fb_helper;
++ unsigned int color_mode;
++ int ret;
++
++ /* TODO: Use format info throughout DRM */
++ if (format) {
++ unsigned int bpp = drm_format_info_bpp(format, 0);
++
++ switch (bpp) {
++ case 16:
++ color_mode = format->depth; // could also be 15
++ break;
++ default:
++ color_mode = bpp;
++ }
++ } else {
++ switch (dev->mode_config.preferred_depth) {
++ case 0:
++ case 24:
++ color_mode = 32;
++ break;
++ default:
++ color_mode = dev->mode_config.preferred_depth;
++ }
++ }
++
++ drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
++ drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
++
++ fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
++ if (!fb_helper)
++ return -ENOMEM;
++ drm_fb_helper_prepare(dev, fb_helper, color_mode, NULL);
++
++ ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs);
++ if (ret) {
++ drm_err(dev, "Failed to register client: %d\n", ret);
++ goto err_drm_client_init;
++ }
++
++ drm_client_register(&fb_helper->client);
++
++ return 0;
++
++err_drm_client_init:
++ drm_fb_helper_unprepare(fb_helper);
++ kfree(fb_helper);
++ return ret;
++}
++EXPORT_SYMBOL(drm_fbdev_client_setup);
+diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
+index 02ea4e3248fdf..36a606af4ba1d 100644
+--- a/include/drm/drm_drv.h
++++ b/include/drm/drm_drv.h
+@@ -34,6 +34,8 @@
+
+ #include <drm/drm_device.h>
+
++struct drm_fb_helper;
++struct drm_fb_helper_surface_size;
+ struct drm_file;
+ struct drm_gem_object;
+ struct drm_master;
+@@ -366,6 +368,22 @@ struct drm_driver {
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset);
+
++ /**
++ * @fbdev_probe
++ *
++ * Allocates and initialize the fb_info structure for fbdev emulation.
++ * Furthermore it also needs to allocate the DRM framebuffer used to
++ * back the fbdev.
++ *
++ * This callback is mandatory for fbdev support.
++ *
++ * Returns:
++ *
++ * 0 on success ot a negative error code otherwise.
++ */
++ int (*fbdev_probe)(struct drm_fb_helper *fbdev_helper,
++ struct drm_fb_helper_surface_size *sizes);
++
+ /**
+ * @show_fdinfo:
+ *
+diff --git a/include/drm/drm_fbdev_client.h b/include/drm/drm_fbdev_client.h
+new file mode 100644
+index 0000000000000..e11a5614f127c
+--- /dev/null
++++ b/include/drm/drm_fbdev_client.h
+@@ -0,0 +1,19 @@
++/* SPDX-License-Identifier: MIT */
++
++#ifndef DRM_FBDEV_CLIENT_H
++#define DRM_FBDEV_CLIENT_H
++
++struct drm_device;
++struct drm_format_info;
++
++#ifdef CONFIG_DRM_FBDEV_EMULATION
++int drm_fbdev_client_setup(struct drm_device *dev, const struct drm_format_info *format);
++#else
++static inline int drm_fbdev_client_setup(struct drm_device *dev,
++ const struct drm_format_info *format)
++{
++ return 0;
++}
++#endif
++
++#endif
+--
+2.39.5
+
--- /dev/null
+From d8e77b713f50210c6455c32e15515ab975896d32 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2024 09:11:59 +0200
+Subject: drm/fbdev-helper: Move color-mode lookup into 4CC format helper
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit eb1f4adf9101573fc2347978a60d71c4f1176cca ]
+
+The color mode as specified on the kernel command line gives the user's
+preferred color depth and number of bits per pixel. Move the
+color-mode-to-format conversion from fbdev helpers into a 4CC helper,
+so that it can be shared among DRM clients.
+
+v2:
+- fix grammar in commit message (Laurent)
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240924071734.98201-2-tzimmermann@suse.de
+Stable-dep-of: 6b481ab0e685 ("drm/nouveau: select FW caching")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fb_helper.c | 70 +++++++--------------------------
+ drivers/gpu/drm/drm_fourcc.c | 30 +++++++++++++-
+ include/drm/drm_fourcc.h | 1 +
+ 3 files changed, 45 insertions(+), 56 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index eaac2e5726e75..29b0cf3a3fb19 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -1443,67 +1443,27 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
+ EXPORT_SYMBOL(drm_fb_helper_pan_display);
+
+ static uint32_t drm_fb_helper_find_format(struct drm_fb_helper *fb_helper, const uint32_t *formats,
+- size_t format_count, uint32_t bpp, uint32_t depth)
++ size_t format_count, unsigned int color_mode)
+ {
+ struct drm_device *dev = fb_helper->dev;
+ uint32_t format;
+ size_t i;
+
+- /*
+- * Do not consider YUV or other complicated formats
+- * for framebuffers. This means only legacy formats
+- * are supported (fmt->depth is a legacy field), but
+- * the framebuffer emulation can only deal with such
+- * formats, specifically RGB/BGA formats.
+- */
+- format = drm_mode_legacy_fb_format(bpp, depth);
+- if (!format)
+- goto err;
++ format = drm_driver_color_mode_format(dev, color_mode);
++ if (!format) {
++ drm_info(dev, "unsupported color mode of %d\n", color_mode);
++ return DRM_FORMAT_INVALID;
++ }
+
+ for (i = 0; i < format_count; ++i) {
+ if (formats[i] == format)
+ return format;
+ }
+-
+-err:
+- /* We found nothing. */
+- drm_warn(dev, "bpp/depth value of %u/%u not supported\n", bpp, depth);
++ drm_warn(dev, "format %p4cc not supported\n", &format);
+
+ return DRM_FORMAT_INVALID;
+ }
+
+-static uint32_t drm_fb_helper_find_color_mode_format(struct drm_fb_helper *fb_helper,
+- const uint32_t *formats, size_t format_count,
+- unsigned int color_mode)
+-{
+- struct drm_device *dev = fb_helper->dev;
+- uint32_t bpp, depth;
+-
+- switch (color_mode) {
+- case 1:
+- case 2:
+- case 4:
+- case 8:
+- case 16:
+- case 24:
+- bpp = depth = color_mode;
+- break;
+- case 15:
+- bpp = 16;
+- depth = 15;
+- break;
+- case 32:
+- bpp = 32;
+- depth = 24;
+- break;
+- default:
+- drm_info(dev, "unsupported color mode of %d\n", color_mode);
+- return DRM_FORMAT_INVALID;
+- }
+-
+- return drm_fb_helper_find_format(fb_helper, formats, format_count, bpp, depth);
+-}
+-
+ static int __drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
+ struct drm_fb_helper_surface_size *sizes)
+ {
+@@ -1533,10 +1493,10 @@ static int __drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
+ if (!cmdline_mode->bpp_specified)
+ continue;
+
+- surface_format = drm_fb_helper_find_color_mode_format(fb_helper,
+- plane->format_types,
+- plane->format_count,
+- cmdline_mode->bpp);
++ surface_format = drm_fb_helper_find_format(fb_helper,
++ plane->format_types,
++ plane->format_count,
++ cmdline_mode->bpp);
+ if (surface_format != DRM_FORMAT_INVALID)
+ break; /* found supported format */
+ }
+@@ -1546,10 +1506,10 @@ static int __drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
+ break; /* found supported format */
+
+ /* try preferred color mode */
+- surface_format = drm_fb_helper_find_color_mode_format(fb_helper,
+- plane->format_types,
+- plane->format_count,
+- fb_helper->preferred_bpp);
++ surface_format = drm_fb_helper_find_format(fb_helper,
++ plane->format_types,
++ plane->format_count,
++ fb_helper->preferred_bpp);
+ if (surface_format != DRM_FORMAT_INVALID)
+ break; /* found supported format */
+ }
+diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
+index 193cf8ed79128..3a94ca211f9ce 100644
+--- a/drivers/gpu/drm/drm_fourcc.c
++++ b/drivers/gpu/drm/drm_fourcc.c
+@@ -36,7 +36,6 @@
+ * @depth: bit depth per pixel
+ *
+ * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
+- * Useful in fbdev emulation code, since that deals in those values.
+ */
+ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
+ {
+@@ -140,6 +139,35 @@ uint32_t drm_driver_legacy_fb_format(struct drm_device *dev,
+ }
+ EXPORT_SYMBOL(drm_driver_legacy_fb_format);
+
++/**
++ * drm_driver_color_mode_format - Compute DRM 4CC code from color mode
++ * @dev: DRM device
++ * @color_mode: command-line color mode
++ *
++ * Computes a DRM 4CC pixel format code for the given color mode using
++ * drm_driver_color_mode(). The color mode is in the format used and the
++ * kernel command line. It specifies the number of bits per pixel
++ * and color depth in a single value.
++ *
++ * Useful in fbdev emulation code, since that deals in those values. The
++ * helper does not consider YUV or other complicated formats. This means
++ * only legacy formats are supported (fmt->depth is a legacy field), but
++ * the framebuffer emulation can only deal with such formats, specifically
++ * RGB/BGA formats.
++ */
++uint32_t drm_driver_color_mode_format(struct drm_device *dev, unsigned int color_mode)
++{
++ switch (color_mode) {
++ case 15:
++ return drm_driver_legacy_fb_format(dev, 16, 15);
++ case 32:
++ return drm_driver_legacy_fb_format(dev, 32, 24);
++ default:
++ return drm_driver_legacy_fb_format(dev, color_mode, color_mode);
++ }
++}
++EXPORT_SYMBOL(drm_driver_color_mode_format);
++
+ /*
+ * Internal function to query information for a given format. See
+ * drm_format_info() for the public API.
+diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h
+index ccf91daa43070..c3f4405d66629 100644
+--- a/include/drm/drm_fourcc.h
++++ b/include/drm/drm_fourcc.h
+@@ -313,6 +313,7 @@ drm_get_format_info(struct drm_device *dev,
+ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth);
+ uint32_t drm_driver_legacy_fb_format(struct drm_device *dev,
+ uint32_t bpp, uint32_t depth);
++uint32_t drm_driver_color_mode_format(struct drm_device *dev, unsigned int color_mode);
+ unsigned int drm_format_info_block_width(const struct drm_format_info *info,
+ int plane);
+ unsigned int drm_format_info_block_height(const struct drm_format_info *info,
+--
+2.39.5
+
--- /dev/null
+From 4e42c9756a5035cf8c09cee117a38e247a340416 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2024 09:13:02 +0200
+Subject: drm/fbdev-ttm: Support struct drm_driver.fbdev_probe
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit c7c1b9e1d52b0a0dbb0ee552efdc3360c0f5363c ]
+
+Rework fbdev probing to support fbdev_probe in struct drm_driver
+and reimplement the old fb_probe callback on top of it. Provide an
+initializer macro for struct drm_driver that sets the callback
+according to the kernel configuration.
+
+This change allows the common fbdev client to run on top of TTM-
+based DRM drivers.
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Acked-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240924071734.98201-65-tzimmermann@suse.de
+Stable-dep-of: 6b481ab0e685 ("drm/nouveau: select FW caching")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fbdev_ttm.c | 142 +++++++++++++++++---------------
+ include/drm/drm_fbdev_ttm.h | 13 +++
+ 2 files changed, 90 insertions(+), 65 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fbdev_ttm.c b/drivers/gpu/drm/drm_fbdev_ttm.c
+index 119ffb28aaf95..d799cbe944cd3 100644
+--- a/drivers/gpu/drm/drm_fbdev_ttm.c
++++ b/drivers/gpu/drm/drm_fbdev_ttm.c
+@@ -71,71 +71,7 @@ static const struct fb_ops drm_fbdev_ttm_fb_ops = {
+ static int drm_fbdev_ttm_helper_fb_probe(struct drm_fb_helper *fb_helper,
+ struct drm_fb_helper_surface_size *sizes)
+ {
+- struct drm_client_dev *client = &fb_helper->client;
+- struct drm_device *dev = fb_helper->dev;
+- struct drm_client_buffer *buffer;
+- struct fb_info *info;
+- size_t screen_size;
+- void *screen_buffer;
+- u32 format;
+- int ret;
+-
+- drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
+- sizes->surface_width, sizes->surface_height,
+- sizes->surface_bpp);
+-
+- format = drm_driver_legacy_fb_format(dev, sizes->surface_bpp,
+- sizes->surface_depth);
+- buffer = drm_client_framebuffer_create(client, sizes->surface_width,
+- sizes->surface_height, format);
+- if (IS_ERR(buffer))
+- return PTR_ERR(buffer);
+-
+- fb_helper->buffer = buffer;
+- fb_helper->fb = buffer->fb;
+-
+- screen_size = buffer->gem->size;
+- screen_buffer = vzalloc(screen_size);
+- if (!screen_buffer) {
+- ret = -ENOMEM;
+- goto err_drm_client_framebuffer_delete;
+- }
+-
+- info = drm_fb_helper_alloc_info(fb_helper);
+- if (IS_ERR(info)) {
+- ret = PTR_ERR(info);
+- goto err_vfree;
+- }
+-
+- drm_fb_helper_fill_info(info, fb_helper, sizes);
+-
+- info->fbops = &drm_fbdev_ttm_fb_ops;
+-
+- /* screen */
+- info->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
+- info->screen_buffer = screen_buffer;
+- info->fix.smem_len = screen_size;
+-
+- /* deferred I/O */
+- fb_helper->fbdefio.delay = HZ / 20;
+- fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
+-
+- info->fbdefio = &fb_helper->fbdefio;
+- ret = fb_deferred_io_init(info);
+- if (ret)
+- goto err_drm_fb_helper_release_info;
+-
+- return 0;
+-
+-err_drm_fb_helper_release_info:
+- drm_fb_helper_release_info(fb_helper);
+-err_vfree:
+- vfree(screen_buffer);
+-err_drm_client_framebuffer_delete:
+- fb_helper->fb = NULL;
+- fb_helper->buffer = NULL;
+- drm_client_framebuffer_delete(buffer);
+- return ret;
++ return drm_fbdev_ttm_driver_fbdev_probe(fb_helper, sizes);
+ }
+
+ static void drm_fbdev_ttm_damage_blit_real(struct drm_fb_helper *fb_helper,
+@@ -240,6 +176,82 @@ static const struct drm_fb_helper_funcs drm_fbdev_ttm_helper_funcs = {
+ .fb_dirty = drm_fbdev_ttm_helper_fb_dirty,
+ };
+
++/*
++ * struct drm_driver
++ */
++
++int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
++ struct drm_fb_helper_surface_size *sizes)
++{
++ struct drm_client_dev *client = &fb_helper->client;
++ struct drm_device *dev = fb_helper->dev;
++ struct drm_client_buffer *buffer;
++ struct fb_info *info;
++ size_t screen_size;
++ void *screen_buffer;
++ u32 format;
++ int ret;
++
++ drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
++ sizes->surface_width, sizes->surface_height,
++ sizes->surface_bpp);
++
++ format = drm_driver_legacy_fb_format(dev, sizes->surface_bpp,
++ sizes->surface_depth);
++ buffer = drm_client_framebuffer_create(client, sizes->surface_width,
++ sizes->surface_height, format);
++ if (IS_ERR(buffer))
++ return PTR_ERR(buffer);
++
++ fb_helper->funcs = &drm_fbdev_ttm_helper_funcs;
++ fb_helper->buffer = buffer;
++ fb_helper->fb = buffer->fb;
++
++ screen_size = buffer->gem->size;
++ screen_buffer = vzalloc(screen_size);
++ if (!screen_buffer) {
++ ret = -ENOMEM;
++ goto err_drm_client_framebuffer_delete;
++ }
++
++ info = drm_fb_helper_alloc_info(fb_helper);
++ if (IS_ERR(info)) {
++ ret = PTR_ERR(info);
++ goto err_vfree;
++ }
++
++ drm_fb_helper_fill_info(info, fb_helper, sizes);
++
++ info->fbops = &drm_fbdev_ttm_fb_ops;
++
++ /* screen */
++ info->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
++ info->screen_buffer = screen_buffer;
++ info->fix.smem_len = screen_size;
++
++ /* deferred I/O */
++ fb_helper->fbdefio.delay = HZ / 20;
++ fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
++
++ info->fbdefio = &fb_helper->fbdefio;
++ ret = fb_deferred_io_init(info);
++ if (ret)
++ goto err_drm_fb_helper_release_info;
++
++ return 0;
++
++err_drm_fb_helper_release_info:
++ drm_fb_helper_release_info(fb_helper);
++err_vfree:
++ vfree(screen_buffer);
++err_drm_client_framebuffer_delete:
++ fb_helper->fb = NULL;
++ fb_helper->buffer = NULL;
++ drm_client_framebuffer_delete(buffer);
++ return ret;
++}
++EXPORT_SYMBOL(drm_fbdev_ttm_driver_fbdev_probe);
++
+ static void drm_fbdev_ttm_client_unregister(struct drm_client_dev *client)
+ {
+ struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+diff --git a/include/drm/drm_fbdev_ttm.h b/include/drm/drm_fbdev_ttm.h
+index 9e6c3bdf35376..243685d02eb13 100644
+--- a/include/drm/drm_fbdev_ttm.h
++++ b/include/drm/drm_fbdev_ttm.h
+@@ -3,11 +3,24 @@
+ #ifndef DRM_FBDEV_TTM_H
+ #define DRM_FBDEV_TTM_H
+
++#include <linux/stddef.h>
++
+ struct drm_device;
++struct drm_fb_helper;
++struct drm_fb_helper_surface_size;
+
+ #ifdef CONFIG_DRM_FBDEV_EMULATION
++int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
++ struct drm_fb_helper_surface_size *sizes);
++
++#define DRM_FBDEV_TTM_DRIVER_OPS \
++ .fbdev_probe = drm_fbdev_ttm_driver_fbdev_probe
++
+ void drm_fbdev_ttm_setup(struct drm_device *dev, unsigned int preferred_bpp);
+ #else
++#define DRM_FBDEV_TTM_DRIVER_OPS \
++ .fbdev_probe = NULL
++
+ static inline void drm_fbdev_ttm_setup(struct drm_device *dev, unsigned int preferred_bpp)
+ { }
+ #endif
+--
+2.39.5
+
--- /dev/null
+From 987065af8355936425f752120e87bcf0c1dfc963 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 16 Sep 2024 18:29:57 +0300
+Subject: drm/i915/color: Extract intel_color_modeset()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ville Syrjälä <ville.syrjala@linux.intel.com>
+
+[ Upstream commit 84d2d0430f0833cdf52a3d051906add051f20ef0 ]
+
+We always perform the same steps to program color management
+stuff during a full modeset. Extract that code to a helper
+to avoid duplication.
+
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240916152958.17332-2-ville.syrjala@linux.intel.com
+Reviewed-by: Luca Coelho <luciano.coelho@intel.com>
+Stable-dep-of: 30bfc151f0c1 ("drm/xe: Remove double pageflip")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_color.c | 17 ++++++++++
+ drivers/gpu/drm/i915/display/intel_color.h | 1 +
+ drivers/gpu/drm/i915/display/intel_display.c | 33 +++-----------------
+ 3 files changed, 22 insertions(+), 29 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c
+index ec55cb651d449..808bece71c247 100644
+--- a/drivers/gpu/drm/i915/display/intel_color.c
++++ b/drivers/gpu/drm/i915/display/intel_color.c
+@@ -1912,6 +1912,23 @@ void intel_color_post_update(const struct intel_crtc_state *crtc_state)
+ i915->display.funcs.color->color_post_update(crtc_state);
+ }
+
++void intel_color_modeset(const struct intel_crtc_state *crtc_state)
++{
++ struct intel_display *display = to_intel_display(crtc_state);
++
++ intel_color_load_luts(crtc_state);
++ intel_color_commit_noarm(crtc_state);
++ intel_color_commit_arm(crtc_state);
++
++ if (DISPLAY_VER(display) < 9) {
++ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
++ struct intel_plane *plane = to_intel_plane(crtc->base.primary);
++
++ /* update DSPCNTR to configure gamma/csc for pipe bottom color */
++ plane->disable_arm(plane, crtc_state);
++ }
++}
++
+ void intel_color_prepare_commit(struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+ {
+diff --git a/drivers/gpu/drm/i915/display/intel_color.h b/drivers/gpu/drm/i915/display/intel_color.h
+index 79f230a1709ad..ab3aaec06a2ac 100644
+--- a/drivers/gpu/drm/i915/display/intel_color.h
++++ b/drivers/gpu/drm/i915/display/intel_color.h
+@@ -28,6 +28,7 @@ void intel_color_commit_noarm(const struct intel_crtc_state *crtc_state);
+ void intel_color_commit_arm(const struct intel_crtc_state *crtc_state);
+ void intel_color_post_update(const struct intel_crtc_state *crtc_state);
+ void intel_color_load_luts(const struct intel_crtc_state *crtc_state);
++void intel_color_modeset(const struct intel_crtc_state *crtc_state);
+ void intel_color_get_config(struct intel_crtc_state *crtc_state);
+ bool intel_color_lut_equal(const struct intel_crtc_state *crtc_state,
+ const struct drm_property_blob *blob1,
+diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
+index 2c6d0da8a16f8..ac5febd076e10 100644
+--- a/drivers/gpu/drm/i915/display/intel_display.c
++++ b/drivers/gpu/drm/i915/display/intel_display.c
+@@ -1502,14 +1502,6 @@ static void intel_encoders_update_pipe(struct intel_atomic_state *state,
+ }
+ }
+
+-static void intel_disable_primary_plane(const struct intel_crtc_state *crtc_state)
+-{
+- struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+- struct intel_plane *plane = to_intel_plane(crtc->base.primary);
+-
+- plane->disable_arm(plane, crtc_state);
+-}
+-
+ static void ilk_configure_cpu_transcoder(const struct intel_crtc_state *crtc_state)
+ {
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+@@ -1575,11 +1567,7 @@ static void ilk_crtc_enable(struct intel_atomic_state *state,
+ * On ILK+ LUT must be loaded before the pipe is running but with
+ * clocks enabled
+ */
+- intel_color_load_luts(new_crtc_state);
+- intel_color_commit_noarm(new_crtc_state);
+- intel_color_commit_arm(new_crtc_state);
+- /* update DSPCNTR to configure gamma for pipe bottom color */
+- intel_disable_primary_plane(new_crtc_state);
++ intel_color_modeset(new_crtc_state);
+
+ intel_initial_watermarks(state, crtc);
+ intel_enable_transcoder(new_crtc_state);
+@@ -1741,12 +1729,7 @@ static void hsw_crtc_enable(struct intel_atomic_state *state,
+ * On ILK+ LUT must be loaded before the pipe is running but with
+ * clocks enabled
+ */
+- intel_color_load_luts(pipe_crtc_state);
+- intel_color_commit_noarm(pipe_crtc_state);
+- intel_color_commit_arm(pipe_crtc_state);
+- /* update DSPCNTR to configure gamma/csc for pipe bottom color */
+- if (DISPLAY_VER(dev_priv) < 9)
+- intel_disable_primary_plane(pipe_crtc_state);
++ intel_color_modeset(pipe_crtc_state);
+
+ hsw_set_linetime_wm(pipe_crtc_state);
+
+@@ -2147,11 +2130,7 @@ static void valleyview_crtc_enable(struct intel_atomic_state *state,
+
+ i9xx_pfit_enable(new_crtc_state);
+
+- intel_color_load_luts(new_crtc_state);
+- intel_color_commit_noarm(new_crtc_state);
+- intel_color_commit_arm(new_crtc_state);
+- /* update DSPCNTR to configure gamma for pipe bottom color */
+- intel_disable_primary_plane(new_crtc_state);
++ intel_color_modeset(new_crtc_state);
+
+ intel_initial_watermarks(state, crtc);
+ intel_enable_transcoder(new_crtc_state);
+@@ -2187,11 +2166,7 @@ static void i9xx_crtc_enable(struct intel_atomic_state *state,
+
+ i9xx_pfit_enable(new_crtc_state);
+
+- intel_color_load_luts(new_crtc_state);
+- intel_color_commit_noarm(new_crtc_state);
+- intel_color_commit_arm(new_crtc_state);
+- /* update DSPCNTR to configure gamma for pipe bottom color */
+- intel_disable_primary_plane(new_crtc_state);
++ intel_color_modeset(new_crtc_state);
+
+ if (!intel_initial_watermarks(state, crtc))
+ intel_update_watermarks(dev_priv);
+--
+2.39.5
+
--- /dev/null
+From f03e7cca22f4bb50cae98840f91fcf1e6d780a54 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Sep 2024 20:04:13 +0300
+Subject: drm/i915: Plumb 'dsb' all way to the plane hooks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ville Syrjälä <ville.syrjala@linux.intel.com>
+
+[ Upstream commit 01389846f7d61d262cc92d42ad4d1a25730e3eff ]
+
+We need to be able to do both MMIO and DSB based pipe/plane
+programming. To that end plumb the 'dsb' all way from the top
+into the plane commit hooks.
+
+The compiler appears smart enough to combine the branches from
+all the back-to-back register writes into a single branch.
+So the generated asm ends up looking more or less like this:
+plane_hook()
+{
+ if (dsb) {
+ intel_dsb_reg_write();
+ intel_dsb_reg_write();
+ ...
+ } else {
+ intel_de_write_fw();
+ intel_de_write_fw();
+ ...
+ }
+}
+which seems like a reasonably efficient way to do this.
+
+An alternative I was also considering is some kind of closure
+(register write function + display vs. dsb pointer passed to it).
+That does result is smaller code as there are no branches anymore,
+but having each register access go via function pointer sounds
+less efficient.
+
+Not that I actually measured the overhead of either approach yet.
+Also the reg_rw tracepoint seems to be making a huge mess of the
+generated code for the mmio path. And additionally there's some
+kind of IS_GSI_REG() hack in __raw_uncore_read() which ends up
+generating a pointless branch for every mmio register access.
+So looks like there might be quite a bit of room for improvement
+in the mmio path still.
+
+Reviewed-by: Animesh Manna <animesh.manna@intel.com>
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240930170415.23841-12-ville.syrjala@linux.intel.com
+Stable-dep-of: 30bfc151f0c1 ("drm/xe: Remove double pageflip")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/i9xx_plane.c | 22 +-
+ .../gpu/drm/i915/display/intel_atomic_plane.c | 49 +--
+ .../gpu/drm/i915/display/intel_atomic_plane.h | 19 +-
+ drivers/gpu/drm/i915/display/intel_color.c | 2 +-
+ drivers/gpu/drm/i915/display/intel_cursor.c | 101 +++---
+ drivers/gpu/drm/i915/display/intel_de.h | 11 +
+ drivers/gpu/drm/i915/display/intel_display.c | 25 +-
+ .../drm/i915/display/intel_display_types.h | 16 +-
+ drivers/gpu/drm/i915/display/intel_sprite.c | 27 +-
+ .../drm/i915/display/skl_universal_plane.c | 307 ++++++++++--------
+ drivers/gpu/drm/xe/display/xe_plane_initial.c | 2 +-
+ 11 files changed, 334 insertions(+), 247 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/i9xx_plane.c b/drivers/gpu/drm/i915/display/i9xx_plane.c
+index 9447f7229b608..17a1e3801a85c 100644
+--- a/drivers/gpu/drm/i915/display/i9xx_plane.c
++++ b/drivers/gpu/drm/i915/display/i9xx_plane.c
+@@ -416,7 +416,8 @@ static int i9xx_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
+ return DIV_ROUND_UP(pixel_rate * num, den);
+ }
+
+-static void i9xx_plane_update_noarm(struct intel_plane *plane,
++static void i9xx_plane_update_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -444,7 +445,8 @@ static void i9xx_plane_update_noarm(struct intel_plane *plane,
+ }
+ }
+
+-static void i9xx_plane_update_arm(struct intel_plane *plane,
++static void i9xx_plane_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -507,7 +509,8 @@ static void i9xx_plane_update_arm(struct intel_plane *plane,
+ intel_plane_ggtt_offset(plane_state) + dspaddr_offset);
+ }
+
+-static void i830_plane_update_arm(struct intel_plane *plane,
++static void i830_plane_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -517,11 +520,12 @@ static void i830_plane_update_arm(struct intel_plane *plane,
+ * Additional breakage on i830 causes register reads to return
+ * the last latched value instead of the last written value [ALM026].
+ */
+- i9xx_plane_update_noarm(plane, crtc_state, plane_state);
+- i9xx_plane_update_arm(plane, crtc_state, plane_state);
++ i9xx_plane_update_noarm(dsb, plane, crtc_state, plane_state);
++ i9xx_plane_update_arm(dsb, plane, crtc_state, plane_state);
+ }
+
+-static void i9xx_plane_disable_arm(struct intel_plane *plane,
++static void i9xx_plane_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+@@ -549,7 +553,8 @@ static void i9xx_plane_disable_arm(struct intel_plane *plane,
+ }
+
+ static void
+-g4x_primary_async_flip(struct intel_plane *plane,
++g4x_primary_async_flip(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ bool async_flip)
+@@ -569,7 +574,8 @@ g4x_primary_async_flip(struct intel_plane *plane,
+ }
+
+ static void
+-vlv_primary_async_flip(struct intel_plane *plane,
++vlv_primary_async_flip(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ bool async_flip)
+diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
+index e979786aa5cf3..5c2a7987cccb4 100644
+--- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c
++++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
+@@ -790,7 +790,8 @@ skl_next_plane_to_commit(struct intel_atomic_state *state,
+ return NULL;
+ }
+
+-void intel_plane_update_noarm(struct intel_plane *plane,
++void intel_plane_update_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -799,10 +800,11 @@ void intel_plane_update_noarm(struct intel_plane *plane,
+ trace_intel_plane_update_noarm(plane, crtc);
+
+ if (plane->update_noarm)
+- plane->update_noarm(plane, crtc_state, plane_state);
++ plane->update_noarm(dsb, plane, crtc_state, plane_state);
+ }
+
+-void intel_plane_async_flip(struct intel_plane *plane,
++void intel_plane_async_flip(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ bool async_flip)
+@@ -810,34 +812,37 @@ void intel_plane_async_flip(struct intel_plane *plane,
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ trace_intel_plane_async_flip(plane, crtc, async_flip);
+- plane->async_flip(plane, crtc_state, plane_state, async_flip);
++ plane->async_flip(dsb, plane, crtc_state, plane_state, async_flip);
+ }
+
+-void intel_plane_update_arm(struct intel_plane *plane,
++void intel_plane_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ if (crtc_state->do_async_flip && plane->async_flip) {
+- intel_plane_async_flip(plane, crtc_state, plane_state, true);
++ intel_plane_async_flip(dsb, plane, crtc_state, plane_state, true);
+ return;
+ }
+
+ trace_intel_plane_update_arm(plane, crtc);
+- plane->update_arm(plane, crtc_state, plane_state);
++ plane->update_arm(dsb, plane, crtc_state, plane_state);
+ }
+
+-void intel_plane_disable_arm(struct intel_plane *plane,
++void intel_plane_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ trace_intel_plane_disable_arm(plane, crtc);
+- plane->disable_arm(plane, crtc_state);
++ plane->disable_arm(dsb, plane, crtc_state);
+ }
+
+-void intel_crtc_planes_update_noarm(struct intel_atomic_state *state,
++void intel_crtc_planes_update_noarm(struct intel_dsb *dsb,
++ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+ {
+ struct intel_crtc_state *new_crtc_state =
+@@ -862,11 +867,13 @@ void intel_crtc_planes_update_noarm(struct intel_atomic_state *state,
+ /* TODO: for mailbox updates this should be skipped */
+ if (new_plane_state->uapi.visible ||
+ new_plane_state->planar_slave)
+- intel_plane_update_noarm(plane, new_crtc_state, new_plane_state);
++ intel_plane_update_noarm(dsb, plane,
++ new_crtc_state, new_plane_state);
+ }
+ }
+
+-static void skl_crtc_planes_update_arm(struct intel_atomic_state *state,
++static void skl_crtc_planes_update_arm(struct intel_dsb *dsb,
++ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+ {
+ struct intel_crtc_state *old_crtc_state =
+@@ -893,13 +900,14 @@ static void skl_crtc_planes_update_arm(struct intel_atomic_state *state,
+ */
+ if (new_plane_state->uapi.visible ||
+ new_plane_state->planar_slave)
+- intel_plane_update_arm(plane, new_crtc_state, new_plane_state);
++ intel_plane_update_arm(dsb, plane, new_crtc_state, new_plane_state);
+ else
+- intel_plane_disable_arm(plane, new_crtc_state);
++ intel_plane_disable_arm(dsb, plane, new_crtc_state);
+ }
+ }
+
+-static void i9xx_crtc_planes_update_arm(struct intel_atomic_state *state,
++static void i9xx_crtc_planes_update_arm(struct intel_dsb *dsb,
++ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+ {
+ struct intel_crtc_state *new_crtc_state =
+@@ -919,21 +927,22 @@ static void i9xx_crtc_planes_update_arm(struct intel_atomic_state *state,
+ * would have to be called here as well.
+ */
+ if (new_plane_state->uapi.visible)
+- intel_plane_update_arm(plane, new_crtc_state, new_plane_state);
++ intel_plane_update_arm(dsb, plane, new_crtc_state, new_plane_state);
+ else
+- intel_plane_disable_arm(plane, new_crtc_state);
++ intel_plane_disable_arm(dsb, plane, new_crtc_state);
+ }
+ }
+
+-void intel_crtc_planes_update_arm(struct intel_atomic_state *state,
++void intel_crtc_planes_update_arm(struct intel_dsb *dsb,
++ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+ {
+ struct drm_i915_private *i915 = to_i915(state->base.dev);
+
+ if (DISPLAY_VER(i915) >= 9)
+- skl_crtc_planes_update_arm(state, crtc);
++ skl_crtc_planes_update_arm(dsb, state, crtc);
+ else
+- i9xx_crtc_planes_update_arm(state, crtc);
++ i9xx_crtc_planes_update_arm(dsb, state, crtc);
+ }
+
+ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state,
+diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.h b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
+index 6c4fe35964650..0f982f452ff39 100644
+--- a/drivers/gpu/drm/i915/display/intel_atomic_plane.h
++++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
+@@ -14,6 +14,7 @@ struct drm_rect;
+ struct intel_atomic_state;
+ struct intel_crtc;
+ struct intel_crtc_state;
++struct intel_dsb;
+ struct intel_plane;
+ struct intel_plane_state;
+ enum plane_id;
+@@ -32,26 +33,32 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
+ struct intel_crtc *crtc);
+ void intel_plane_copy_hw_state(struct intel_plane_state *plane_state,
+ const struct intel_plane_state *from_plane_state);
+-void intel_plane_async_flip(struct intel_plane *plane,
++void intel_plane_async_flip(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ bool async_flip);
+-void intel_plane_update_noarm(struct intel_plane *plane,
++void intel_plane_update_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state);
+-void intel_plane_update_arm(struct intel_plane *plane,
++void intel_plane_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state);
+-void intel_plane_disable_arm(struct intel_plane *plane,
++void intel_plane_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state);
+ struct intel_plane *intel_plane_alloc(void);
+ void intel_plane_free(struct intel_plane *plane);
+ struct drm_plane_state *intel_plane_duplicate_state(struct drm_plane *plane);
+ void intel_plane_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state);
+-void intel_crtc_planes_update_noarm(struct intel_atomic_state *state,
++void intel_crtc_planes_update_noarm(struct intel_dsb *dsb,
++ struct intel_atomic_state *state,
+ struct intel_crtc *crtc);
+-void intel_crtc_planes_update_arm(struct intel_atomic_state *state,
++void intel_crtc_planes_update_arm(struct intel_dsb *dsbx,
++ struct intel_atomic_state *state,
+ struct intel_crtc *crtc);
+ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_state,
+ struct intel_crtc_state *crtc_state,
+diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c
+index 808bece71c247..1fbe3cd452c13 100644
+--- a/drivers/gpu/drm/i915/display/intel_color.c
++++ b/drivers/gpu/drm/i915/display/intel_color.c
+@@ -1925,7 +1925,7 @@ void intel_color_modeset(const struct intel_crtc_state *crtc_state)
+ struct intel_plane *plane = to_intel_plane(crtc->base.primary);
+
+ /* update DSPCNTR to configure gamma/csc for pipe bottom color */
+- plane->disable_arm(plane, crtc_state);
++ plane->disable_arm(NULL, plane, crtc_state);
+ }
+ }
+
+diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c
+index 9ad53e1cbbd06..aeadb834d3328 100644
+--- a/drivers/gpu/drm/i915/display/intel_cursor.c
++++ b/drivers/gpu/drm/i915/display/intel_cursor.c
+@@ -275,7 +275,8 @@ static int i845_check_cursor(struct intel_crtc_state *crtc_state,
+ }
+
+ /* TODO: split into noarm+arm pair */
+-static void i845_cursor_update_arm(struct intel_plane *plane,
++static void i845_cursor_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -315,10 +316,11 @@ static void i845_cursor_update_arm(struct intel_plane *plane,
+ }
+ }
+
+-static void i845_cursor_disable_arm(struct intel_plane *plane,
++static void i845_cursor_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+- i845_cursor_update_arm(plane, crtc_state, NULL);
++ i845_cursor_update_arm(dsb, plane, crtc_state, NULL);
+ }
+
+ static bool i845_cursor_get_hw_state(struct intel_plane *plane,
+@@ -527,22 +529,25 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state,
+ return 0;
+ }
+
+-static void i9xx_cursor_disable_sel_fetch_arm(struct intel_plane *plane,
++static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+- struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum pipe pipe = plane->pipe;
+
+ if (!crtc_state->enable_psr2_sel_fetch)
+ return;
+
+- intel_de_write_fw(dev_priv, SEL_FETCH_CUR_CTL(pipe), 0);
++ intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0);
+ }
+
+-static void wa_16021440873(struct intel_plane *plane,
++static void wa_16021440873(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ u32 ctl = plane_state->ctl;
+ int et_y_position = drm_rect_height(&crtc_state->pipe_src) + 1;
+@@ -551,16 +556,18 @@ static void wa_16021440873(struct intel_plane *plane,
+ ctl &= ~MCURSOR_MODE_MASK;
+ ctl |= MCURSOR_MODE_64_2B;
+
+- intel_de_write_fw(dev_priv, SEL_FETCH_CUR_CTL(pipe), ctl);
++ intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), ctl);
+
+- intel_de_write(dev_priv, CURPOS_ERLY_TPT(dev_priv, pipe),
+- CURSOR_POS_Y(et_y_position));
++ intel_de_write_dsb(display, dsb, CURPOS_ERLY_TPT(dev_priv, pipe),
++ CURSOR_POS_Y(et_y_position));
+ }
+
+-static void i9xx_cursor_update_sel_fetch_arm(struct intel_plane *plane,
++static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ enum pipe pipe = plane->pipe;
+
+@@ -571,19 +578,17 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_plane *plane,
+ if (crtc_state->enable_psr2_su_region_et) {
+ u32 val = intel_cursor_position(crtc_state, plane_state,
+ true);
+- intel_de_write_fw(dev_priv,
+- CURPOS_ERLY_TPT(dev_priv, pipe),
+- val);
++
++ intel_de_write_dsb(display, dsb, CURPOS_ERLY_TPT(dev_priv, pipe), val);
+ }
+
+- intel_de_write_fw(dev_priv, SEL_FETCH_CUR_CTL(pipe),
+- plane_state->ctl);
++ intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), plane_state->ctl);
+ } else {
+ /* Wa_16021440873 */
+ if (crtc_state->enable_psr2_su_region_et)
+- wa_16021440873(plane, crtc_state, plane_state);
++ wa_16021440873(dsb, plane, crtc_state, plane_state);
+ else
+- i9xx_cursor_disable_sel_fetch_arm(plane, crtc_state);
++ i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
+ }
+ }
+
+@@ -610,9 +615,11 @@ static u32 skl_cursor_wm_reg_val(const struct skl_wm_level *level)
+ return val;
+ }
+
+-static void skl_write_cursor_wm(struct intel_plane *plane,
++static void skl_write_cursor_wm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ struct drm_i915_private *i915 = to_i915(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+@@ -622,30 +629,32 @@ static void skl_write_cursor_wm(struct intel_plane *plane,
+ int level;
+
+ for (level = 0; level < i915->display.wm.num_levels; level++)
+- intel_de_write_fw(i915, CUR_WM(pipe, level),
+- skl_cursor_wm_reg_val(skl_plane_wm_level(pipe_wm, plane_id, level)));
++ intel_de_write_dsb(display, dsb, CUR_WM(pipe, level),
++ skl_cursor_wm_reg_val(skl_plane_wm_level(pipe_wm, plane_id, level)));
+
+- intel_de_write_fw(i915, CUR_WM_TRANS(pipe),
+- skl_cursor_wm_reg_val(skl_plane_trans_wm(pipe_wm, plane_id)));
++ intel_de_write_dsb(display, dsb, CUR_WM_TRANS(pipe),
++ skl_cursor_wm_reg_val(skl_plane_trans_wm(pipe_wm, plane_id)));
+
+ if (HAS_HW_SAGV_WM(i915)) {
+ const struct skl_plane_wm *wm = &pipe_wm->planes[plane_id];
+
+- intel_de_write_fw(i915, CUR_WM_SAGV(pipe),
+- skl_cursor_wm_reg_val(&wm->sagv.wm0));
+- intel_de_write_fw(i915, CUR_WM_SAGV_TRANS(pipe),
+- skl_cursor_wm_reg_val(&wm->sagv.trans_wm));
++ intel_de_write_dsb(display, dsb, CUR_WM_SAGV(pipe),
++ skl_cursor_wm_reg_val(&wm->sagv.wm0));
++ intel_de_write_dsb(display, dsb, CUR_WM_SAGV_TRANS(pipe),
++ skl_cursor_wm_reg_val(&wm->sagv.trans_wm));
+ }
+
+- intel_de_write_fw(i915, CUR_BUF_CFG(pipe),
+- skl_cursor_ddb_reg_val(ddb));
++ intel_de_write_dsb(display, dsb, CUR_BUF_CFG(pipe),
++ skl_cursor_ddb_reg_val(ddb));
+ }
+
+ /* TODO: split into noarm+arm pair */
+-static void i9xx_cursor_update_arm(struct intel_plane *plane,
++static void i9xx_cursor_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ enum pipe pipe = plane->pipe;
+ u32 cntl = 0, base = 0, pos = 0, fbc_ctl = 0;
+@@ -685,38 +694,36 @@ static void i9xx_cursor_update_arm(struct intel_plane *plane,
+ */
+
+ if (DISPLAY_VER(dev_priv) >= 9)
+- skl_write_cursor_wm(plane, crtc_state);
++ skl_write_cursor_wm(dsb, plane, crtc_state);
+
+ if (plane_state)
+- i9xx_cursor_update_sel_fetch_arm(plane, crtc_state,
+- plane_state);
++ i9xx_cursor_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state);
+ else
+- i9xx_cursor_disable_sel_fetch_arm(plane, crtc_state);
++ i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
+
+ if (plane->cursor.base != base ||
+ plane->cursor.size != fbc_ctl ||
+ plane->cursor.cntl != cntl) {
+ if (HAS_CUR_FBC(dev_priv))
+- intel_de_write_fw(dev_priv,
+- CUR_FBC_CTL(dev_priv, pipe),
+- fbc_ctl);
+- intel_de_write_fw(dev_priv, CURCNTR(dev_priv, pipe), cntl);
+- intel_de_write_fw(dev_priv, CURPOS(dev_priv, pipe), pos);
+- intel_de_write_fw(dev_priv, CURBASE(dev_priv, pipe), base);
++ intel_de_write_dsb(display, dsb, CUR_FBC_CTL(dev_priv, pipe), fbc_ctl);
++ intel_de_write_dsb(display, dsb, CURCNTR(dev_priv, pipe), cntl);
++ intel_de_write_dsb(display, dsb, CURPOS(dev_priv, pipe), pos);
++ intel_de_write_dsb(display, dsb, CURBASE(dev_priv, pipe), base);
+
+ plane->cursor.base = base;
+ plane->cursor.size = fbc_ctl;
+ plane->cursor.cntl = cntl;
+ } else {
+- intel_de_write_fw(dev_priv, CURPOS(dev_priv, pipe), pos);
+- intel_de_write_fw(dev_priv, CURBASE(dev_priv, pipe), base);
++ intel_de_write_dsb(display, dsb, CURPOS(dev_priv, pipe), pos);
++ intel_de_write_dsb(display, dsb, CURBASE(dev_priv, pipe), base);
+ }
+ }
+
+-static void i9xx_cursor_disable_arm(struct intel_plane *plane,
++static void i9xx_cursor_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+- i9xx_cursor_update_arm(plane, crtc_state, NULL);
++ i9xx_cursor_update_arm(dsb, plane, crtc_state, NULL);
+ }
+
+ static bool i9xx_cursor_get_hw_state(struct intel_plane *plane,
+@@ -905,10 +912,10 @@ intel_legacy_cursor_update(struct drm_plane *_plane,
+ }
+
+ if (new_plane_state->uapi.visible) {
+- intel_plane_update_noarm(plane, crtc_state, new_plane_state);
+- intel_plane_update_arm(plane, crtc_state, new_plane_state);
++ intel_plane_update_noarm(NULL, plane, crtc_state, new_plane_state);
++ intel_plane_update_arm(NULL, plane, crtc_state, new_plane_state);
+ } else {
+- intel_plane_disable_arm(plane, crtc_state);
++ intel_plane_disable_arm(NULL, plane, crtc_state);
+ }
+
+ local_irq_enable();
+diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h
+index e881bfeafb47d..e017cd4a81685 100644
+--- a/drivers/gpu/drm/i915/display/intel_de.h
++++ b/drivers/gpu/drm/i915/display/intel_de.h
+@@ -8,6 +8,7 @@
+
+ #include "i915_drv.h"
+ #include "i915_trace.h"
++#include "intel_dsb.h"
+ #include "intel_uncore.h"
+
+ static inline struct intel_uncore *__to_uncore(struct intel_display *display)
+@@ -233,4 +234,14 @@ __intel_de_write_notrace(struct intel_display *display, i915_reg_t reg,
+ }
+ #define intel_de_write_notrace(p,...) __intel_de_write_notrace(__to_intel_display(p), __VA_ARGS__)
+
++static __always_inline void
++intel_de_write_dsb(struct intel_display *display, struct intel_dsb *dsb,
++ i915_reg_t reg, u32 val)
++{
++ if (dsb)
++ intel_dsb_reg_write(dsb, reg, val);
++ else
++ intel_de_write_fw(display, reg, val);
++}
++
+ #endif /* __INTEL_DE_H__ */
+diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
+index ac5febd076e10..3039ee03e1c7a 100644
+--- a/drivers/gpu/drm/i915/display/intel_display.c
++++ b/drivers/gpu/drm/i915/display/intel_display.c
+@@ -135,7 +135,8 @@
+ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state);
+ static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state);
+ static void hsw_set_transconf(const struct intel_crtc_state *crtc_state);
+-static void bdw_set_pipe_misc(const struct intel_crtc_state *crtc_state);
++static void bdw_set_pipe_misc(struct intel_dsb *dsb,
++ const struct intel_crtc_state *crtc_state);
+
+ /* returns HPLL frequency in kHz */
+ int vlv_get_hpll_vco(struct drm_i915_private *dev_priv)
+@@ -715,7 +716,7 @@ void intel_plane_disable_noatomic(struct intel_crtc *crtc,
+ if (DISPLAY_VER(dev_priv) == 2 && !crtc_state->active_planes)
+ intel_set_cpu_fifo_underrun_reporting(dev_priv, crtc->pipe, false);
+
+- intel_plane_disable_arm(plane, crtc_state);
++ intel_plane_disable_arm(NULL, plane, crtc_state);
+ intel_crtc_wait_for_next_vblank(crtc);
+ }
+
+@@ -1172,8 +1173,8 @@ static void intel_crtc_async_flip_disable_wa(struct intel_atomic_state *state,
+ * Apart from the async flip bit we want to
+ * preserve the old state for the plane.
+ */
+- intel_plane_async_flip(plane, old_crtc_state,
+- old_plane_state, false);
++ intel_plane_async_flip(NULL, plane,
++ old_crtc_state, old_plane_state, false);
+ need_vbl_wait = true;
+ }
+ }
+@@ -1315,7 +1316,7 @@ static void intel_crtc_disable_planes(struct intel_atomic_state *state,
+ !(update_mask & BIT(plane->id)))
+ continue;
+
+- intel_plane_disable_arm(plane, new_crtc_state);
++ intel_plane_disable_arm(NULL, plane, new_crtc_state);
+
+ if (old_plane_state->uapi.visible)
+ fb_bits |= plane->frontbuffer_bit;
+@@ -1704,7 +1705,7 @@ static void hsw_crtc_enable(struct intel_atomic_state *state,
+ intel_set_pipe_src_size(pipe_crtc_state);
+
+ if (DISPLAY_VER(dev_priv) >= 9 || IS_BROADWELL(dev_priv))
+- bdw_set_pipe_misc(pipe_crtc_state);
++ bdw_set_pipe_misc(NULL, pipe_crtc_state);
+ }
+
+ if (!transcoder_is_dsi(cpu_transcoder))
+@@ -3221,9 +3222,11 @@ static void hsw_set_transconf(const struct intel_crtc_state *crtc_state)
+ intel_de_posting_read(dev_priv, TRANSCONF(dev_priv, cpu_transcoder));
+ }
+
+-static void bdw_set_pipe_misc(const struct intel_crtc_state *crtc_state)
++static void bdw_set_pipe_misc(struct intel_dsb *dsb,
++ const struct intel_crtc_state *crtc_state)
+ {
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
++ struct intel_display *display = to_intel_display(crtc->base.dev);
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+ u32 val = 0;
+
+@@ -3268,7 +3271,7 @@ static void bdw_set_pipe_misc(const struct intel_crtc_state *crtc_state)
+ if (IS_BROADWELL(dev_priv))
+ val |= PIPE_MISC_PSR_MASK_SPRITE_ENABLE;
+
+- intel_de_write(dev_priv, PIPE_MISC(crtc->pipe), val);
++ intel_de_write_dsb(display, dsb, PIPE_MISC(crtc->pipe), val);
+ }
+
+ int bdw_get_pipe_misc_bpp(struct intel_crtc *crtc)
+@@ -6821,7 +6824,7 @@ static void commit_pipe_pre_planes(struct intel_atomic_state *state,
+ intel_color_commit_arm(new_crtc_state);
+
+ if (DISPLAY_VER(dev_priv) >= 9 || IS_BROADWELL(dev_priv))
+- bdw_set_pipe_misc(new_crtc_state);
++ bdw_set_pipe_misc(NULL, new_crtc_state);
+
+ if (intel_crtc_needs_fastset(new_crtc_state))
+ intel_pipe_fastset(old_crtc_state, new_crtc_state);
+@@ -6921,7 +6924,7 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
+ intel_crtc_needs_color_update(new_crtc_state))
+ intel_color_commit_noarm(new_crtc_state);
+
+- intel_crtc_planes_update_noarm(state, crtc);
++ intel_crtc_planes_update_noarm(NULL, state, crtc);
+ }
+
+ static void intel_update_crtc(struct intel_atomic_state *state,
+@@ -6937,7 +6940,7 @@ static void intel_update_crtc(struct intel_atomic_state *state,
+
+ commit_pipe_pre_planes(state, crtc);
+
+- intel_crtc_planes_update_arm(state, crtc);
++ intel_crtc_planes_update_arm(NULL, state, crtc);
+
+ commit_pipe_post_planes(state, crtc);
+
+diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
+index f29e5dc3db910..3e24d2e90d3cf 100644
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -1036,6 +1036,10 @@ struct intel_csc_matrix {
+ u16 postoff[3];
+ };
+
++void intel_io_mmio_fw_write(void *ctx, i915_reg_t reg, u32 val);
++
++typedef void (*intel_io_reg_write)(void *ctx, i915_reg_t reg, u32 val);
++
+ struct intel_crtc_state {
+ /*
+ * uapi (drm) state. This is the software state shown to userspace.
+@@ -1578,22 +1582,26 @@ struct intel_plane {
+ u32 pixel_format, u64 modifier,
+ unsigned int rotation);
+ /* Write all non-self arming plane registers */
+- void (*update_noarm)(struct intel_plane *plane,
++ void (*update_noarm)(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state);
+ /* Write all self-arming plane registers */
+- void (*update_arm)(struct intel_plane *plane,
++ void (*update_arm)(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state);
+ /* Disable the plane, must arm */
+- void (*disable_arm)(struct intel_plane *plane,
++ void (*disable_arm)(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state);
+ bool (*get_hw_state)(struct intel_plane *plane, enum pipe *pipe);
+ int (*check_plane)(struct intel_crtc_state *crtc_state,
+ struct intel_plane_state *plane_state);
+ int (*min_cdclk)(const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state);
+- void (*async_flip)(struct intel_plane *plane,
++ void (*async_flip)(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ bool async_flip);
+diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c
+index e657b09ede999..e6fadcef58e06 100644
+--- a/drivers/gpu/drm/i915/display/intel_sprite.c
++++ b/drivers/gpu/drm/i915/display/intel_sprite.c
+@@ -378,7 +378,8 @@ static void vlv_sprite_update_gamma(const struct intel_plane_state *plane_state)
+ }
+
+ static void
+-vlv_sprite_update_noarm(struct intel_plane *plane,
++vlv_sprite_update_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -399,7 +400,8 @@ vlv_sprite_update_noarm(struct intel_plane *plane,
+ }
+
+ static void
+-vlv_sprite_update_arm(struct intel_plane *plane,
++vlv_sprite_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -449,7 +451,8 @@ vlv_sprite_update_arm(struct intel_plane *plane,
+ }
+
+ static void
+-vlv_sprite_disable_arm(struct intel_plane *plane,
++vlv_sprite_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+ struct intel_display *display = to_intel_display(plane->base.dev);
+@@ -795,7 +798,8 @@ static void ivb_sprite_update_gamma(const struct intel_plane_state *plane_state)
+ }
+
+ static void
+-ivb_sprite_update_noarm(struct intel_plane *plane,
++ivb_sprite_update_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -826,7 +830,8 @@ ivb_sprite_update_noarm(struct intel_plane *plane,
+ }
+
+ static void
+-ivb_sprite_update_arm(struct intel_plane *plane,
++ivb_sprite_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -874,7 +879,8 @@ ivb_sprite_update_arm(struct intel_plane *plane,
+ }
+
+ static void
+-ivb_sprite_disable_arm(struct intel_plane *plane,
++ivb_sprite_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+ struct intel_display *display = to_intel_display(plane->base.dev);
+@@ -1133,7 +1139,8 @@ static void ilk_sprite_update_gamma(const struct intel_plane_state *plane_state)
+ }
+
+ static void
+-g4x_sprite_update_noarm(struct intel_plane *plane,
++g4x_sprite_update_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -1162,7 +1169,8 @@ g4x_sprite_update_noarm(struct intel_plane *plane,
+ }
+
+ static void
+-g4x_sprite_update_arm(struct intel_plane *plane,
++g4x_sprite_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+@@ -1206,7 +1214,8 @@ g4x_sprite_update_arm(struct intel_plane *plane,
+ }
+
+ static void
+-g4x_sprite_disable_arm(struct intel_plane *plane,
++g4x_sprite_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+ struct intel_display *display = to_intel_display(plane->base.dev);
+diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
+index 62a5287ea1d9c..7f77a76309bd5 100644
+--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
++++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
+@@ -589,11 +589,11 @@ static u32 skl_plane_min_alignment(struct intel_plane *plane,
+ * in full-range YCbCr.
+ */
+ static void
+-icl_program_input_csc(struct intel_plane *plane,
+- const struct intel_crtc_state *crtc_state,
++icl_program_input_csc(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_plane_state *plane_state)
+ {
+- struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum pipe pipe = plane->pipe;
+ enum plane_id plane_id = plane->id;
+
+@@ -637,31 +637,31 @@ icl_program_input_csc(struct intel_plane *plane,
+ };
+ const u16 *csc = input_csc_matrix[plane_state->hw.color_encoding];
+
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 0),
+- ROFF(csc[0]) | GOFF(csc[1]));
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 1),
+- BOFF(csc[2]));
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 2),
+- ROFF(csc[3]) | GOFF(csc[4]));
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 3),
+- BOFF(csc[5]));
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 4),
+- ROFF(csc[6]) | GOFF(csc[7]));
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 5),
+- BOFF(csc[8]));
+-
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 0),
+- PREOFF_YUV_TO_RGB_HI);
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 1),
+- PREOFF_YUV_TO_RGB_ME);
+- intel_de_write_fw(dev_priv, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 2),
+- PREOFF_YUV_TO_RGB_LO);
+- intel_de_write_fw(dev_priv,
+- PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 0), 0x0);
+- intel_de_write_fw(dev_priv,
+- PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 1), 0x0);
+- intel_de_write_fw(dev_priv,
+- PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 2), 0x0);
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 0),
++ ROFF(csc[0]) | GOFF(csc[1]));
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 1),
++ BOFF(csc[2]));
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 2),
++ ROFF(csc[3]) | GOFF(csc[4]));
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 3),
++ BOFF(csc[5]));
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 4),
++ ROFF(csc[6]) | GOFF(csc[7]));
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_COEFF(pipe, plane_id, 5),
++ BOFF(csc[8]));
++
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 0),
++ PREOFF_YUV_TO_RGB_HI);
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 1),
++ PREOFF_YUV_TO_RGB_ME);
++ intel_de_write_dsb(display, dsb, PLANE_INPUT_CSC_PREOFF(pipe, plane_id, 2),
++ PREOFF_YUV_TO_RGB_LO);
++ intel_de_write_dsb(display, dsb,
++ PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 0), 0x0);
++ intel_de_write_dsb(display, dsb,
++ PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 1), 0x0);
++ intel_de_write_dsb(display, dsb,
++ PLANE_INPUT_CSC_POSTOFF(pipe, plane_id, 2), 0x0);
+ }
+
+ static unsigned int skl_plane_stride_mult(const struct drm_framebuffer *fb,
+@@ -715,9 +715,11 @@ static u32 skl_plane_wm_reg_val(const struct skl_wm_level *level)
+ return val;
+ }
+
+-static void skl_write_plane_wm(struct intel_plane *plane,
++static void skl_write_plane_wm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ struct drm_i915_private *i915 = to_i915(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+@@ -729,71 +731,75 @@ static void skl_write_plane_wm(struct intel_plane *plane,
+ int level;
+
+ for (level = 0; level < i915->display.wm.num_levels; level++)
+- intel_de_write_fw(i915, PLANE_WM(pipe, plane_id, level),
+- skl_plane_wm_reg_val(skl_plane_wm_level(pipe_wm, plane_id, level)));
++ intel_de_write_dsb(display, dsb, PLANE_WM(pipe, plane_id, level),
++ skl_plane_wm_reg_val(skl_plane_wm_level(pipe_wm, plane_id, level)));
+
+- intel_de_write_fw(i915, PLANE_WM_TRANS(pipe, plane_id),
+- skl_plane_wm_reg_val(skl_plane_trans_wm(pipe_wm, plane_id)));
++ intel_de_write_dsb(display, dsb, PLANE_WM_TRANS(pipe, plane_id),
++ skl_plane_wm_reg_val(skl_plane_trans_wm(pipe_wm, plane_id)));
+
+ if (HAS_HW_SAGV_WM(i915)) {
+ const struct skl_plane_wm *wm = &pipe_wm->planes[plane_id];
+
+- intel_de_write_fw(i915, PLANE_WM_SAGV(pipe, plane_id),
+- skl_plane_wm_reg_val(&wm->sagv.wm0));
+- intel_de_write_fw(i915, PLANE_WM_SAGV_TRANS(pipe, plane_id),
+- skl_plane_wm_reg_val(&wm->sagv.trans_wm));
++ intel_de_write_dsb(display, dsb, PLANE_WM_SAGV(pipe, plane_id),
++ skl_plane_wm_reg_val(&wm->sagv.wm0));
++ intel_de_write_dsb(display, dsb, PLANE_WM_SAGV_TRANS(pipe, plane_id),
++ skl_plane_wm_reg_val(&wm->sagv.trans_wm));
+ }
+
+- intel_de_write_fw(i915, PLANE_BUF_CFG(pipe, plane_id),
+- skl_plane_ddb_reg_val(ddb));
++ intel_de_write_dsb(display, dsb, PLANE_BUF_CFG(pipe, plane_id),
++ skl_plane_ddb_reg_val(ddb));
+
+ if (DISPLAY_VER(i915) < 11)
+- intel_de_write_fw(i915, PLANE_NV12_BUF_CFG(pipe, plane_id),
+- skl_plane_ddb_reg_val(ddb_y));
++ intel_de_write_dsb(display, dsb, PLANE_NV12_BUF_CFG(pipe, plane_id),
++ skl_plane_ddb_reg_val(ddb_y));
+ }
+
+ static void
+-skl_plane_disable_arm(struct intel_plane *plane,
++skl_plane_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+- struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+
+- skl_write_plane_wm(plane, crtc_state);
++ skl_write_plane_wm(dsb, plane, crtc_state);
+
+- intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), 0);
+- intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id), 0);
++ intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id), 0);
+ }
+
+-static void icl_plane_disable_sel_fetch_arm(struct intel_plane *plane,
++static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
+- struct drm_i915_private *i915 = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum pipe pipe = plane->pipe;
+
+ if (!crtc_state->enable_psr2_sel_fetch)
+ return;
+
+- intel_de_write_fw(i915, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0);
++ intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0);
+ }
+
+ static void
+-icl_plane_disable_arm(struct intel_plane *plane,
++icl_plane_disable_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+ {
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+
+ if (icl_is_hdr_plane(dev_priv, plane_id))
+- intel_de_write_fw(dev_priv, PLANE_CUS_CTL(pipe, plane_id), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CUS_CTL(pipe, plane_id), 0);
+
+- skl_write_plane_wm(plane, crtc_state);
++ skl_write_plane_wm(dsb, plane, crtc_state);
+
+- icl_plane_disable_sel_fetch_arm(plane, crtc_state);
+- intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), 0);
+- intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id), 0);
++ icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
++ intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id), 0);
++ intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id), 0);
+ }
+
+ static bool
+@@ -1230,28 +1236,30 @@ static u32 skl_plane_keymsk(const struct intel_plane_state *plane_state)
+ return keymsk;
+ }
+
+-static void icl_plane_csc_load_black(struct intel_plane *plane)
++static void icl_plane_csc_load_black(struct intel_dsb *dsb,
++ struct intel_plane *plane,
++ const struct intel_crtc_state *crtc_state)
+ {
+- struct drm_i915_private *i915 = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+
+- intel_de_write_fw(i915, PLANE_CSC_COEFF(pipe, plane_id, 0), 0);
+- intel_de_write_fw(i915, PLANE_CSC_COEFF(pipe, plane_id, 1), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 0), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 1), 0);
+
+- intel_de_write_fw(i915, PLANE_CSC_COEFF(pipe, plane_id, 2), 0);
+- intel_de_write_fw(i915, PLANE_CSC_COEFF(pipe, plane_id, 3), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 2), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 3), 0);
+
+- intel_de_write_fw(i915, PLANE_CSC_COEFF(pipe, plane_id, 4), 0);
+- intel_de_write_fw(i915, PLANE_CSC_COEFF(pipe, plane_id, 5), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 4), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane_id, 5), 0);
+
+- intel_de_write_fw(i915, PLANE_CSC_PREOFF(pipe, plane_id, 0), 0);
+- intel_de_write_fw(i915, PLANE_CSC_PREOFF(pipe, plane_id, 1), 0);
+- intel_de_write_fw(i915, PLANE_CSC_PREOFF(pipe, plane_id, 2), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane_id, 0), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane_id, 1), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane_id, 2), 0);
+
+- intel_de_write_fw(i915, PLANE_CSC_POSTOFF(pipe, plane_id, 0), 0);
+- intel_de_write_fw(i915, PLANE_CSC_POSTOFF(pipe, plane_id, 1), 0);
+- intel_de_write_fw(i915, PLANE_CSC_POSTOFF(pipe, plane_id, 2), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_POSTOFF(pipe, plane_id, 0), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_POSTOFF(pipe, plane_id, 1), 0);
++ intel_de_write_dsb(display, dsb, PLANE_CSC_POSTOFF(pipe, plane_id, 2), 0);
+ }
+
+ static int icl_plane_color_plane(const struct intel_plane_state *plane_state)
+@@ -1264,11 +1272,12 @@ static int icl_plane_color_plane(const struct intel_plane_state *plane_state)
+ }
+
+ static void
+-skl_plane_update_noarm(struct intel_plane *plane,
++skl_plane_update_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+- struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+ u32 stride = skl_plane_stride(plane_state, 0);
+@@ -1283,21 +1292,23 @@ skl_plane_update_noarm(struct intel_plane *plane,
+ crtc_y = 0;
+ }
+
+- intel_de_write_fw(dev_priv, PLANE_STRIDE(pipe, plane_id),
+- PLANE_STRIDE_(stride));
+- intel_de_write_fw(dev_priv, PLANE_POS(pipe, plane_id),
+- PLANE_POS_Y(crtc_y) | PLANE_POS_X(crtc_x));
+- intel_de_write_fw(dev_priv, PLANE_SIZE(pipe, plane_id),
+- PLANE_HEIGHT(src_h - 1) | PLANE_WIDTH(src_w - 1));
++ intel_de_write_dsb(display, dsb, PLANE_STRIDE(pipe, plane_id),
++ PLANE_STRIDE_(stride));
++ intel_de_write_dsb(display, dsb, PLANE_POS(pipe, plane_id),
++ PLANE_POS_Y(crtc_y) | PLANE_POS_X(crtc_x));
++ intel_de_write_dsb(display, dsb, PLANE_SIZE(pipe, plane_id),
++ PLANE_HEIGHT(src_h - 1) | PLANE_WIDTH(src_w - 1));
+
+- skl_write_plane_wm(plane, crtc_state);
++ skl_write_plane_wm(dsb, plane, crtc_state);
+ }
+
+ static void
+-skl_plane_update_arm(struct intel_plane *plane,
++skl_plane_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+@@ -1317,22 +1328,26 @@ skl_plane_update_arm(struct intel_plane *plane,
+ plane_color_ctl = plane_state->color_ctl |
+ glk_plane_color_ctl_crtc(crtc_state);
+
+- intel_de_write_fw(dev_priv, PLANE_KEYVAL(pipe, plane_id), skl_plane_keyval(plane_state));
+- intel_de_write_fw(dev_priv, PLANE_KEYMSK(pipe, plane_id), skl_plane_keymsk(plane_state));
+- intel_de_write_fw(dev_priv, PLANE_KEYMAX(pipe, plane_id), skl_plane_keymax(plane_state));
++ intel_de_write_dsb(display, dsb, PLANE_KEYVAL(pipe, plane_id),
++ skl_plane_keyval(plane_state));
++ intel_de_write_dsb(display, dsb, PLANE_KEYMSK(pipe, plane_id),
++ skl_plane_keymsk(plane_state));
++ intel_de_write_dsb(display, dsb, PLANE_KEYMAX(pipe, plane_id),
++ skl_plane_keymax(plane_state));
+
+- intel_de_write_fw(dev_priv, PLANE_OFFSET(pipe, plane_id),
+- PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x));
++ intel_de_write_dsb(display, dsb, PLANE_OFFSET(pipe, plane_id),
++ PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x));
+
+- intel_de_write_fw(dev_priv, PLANE_AUX_DIST(pipe, plane_id),
+- skl_plane_aux_dist(plane_state, 0));
++ intel_de_write_dsb(display, dsb, PLANE_AUX_DIST(pipe, plane_id),
++ skl_plane_aux_dist(plane_state, 0));
+
+- intel_de_write_fw(dev_priv, PLANE_AUX_OFFSET(pipe, plane_id),
+- PLANE_OFFSET_Y(plane_state->view.color_plane[1].y) |
+- PLANE_OFFSET_X(plane_state->view.color_plane[1].x));
++ intel_de_write_dsb(display, dsb, PLANE_AUX_OFFSET(pipe, plane_id),
++ PLANE_OFFSET_Y(plane_state->view.color_plane[1].y) |
++ PLANE_OFFSET_X(plane_state->view.color_plane[1].x));
+
+ if (DISPLAY_VER(dev_priv) >= 10)
+- intel_de_write_fw(dev_priv, PLANE_COLOR_CTL(pipe, plane_id), plane_color_ctl);
++ intel_de_write_dsb(display, dsb, PLANE_COLOR_CTL(pipe, plane_id),
++ plane_color_ctl);
+
+ /*
+ * Enable the scaler before the plane so that we don't
+@@ -1349,17 +1364,19 @@ skl_plane_update_arm(struct intel_plane *plane,
+ * disabled. Try to make the plane enable atomic by writing
+ * the control register just before the surface register.
+ */
+- intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), plane_ctl);
+- intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id),
+- skl_plane_surf(plane_state, 0));
++ intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id),
++ plane_ctl);
++ intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id),
++ skl_plane_surf(plane_state, 0));
+ }
+
+-static void icl_plane_update_sel_fetch_noarm(struct intel_plane *plane,
++static void icl_plane_update_sel_fetch_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ int color_plane)
+ {
+- struct drm_i915_private *i915 = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum pipe pipe = plane->pipe;
+ const struct drm_rect *clip;
+ u32 val;
+@@ -1376,7 +1393,7 @@ static void icl_plane_update_sel_fetch_noarm(struct intel_plane *plane,
+ y = (clip->y1 + plane_state->uapi.dst.y1);
+ val = y << 16;
+ val |= plane_state->uapi.dst.x1;
+- intel_de_write_fw(i915, SEL_FETCH_PLANE_POS(pipe, plane->id), val);
++ intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_POS(pipe, plane->id), val);
+
+ x = plane_state->view.color_plane[color_plane].x;
+
+@@ -1391,20 +1408,21 @@ static void icl_plane_update_sel_fetch_noarm(struct intel_plane *plane,
+
+ val = y << 16 | x;
+
+- intel_de_write_fw(i915, SEL_FETCH_PLANE_OFFSET(pipe, plane->id),
+- val);
++ intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_OFFSET(pipe, plane->id), val);
+
+ /* Sizes are 0 based */
+ val = (drm_rect_height(clip) - 1) << 16;
+ val |= (drm_rect_width(&plane_state->uapi.src) >> 16) - 1;
+- intel_de_write_fw(i915, SEL_FETCH_PLANE_SIZE(pipe, plane->id), val);
++ intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_SIZE(pipe, plane->id), val);
+ }
+
+ static void
+-icl_plane_update_noarm(struct intel_plane *plane,
++icl_plane_update_noarm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+@@ -1428,76 +1446,82 @@ icl_plane_update_noarm(struct intel_plane *plane,
+ crtc_y = 0;
+ }
+
+- intel_de_write_fw(dev_priv, PLANE_STRIDE(pipe, plane_id),
+- PLANE_STRIDE_(stride));
+- intel_de_write_fw(dev_priv, PLANE_POS(pipe, plane_id),
+- PLANE_POS_Y(crtc_y) | PLANE_POS_X(crtc_x));
+- intel_de_write_fw(dev_priv, PLANE_SIZE(pipe, plane_id),
+- PLANE_HEIGHT(src_h - 1) | PLANE_WIDTH(src_w - 1));
++ intel_de_write_dsb(display, dsb, PLANE_STRIDE(pipe, plane_id),
++ PLANE_STRIDE_(stride));
++ intel_de_write_dsb(display, dsb, PLANE_POS(pipe, plane_id),
++ PLANE_POS_Y(crtc_y) | PLANE_POS_X(crtc_x));
++ intel_de_write_dsb(display, dsb, PLANE_SIZE(pipe, plane_id),
++ PLANE_HEIGHT(src_h - 1) | PLANE_WIDTH(src_w - 1));
+
+- intel_de_write_fw(dev_priv, PLANE_KEYVAL(pipe, plane_id), skl_plane_keyval(plane_state));
+- intel_de_write_fw(dev_priv, PLANE_KEYMSK(pipe, plane_id), skl_plane_keymsk(plane_state));
+- intel_de_write_fw(dev_priv, PLANE_KEYMAX(pipe, plane_id), skl_plane_keymax(plane_state));
++ intel_de_write_dsb(display, dsb, PLANE_KEYVAL(pipe, plane_id),
++ skl_plane_keyval(plane_state));
++ intel_de_write_dsb(display, dsb, PLANE_KEYMSK(pipe, plane_id),
++ skl_plane_keymsk(plane_state));
++ intel_de_write_dsb(display, dsb, PLANE_KEYMAX(pipe, plane_id),
++ skl_plane_keymax(plane_state));
+
+- intel_de_write_fw(dev_priv, PLANE_OFFSET(pipe, plane_id),
+- PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x));
++ intel_de_write_dsb(display, dsb, PLANE_OFFSET(pipe, plane_id),
++ PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x));
+
+ if (intel_fb_is_rc_ccs_cc_modifier(fb->modifier)) {
+- intel_de_write_fw(dev_priv, PLANE_CC_VAL(pipe, plane_id, 0),
+- lower_32_bits(plane_state->ccval));
+- intel_de_write_fw(dev_priv, PLANE_CC_VAL(pipe, plane_id, 1),
+- upper_32_bits(plane_state->ccval));
++ intel_de_write_dsb(display, dsb, PLANE_CC_VAL(pipe, plane_id, 0),
++ lower_32_bits(plane_state->ccval));
++ intel_de_write_dsb(display, dsb, PLANE_CC_VAL(pipe, plane_id, 1),
++ upper_32_bits(plane_state->ccval));
+ }
+
+ /* FLAT CCS doesn't need to program AUX_DIST */
+ if (!HAS_FLAT_CCS(dev_priv) && DISPLAY_VER(dev_priv) < 20)
+- intel_de_write_fw(dev_priv, PLANE_AUX_DIST(pipe, plane_id),
+- skl_plane_aux_dist(plane_state, color_plane));
++ intel_de_write_dsb(display, dsb, PLANE_AUX_DIST(pipe, plane_id),
++ skl_plane_aux_dist(plane_state, color_plane));
+
+ if (icl_is_hdr_plane(dev_priv, plane_id))
+- intel_de_write_fw(dev_priv, PLANE_CUS_CTL(pipe, plane_id),
+- plane_state->cus_ctl);
++ intel_de_write_dsb(display, dsb, PLANE_CUS_CTL(pipe, plane_id),
++ plane_state->cus_ctl);
+
+- intel_de_write_fw(dev_priv, PLANE_COLOR_CTL(pipe, plane_id), plane_color_ctl);
++ intel_de_write_dsb(display, dsb, PLANE_COLOR_CTL(pipe, plane_id),
++ plane_color_ctl);
+
+ if (fb->format->is_yuv && icl_is_hdr_plane(dev_priv, plane_id))
+- icl_program_input_csc(plane, crtc_state, plane_state);
++ icl_program_input_csc(dsb, plane, plane_state);
+
+- skl_write_plane_wm(plane, crtc_state);
++ skl_write_plane_wm(dsb, plane, crtc_state);
+
+ /*
+ * FIXME: pxp session invalidation can hit any time even at time of commit
+ * or after the commit, display content will be garbage.
+ */
+ if (plane_state->force_black)
+- icl_plane_csc_load_black(plane);
++ icl_plane_csc_load_black(dsb, plane, crtc_state);
+
+- icl_plane_update_sel_fetch_noarm(plane, crtc_state, plane_state, color_plane);
++ icl_plane_update_sel_fetch_noarm(dsb, plane, crtc_state, plane_state, color_plane);
+ }
+
+-static void icl_plane_update_sel_fetch_arm(struct intel_plane *plane,
++static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+- struct drm_i915_private *i915 = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum pipe pipe = plane->pipe;
+
+ if (!crtc_state->enable_psr2_sel_fetch)
+ return;
+
+ if (drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0)
+- intel_de_write_fw(i915, SEL_FETCH_PLANE_CTL(pipe, plane->id),
+- SEL_FETCH_PLANE_CTL_ENABLE);
++ intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id),
++ SEL_FETCH_PLANE_CTL_ENABLE);
+ else
+- icl_plane_disable_sel_fetch_arm(plane, crtc_state);
++ icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
+ }
+
+ static void
+-icl_plane_update_arm(struct intel_plane *plane,
++icl_plane_update_arm(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+ {
+- struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+ int color_plane = icl_plane_color_plane(plane_state);
+@@ -1516,25 +1540,27 @@ icl_plane_update_arm(struct intel_plane *plane,
+ if (plane_state->scaler_id >= 0)
+ skl_program_plane_scaler(plane, crtc_state, plane_state);
+
+- icl_plane_update_sel_fetch_arm(plane, crtc_state, plane_state);
++ icl_plane_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state);
+
+ /*
+ * The control register self-arms if the plane was previously
+ * disabled. Try to make the plane enable atomic by writing
+ * the control register just before the surface register.
+ */
+- intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), plane_ctl);
+- intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id),
+- skl_plane_surf(plane_state, color_plane));
++ intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id),
++ plane_ctl);
++ intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id),
++ skl_plane_surf(plane_state, color_plane));
+ }
+
+ static void
+-skl_plane_async_flip(struct intel_plane *plane,
++skl_plane_async_flip(struct intel_dsb *dsb,
++ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ bool async_flip)
+ {
+- struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
++ struct intel_display *display = to_intel_display(plane->base.dev);
+ enum plane_id plane_id = plane->id;
+ enum pipe pipe = plane->pipe;
+ u32 plane_ctl = plane_state->ctl;
+@@ -1544,9 +1570,10 @@ skl_plane_async_flip(struct intel_plane *plane,
+ if (async_flip)
+ plane_ctl |= PLANE_CTL_ASYNC_FLIP;
+
+- intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), plane_ctl);
+- intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id),
+- skl_plane_surf(plane_state, 0));
++ intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id),
++ plane_ctl);
++ intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id),
++ skl_plane_surf(plane_state, 0));
+ }
+
+ static bool intel_format_is_p01x(u32 format)
+diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c
+index a50ab9eae40ae..0ae3f02b9261b 100644
+--- a/drivers/gpu/drm/xe/display/xe_plane_initial.c
++++ b/drivers/gpu/drm/xe/display/xe_plane_initial.c
+@@ -248,7 +248,7 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
+ * the lookup of sysmem scratch pages.
+ */
+ plane->check_plane(crtc_state, plane_state);
+- plane->async_flip(plane, crtc_state, plane_state, true);
++ plane->async_flip(NULL, plane, crtc_state, plane_state, true);
+ return;
+
+ nofb:
+--
+2.39.5
+
--- /dev/null
+From d5dba737045d82588540685e7f89dc74a33f79b0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 21 Feb 2025 10:49:35 +0000
+Subject: drm/imagination: Fix timestamps in firmware traces
+
+From: Alessio Belle <alessio.belle@imgtec.com>
+
+[ Upstream commit 1d2eabb6616433ccaa13927811bdfa205e91ba60 ]
+
+When firmware traces are enabled, the firmware dumps 48-bit timestamps
+for each trace as two 32-bit values, highest 32 bits (of which only 16
+useful) first.
+
+The driver was reassembling them the other way round i.e. interpreting
+the first value in memory as the lowest 32 bits, and the second value
+as the highest 32 bits (then truncated to 16 bits).
+
+Due to this, firmware trace dumps showed very large timestamps even for
+traces recorded shortly after GPU boot. The timestamps in these dumps
+would also sometimes jump backwards because of the truncation.
+
+Example trace dumped after loading the powervr module and enabling
+firmware traces, where each line is commented with the timestamp value
+in hexadecimal to better show both issues:
+
+[93540092739584] : Host Sync Partition marker: 1 // 0x551300000000
+[28419798597632] : GPU units deinit // 0x19d900000000
+[28548647616512] : GPU deinit // 0x19f700000000
+
+Update logic to reassemble the timestamps halves in the correct order.
+
+Fixes: cb56cd610866 ("drm/imagination: Add firmware trace to debugfs")
+Signed-off-by: Alessio Belle <alessio.belle@imgtec.com>
+Reviewed-by: Matt Coster <matt.coster@imgtec.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250221-fix-fw-trace-timestamps-v1-1-dba4aeb030ca@imgtec.com
+Signed-off-by: Matt Coster <matt.coster@imgtec.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/imagination/pvr_fw_trace.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c
+index 73707daa4e52d..5dbb636d7d4ff 100644
+--- a/drivers/gpu/drm/imagination/pvr_fw_trace.c
++++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c
+@@ -333,8 +333,8 @@ static int fw_trace_seq_show(struct seq_file *s, void *v)
+ if (sf_id == ROGUE_FW_SF_LAST)
+ return -EINVAL;
+
+- timestamp = read_fw_trace(trace_seq_data, 1) |
+- ((u64)read_fw_trace(trace_seq_data, 2) << 32);
++ timestamp = ((u64)read_fw_trace(trace_seq_data, 1) << 32) |
++ read_fw_trace(trace_seq_data, 2);
+ timestamp = (timestamp & ~ROGUE_FWT_TIMESTAMP_TIME_CLRMSK) >>
+ ROGUE_FWT_TIMESTAMP_TIME_SHIFT;
+
+--
+2.39.5
+
--- /dev/null
+From e1276b7c7d8d6602858838e7b8a589da35a342d1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2024 09:13:06 +0200
+Subject: drm/nouveau: Run DRM default client setup
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit ef350898ae22db832ada972476fa2999f8ea978c ]
+
+Call drm_client_setup() to run the kernel's default client setup
+for DRM. Set fbdev_probe in struct drm_driver, so that the client
+setup can start the common fbdev client.
+
+The nouveau driver specifies a preferred color mode depending on
+the available video memory, with a default of 32. Adapt this for
+the new client interface.
+
+v5:
+- select DRM_CLIENT_SELECTION
+v2:
+- style changes
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Cc: Karol Herbst <kherbst@redhat.com>
+Cc: Lyude Paul <lyude@redhat.com>
+Cc: Danilo Krummrich <dakr@redhat.com>
+Reviewed-by: Lyude Paul <lyude@redhat.com>
+Acked-by: Danilo Krummrich <dakr@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240924071734.98201-69-tzimmermann@suse.de
+Stable-dep-of: 6b481ab0e685 ("drm/nouveau: select FW caching")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/nouveau/Kconfig | 1 +
+ drivers/gpu/drm/nouveau/nouveau_drm.c | 10 ++++++++--
+ 2 files changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
+index ceef470c9fbfc..ce840300578d8 100644
+--- a/drivers/gpu/drm/nouveau/Kconfig
++++ b/drivers/gpu/drm/nouveau/Kconfig
+@@ -4,6 +4,7 @@ config DRM_NOUVEAU
+ depends on DRM && PCI && MMU
+ select IOMMU_API
+ select FW_LOADER
++ select DRM_CLIENT_SELECTION
+ select DRM_DISPLAY_DP_HELPER
+ select DRM_DISPLAY_HDMI_HELPER
+ select DRM_DISPLAY_HELPER
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
+index 34985771b2a28..6e5adab034713 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
+@@ -31,6 +31,7 @@
+ #include <linux/dynamic_debug.h>
+
+ #include <drm/drm_aperture.h>
++#include <drm/drm_client_setup.h>
+ #include <drm/drm_drv.h>
+ #include <drm/drm_fbdev_ttm.h>
+ #include <drm/drm_gem_ttm_helper.h>
+@@ -836,6 +837,7 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
+ {
+ struct nvkm_device *device;
+ struct nouveau_drm *drm;
++ const struct drm_format_info *format;
+ int ret;
+
+ if (vga_switcheroo_client_probe_defer(pdev))
+@@ -873,9 +875,11 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
+ goto fail_pci;
+
+ if (drm->client.device.info.ram_size <= 32 * 1024 * 1024)
+- drm_fbdev_ttm_setup(drm->dev, 8);
++ format = drm_format_info(DRM_FORMAT_C8);
+ else
+- drm_fbdev_ttm_setup(drm->dev, 32);
++ format = NULL;
++
++ drm_client_setup(drm->dev, format);
+
+ quirk_broken_nv_runpm(pdev);
+ return 0;
+@@ -1318,6 +1322,8 @@ driver_stub = {
+ .dumb_create = nouveau_display_dumb_create,
+ .dumb_map_offset = drm_gem_ttm_dumb_map_offset,
+
++ DRM_FBDEV_TTM_DRIVER_OPS,
++
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ #ifdef GIT_REVISION
+--
+2.39.5
+
--- /dev/null
+From 7b59383c16b389f021b1f2bcbdae6c2a96db6f19 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Feb 2025 11:25:31 +1000
+Subject: drm/nouveau: select FW caching
+
+From: Dave Airlie <airlied@redhat.com>
+
+[ Upstream commit 6b481ab0e6855fb30e2923c51f62f1662d1cda7e ]
+
+nouveau tries to load some firmware during suspend that it loaded
+earlier, but with fw caching disabled it hangs suspend, so just rely on
+FW cache enabling instead of working around it in the driver.
+
+Fixes: 176fdcbddfd2 ("drm/nouveau/gsp/r535: add support for booting GSP-RM")
+Signed-off-by: Dave Airlie <airlied@redhat.com>
+Signed-off-by: Danilo Krummrich <dakr@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250207012531.621369-1-airlied@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/nouveau/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
+index ce840300578d8..1050a4617fc15 100644
+--- a/drivers/gpu/drm/nouveau/Kconfig
++++ b/drivers/gpu/drm/nouveau/Kconfig
+@@ -4,6 +4,7 @@ config DRM_NOUVEAU
+ depends on DRM && PCI && MMU
+ select IOMMU_API
+ select FW_LOADER
++ select FW_CACHE if PM_SLEEP
+ select DRM_CLIENT_SELECTION
+ select DRM_DISPLAY_DP_HELPER
+ select DRM_DISPLAY_HDMI_HELPER
+--
+2.39.5
+
--- /dev/null
+From cdbd6d0ab1ec0c1da11d5fa3c0274ebae6749d18 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 18 Feb 2025 13:41:50 +0100
+Subject: drm/sched: Fix preprocessor guard
+
+From: Philipp Stanner <phasta@kernel.org>
+
+[ Upstream commit 23e0832d6d7be2d3c713f9390c060b6f1c48bf36 ]
+
+When writing the header guard for gpu_scheduler_trace.h, a typo,
+apparently, occurred.
+
+Fix the typo and document the scope of the guard.
+
+Fixes: 353da3c520b4 ("drm/amdgpu: add tracepoint for scheduler (v2)")
+Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+Signed-off-by: Philipp Stanner <phasta@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250218124149.118002-2-phasta@kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/scheduler/gpu_scheduler_trace.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h b/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h
+index c75302ca3427c..f56e77e7f6d02 100644
+--- a/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h
++++ b/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h
+@@ -21,7 +21,7 @@
+ *
+ */
+
+-#if !defined(_GPU_SCHED_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
++#if !defined(_GPU_SCHED_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+ #define _GPU_SCHED_TRACE_H_
+
+ #include <linux/stringify.h>
+@@ -106,7 +106,7 @@ TRACE_EVENT(drm_sched_job_wait_dep,
+ __entry->seqno)
+ );
+
+-#endif
++#endif /* _GPU_SCHED_TRACE_H_ */
+
+ /* This part must be outside protection */
+ #undef TRACE_INCLUDE_PATH
+--
+2.39.5
+
--- /dev/null
+From 200547e1c1e89e4e07ec43a8bc92afecb9696974 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 10 Dec 2024 09:31:02 +0100
+Subject: drm/xe: Remove double pageflip
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Maarten Lankhorst <dev@lankhorst.se>
+
+[ Upstream commit 30bfc151f0c1ec80c27a80a7651b2c15c648ad16 ]
+
+This is already handled below in the code by fixup_initial_plane_config.
+
+Fixes: a8153627520a ("drm/i915: Try to relocate the BIOS fb to the start of ggtt")
+Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Reviewed-by: Vinod Govindapillai <vinod.govindapillai@intel.com>
+Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20241210083111.230484-3-dev@lankhorst.se
+Signed-off-by: Maarten Lankhorst <dev@lankhorst.se>
+(cherry picked from commit 2218704997979fbf11765281ef752f07c5cf25bb)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/xe/display/xe_plane_initial.c | 10 ----------
+ 1 file changed, 10 deletions(-)
+
+diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c
+index 0ae3f02b9261b..f99d38cc5d8e9 100644
+--- a/drivers/gpu/drm/xe/display/xe_plane_initial.c
++++ b/drivers/gpu/drm/xe/display/xe_plane_initial.c
+@@ -194,8 +194,6 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
+ to_intel_plane(crtc->base.primary);
+ struct intel_plane_state *plane_state =
+ to_intel_plane_state(plane->base.state);
+- struct intel_crtc_state *crtc_state =
+- to_intel_crtc_state(crtc->base.state);
+ struct drm_framebuffer *fb;
+ struct i915_vma *vma;
+
+@@ -241,14 +239,6 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
+ atomic_or(plane->frontbuffer_bit, &to_intel_frontbuffer(fb)->bits);
+
+ plane_config->vma = vma;
+-
+- /*
+- * Flip to the newly created mapping ASAP, so we can re-use the
+- * first part of GGTT for WOPCM, prevent flickering, and prevent
+- * the lookup of sysmem scratch pages.
+- */
+- plane->check_plane(crtc_state, plane_state);
+- plane->async_flip(NULL, plane, crtc_state, plane_state, true);
+ return;
+
+ nofb:
+--
+2.39.5
+
--- /dev/null
+From 112bdd3123313bc0153be7d86df90f795bc1752b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jan 2025 07:05:11 +0100
+Subject: ethtool: linkstate: migrate linkstate functions to support multi-PHY
+ setups
+
+From: Oleksij Rempel <o.rempel@pengutronix.de>
+
+[ Upstream commit fe55b1d401c697c2ef126fe3ebbcaa6885fced5a ]
+
+Adapt linkstate_get_sqi() and linkstate_get_sqi_max() to take a
+phy_device argument directly, enabling support for setups with
+multiple PHYs. The previous assumption of a single PHY attached to
+a net_device no longer holds.
+
+Use ethnl_req_get_phydev() to identify the appropriate PHY device
+for the operation. Update linkstate_prepare_data() and related
+logic to accommodate this change, ensuring compatibility with
+multi-PHY configurations.
+
+Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Stable-dep-of: 637399bf7e77 ("net: ethtool: netlink: Allow NULL nlattrs when getting a phy_device")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ethtool/linkstate.c | 23 +++++++++++++++--------
+ 1 file changed, 15 insertions(+), 8 deletions(-)
+
+diff --git a/net/ethtool/linkstate.c b/net/ethtool/linkstate.c
+index 34d76e87847d0..459cfea7652d4 100644
+--- a/net/ethtool/linkstate.c
++++ b/net/ethtool/linkstate.c
+@@ -26,9 +26,8 @@ const struct nla_policy ethnl_linkstate_get_policy[] = {
+ NLA_POLICY_NESTED(ethnl_header_policy_stats),
+ };
+
+-static int linkstate_get_sqi(struct net_device *dev)
++static int linkstate_get_sqi(struct phy_device *phydev)
+ {
+- struct phy_device *phydev = dev->phydev;
+ int ret;
+
+ if (!phydev)
+@@ -46,9 +45,8 @@ static int linkstate_get_sqi(struct net_device *dev)
+ return ret;
+ }
+
+-static int linkstate_get_sqi_max(struct net_device *dev)
++static int linkstate_get_sqi_max(struct phy_device *phydev)
+ {
+- struct phy_device *phydev = dev->phydev;
+ int ret;
+
+ if (!phydev)
+@@ -100,19 +98,28 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
+ {
+ struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base);
+ struct net_device *dev = reply_base->dev;
++ struct nlattr **tb = info->attrs;
++ struct phy_device *phydev;
+ int ret;
+
++ phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_LINKSTATE_HEADER],
++ info->extack);
++ if (IS_ERR(phydev)) {
++ ret = PTR_ERR(phydev);
++ goto out;
++ }
++
+ ret = ethnl_ops_begin(dev);
+ if (ret < 0)
+ return ret;
+ data->link = __ethtool_get_link(dev);
+
+- ret = linkstate_get_sqi(dev);
++ ret = linkstate_get_sqi(phydev);
+ if (linkstate_sqi_critical_error(ret))
+ goto out;
+ data->sqi = ret;
+
+- ret = linkstate_get_sqi_max(dev);
++ ret = linkstate_get_sqi_max(phydev);
+ if (linkstate_sqi_critical_error(ret))
+ goto out;
+ data->sqi_max = ret;
+@@ -127,9 +134,9 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
+ sizeof(data->link_stats) / 8);
+
+ if (req_base->flags & ETHTOOL_FLAG_STATS) {
+- if (dev->phydev)
++ if (phydev)
+ data->link_stats.link_down_events =
+- READ_ONCE(dev->phydev->link_down_events);
++ READ_ONCE(phydev->link_down_events);
+
+ if (dev->ethtool_ops->get_link_ext_stats)
+ dev->ethtool_ops->get_link_ext_stats(dev,
+--
+2.39.5
+
--- /dev/null
+From e7c7fd7135710d430d37174ccdf0e619de424109 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 Nov 2024 10:50:55 +0800
+Subject: exfat: fix just enough dentries but allocate a new cluster to dir
+
+From: Yuezhang Mo <Yuezhang.Mo@sony.com>
+
+[ Upstream commit 6697f819a10b238ccf01998c3f203d65d8374696 ]
+
+This commit fixes the condition for allocating cluster to parent
+directory to avoid allocating new cluster to parent directory when
+there are just enough empty directory entries at the end of the
+parent directory.
+
+Fixes: af02c72d0b62 ("exfat: convert exfat_find_empty_entry() to use dentry cache")
+Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/exfat/namei.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
+index 337197ece5995..e47a5ddfc79b3 100644
+--- a/fs/exfat/namei.c
++++ b/fs/exfat/namei.c
+@@ -237,7 +237,7 @@ static int exfat_search_empty_slot(struct super_block *sb,
+ dentry = 0;
+ }
+
+- while (dentry + num_entries < total_entries &&
++ while (dentry + num_entries <= total_entries &&
+ clu.dir != EXFAT_EOF_CLUSTER) {
+ i = dentry & (dentries_per_clu - 1);
+
+--
+2.39.5
+
--- /dev/null
+From 47cc1de036b844ed4539b9540e8c24300619d964 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 31 Jan 2025 12:55:55 +0900
+Subject: exfat: fix soft lockup in exfat_clear_bitmap
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 9da33619e0ca53627641bc97d1b93ec741299111 ]
+
+bitmap clear loop will take long time in __exfat_free_cluster()
+if data size of file/dir enty is invalid.
+If cluster bit in bitmap is already clear, stop clearing bitmap go to
+out of loop.
+
+Fixes: 31023864e67a ("exfat: add fat entry operations")
+Reported-by: Kun Hu <huk23@m.fudan.edu.cn>, Jiaji Qin <jjtan24@m.fudan.edu.cn>
+Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/exfat/balloc.c | 10 ++++++++--
+ fs/exfat/exfat_fs.h | 2 +-
+ fs/exfat/fatent.c | 11 +++++++----
+ 3 files changed, 16 insertions(+), 7 deletions(-)
+
+diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
+index ce9be95c9172f..9ff825f1502d5 100644
+--- a/fs/exfat/balloc.c
++++ b/fs/exfat/balloc.c
+@@ -141,7 +141,7 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
+ return 0;
+ }
+
+-void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
++int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
+ {
+ int i, b;
+ unsigned int ent_idx;
+@@ -150,13 +150,17 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
+ struct exfat_mount_options *opts = &sbi->options;
+
+ if (!is_valid_cluster(sbi, clu))
+- return;
++ return -EIO;
+
+ ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
+ i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
+ b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
+
++ if (!test_bit_le(b, sbi->vol_amap[i]->b_data))
++ return -EIO;
++
+ clear_bit_le(b, sbi->vol_amap[i]->b_data);
++
+ exfat_update_bh(sbi->vol_amap[i], sync);
+
+ if (opts->discard) {
+@@ -171,6 +175,8 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
+ opts->discard = 0;
+ }
+ }
++
++ return 0;
+ }
+
+ /*
+diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
+index 3cdc1de362a94..d2ba8e2d0c398 100644
+--- a/fs/exfat/exfat_fs.h
++++ b/fs/exfat/exfat_fs.h
+@@ -452,7 +452,7 @@ int exfat_count_num_clusters(struct super_block *sb,
+ int exfat_load_bitmap(struct super_block *sb);
+ void exfat_free_bitmap(struct exfat_sb_info *sbi);
+ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync);
+-void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
++int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
+ unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu);
+ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
+ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
+diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
+index 9e5492ac409b0..6f3651c6ca91e 100644
+--- a/fs/exfat/fatent.c
++++ b/fs/exfat/fatent.c
+@@ -175,6 +175,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
+ BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu));
+
+ if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
++ int err;
+ unsigned int last_cluster = p_chain->dir + p_chain->size - 1;
+ do {
+ bool sync = false;
+@@ -189,7 +190,9 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
+ cur_cmap_i = next_cmap_i;
+ }
+
+- exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
++ err = exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
++ if (err)
++ break;
+ clu++;
+ num_clusters++;
+ } while (num_clusters < p_chain->size);
+@@ -210,12 +213,13 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
+ cur_cmap_i = next_cmap_i;
+ }
+
+- exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
++ if (exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))))
++ break;
+ clu = n_clu;
+ num_clusters++;
+
+ if (err)
+- goto dec_used_clus;
++ break;
+
+ if (num_clusters >= sbi->num_clusters - EXFAT_FIRST_CLUSTER) {
+ /*
+@@ -229,7 +233,6 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
+ } while (clu != EXFAT_EOF_CLUSTER);
+ }
+
+-dec_used_clus:
+ sbi->used_clusters -= num_clusters;
+ return 0;
+ }
+--
+2.39.5
+
--- /dev/null
+From 9c9fab59b353d3e6da6c31c029aaa282137626e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Feb 2025 14:14:21 -0600
+Subject: exfat: short-circuit zero-byte writes in exfat_file_write_iter
+
+From: Eric Sandeen <sandeen@redhat.com>
+
+[ Upstream commit fda94a9919fd632033979ad7765a99ae3cab9289 ]
+
+When generic_write_checks() returns zero, it means that
+iov_iter_count() is zero, and there is no work to do.
+
+Simply return success like all other filesystems do, rather than
+proceeding down the write path, which today yields an -EFAULT in
+generic_perform_write() via the
+(fault_in_iov_iter_readable(i, bytes) == bytes) check when bytes
+== 0.
+
+Fixes: 11a347fb6cef ("exfat: change to get file size from DataLength")
+Reported-by: Noah <kernel-org-10@maxgrass.eu>
+Signed-off-by: Eric Sandeen <sandeen@redhat.com>
+Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/exfat/file.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/exfat/file.c b/fs/exfat/file.c
+index 05b51e7217838..807349d8ea050 100644
+--- a/fs/exfat/file.c
++++ b/fs/exfat/file.c
+@@ -587,7 +587,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
+ valid_size = ei->valid_size;
+
+ ret = generic_write_checks(iocb, iter);
+- if (ret < 0)
++ if (ret <= 0)
+ goto unlock;
+
+ if (iocb->ki_flags & IOCB_DIRECT) {
+--
+2.39.5
+
--- /dev/null
+From d74248a89b27d81cc2450c139b019719895ccdc7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Mar 2025 07:33:58 -1000
+Subject: fs/pipe: do not open-code pipe head/tail logic in FIONREAD
+
+From: Linus Torvalds <torvalds@linux-foundation.org>
+
+[ Upstream commit d810d4c27bf34c719243bab9feb0d843edc09fd7 ]
+
+Rasmus points out that we do indeed have other cases of breakage from
+the type changes that were introduced on 32-bit targets in order to read
+the pipe head and tail values atomically (commit 3d252160b818: "fs/pipe:
+Read pipe->{head,tail} atomically outside pipe->mutex").
+
+Fix it up by using the proper helper functions that now deal with the
+pipe buffer index types properly. This makes the code simpler and more
+obvious.
+
+The compiler does the CSE and loop hoisting of the pipe ring size
+masking that we used to do manually, so open-coding this was never a
+good idea.
+
+Reported-by: Rasmus Villemoes <ravi@prevas.dk>
+Link: https://lore.kernel.org/all/87cyeu5zgk.fsf@prevas.dk/
+Fixes: 3d252160b818 ("fs/pipe: Read pipe->{head,tail} atomically outside pipe->mutex")Cc: Oleg Nesterov <oleg@redhat.com>
+Cc: Mateusz Guzik <mjguzik@gmail.com>
+Cc: K Prateek Nayak <kprateek.nayak@amd.com>
+Cc: Swapnil Sapkal <swapnil.sapkal@amd.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/pipe.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/fs/pipe.c b/fs/pipe.c
+index 0b2b6ccb8ec52..6e72670a8a0dc 100644
+--- a/fs/pipe.c
++++ b/fs/pipe.c
+@@ -613,7 +613,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
+ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ {
+ struct pipe_inode_info *pipe = filp->private_data;
+- unsigned int count, head, tail, mask;
++ unsigned int count, head, tail;
+
+ switch (cmd) {
+ case FIONREAD:
+@@ -621,10 +621,9 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ count = 0;
+ head = pipe->head;
+ tail = pipe->tail;
+- mask = pipe->ring_size - 1;
+
+- while (tail != head) {
+- count += pipe->bufs[tail & mask].len;
++ while (!pipe_empty(head, tail)) {
++ count += pipe_buf(pipe, tail)->len;
+ tail++;
+ }
+ mutex_unlock(&pipe->mutex);
+--
+2.39.5
+
--- /dev/null
+From bb3b60e2c921cb253f99be3580bc23af9e5dc2f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Mar 2025 07:53:25 -1000
+Subject: fs/pipe: fix pipe buffer index use in FUSE
+
+From: Linus Torvalds <torvalds@linux-foundation.org>
+
+[ Upstream commit ebb0f38bb47f74b29e267babdbcd2c47d5292aa8 ]
+
+This was another case that Rasmus pointed out where the direct access to
+the pipe head and tail pointers broke on 32-bit configurations due to
+the type changes.
+
+As with the pipe FIONREAD case, fix it by using the appropriate helper
+functions that deal with the right pipe index sizing.
+
+Reported-by: Rasmus Villemoes <ravi@prevas.dk>
+Link: https://lore.kernel.org/all/878qpi5wz4.fsf@prevas.dk/
+Fixes: 3d252160b818 ("fs/pipe: Read pipe->{head,tail} atomically outside pipe->mutex")Cc: Oleg >
+Cc: Mateusz Guzik <mjguzik@gmail.com>
+Cc: K Prateek Nayak <kprateek.nayak@amd.com>
+Cc: Swapnil Sapkal <swapnil.sapkal@amd.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/fuse/dev.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
+index 1f64ae6d7a69e..f609701297311 100644
+--- a/fs/fuse/dev.c
++++ b/fs/fuse/dev.c
+@@ -2079,7 +2079,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
+ struct file *out, loff_t *ppos,
+ size_t len, unsigned int flags)
+ {
+- unsigned int head, tail, mask, count;
++ unsigned int head, tail, count;
+ unsigned nbuf;
+ unsigned idx;
+ struct pipe_buffer *bufs;
+@@ -2096,8 +2096,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
+
+ head = pipe->head;
+ tail = pipe->tail;
+- mask = pipe->ring_size - 1;
+- count = head - tail;
++ count = pipe_occupancy(head, tail);
+
+ bufs = kvmalloc_array(count, sizeof(struct pipe_buffer), GFP_KERNEL);
+ if (!bufs) {
+@@ -2107,8 +2106,8 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
+
+ nbuf = 0;
+ rem = 0;
+- for (idx = tail; idx != head && rem < len; idx++)
+- rem += pipe->bufs[idx & mask].len;
++ for (idx = tail; !pipe_empty(head, idx) && rem < len; idx++)
++ rem += pipe_buf(pipe, idx)->len;
+
+ ret = -EINVAL;
+ if (rem < len)
+@@ -2119,10 +2118,10 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
+ struct pipe_buffer *ibuf;
+ struct pipe_buffer *obuf;
+
+- if (WARN_ON(nbuf >= count || tail == head))
++ if (WARN_ON(nbuf >= count || pipe_empty(head, tail)))
+ goto out_free;
+
+- ibuf = &pipe->bufs[tail & mask];
++ ibuf = pipe_buf(pipe, tail);
+ obuf = &bufs[nbuf];
+
+ if (rem >= ibuf->len) {
+--
+2.39.5
+
--- /dev/null
+From 60e10eb27fe5399c6173c917c1b9dda3f2f96333 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Mar 2025 07:08:09 -1000
+Subject: fs/pipe: Fix pipe_occupancy() with 16-bit indexes
+
+From: Linus Torvalds <torvalds@linux-foundation.org>
+
+[ Upstream commit c27c66afc449b80f3b4b84d123358c0248f2cf63 ]
+
+The pipe_occupancy() logic implicitly relied on the natural unsigned
+modulo arithmetic in C, but that doesn't work for the new 'pipe_index_t'
+case, since any arithmetic will be done in 'int' (and here we had also
+made it 'unsigned int' due to the function call boundary).
+
+So make the modulo arithmetic explicit by casting the result to the
+proper type.
+
+Cc: Oleg Nesterov <oleg@redhat.com>
+Cc: Mateusz Guzik <mjguzik@gmail.com>
+Cc: Manfred Spraul <manfred@colorfullife.com>
+Cc: Christian Brauner <brauner@kernel.org>
+Cc: Swapnil Sapkal <swapnil.sapkal@amd.com>
+Cc: Alexey Gladkov <legion@kernel.org>
+Cc: K Prateek Nayak <kprateek.nayak@amd.com>
+Link: https://lore.kernel.org/all/CAHk-=wjyHsGLx=rxg6PKYBNkPYAejgo7=CbyL3=HGLZLsAaJFQ@mail.gmail.com/
+Fixes: 3d252160b818 ("fs/pipe: Read pipe->{head,tail} atomically outside pipe->mutex")
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/pipe_fs_i.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
+index 3cc4f8eab853f..1f013ed7577ef 100644
+--- a/include/linux/pipe_fs_i.h
++++ b/include/linux/pipe_fs_i.h
+@@ -192,7 +192,7 @@ static inline bool pipe_empty(unsigned int head, unsigned int tail)
+ */
+ static inline unsigned int pipe_occupancy(unsigned int head, unsigned int tail)
+ {
+- return head - tail;
++ return (pipe_index_t)(head - tail);
+ }
+
+ /**
+--
+2.39.5
+
--- /dev/null
+From 72fecd3b278bbe1df4a004b5dd5b8c721b2a4183 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 4 Mar 2025 13:51:38 +0000
+Subject: fs/pipe: Read pipe->{head,tail} atomically outside pipe->mutex
+
+From: Linus Torvalds <torvalds@linux-foundation.org>
+
+[ Upstream commit 3d252160b818045f3a152b13756f6f37ca34639d ]
+
+pipe_readable(), pipe_writable(), and pipe_poll() can read "pipe->head"
+and "pipe->tail" outside of "pipe->mutex" critical section. When the
+head and the tail are read individually in that order, there is a window
+for interruption between the two reads in which both the head and the
+tail can be updated by concurrent readers and writers.
+
+One of the problematic scenarios observed with hackbench running
+multiple groups on a large server on a particular pipe inode is as
+follows:
+
+ pipe->head = 36
+ pipe->tail = 36
+
+ hackbench-118762 [057] ..... 1029.550548: pipe_write: *wakes up: pipe not full*
+ hackbench-118762 [057] ..... 1029.550548: pipe_write: head: 36 -> 37 [tail: 36]
+ hackbench-118762 [057] ..... 1029.550548: pipe_write: *wake up next reader 118740*
+ hackbench-118762 [057] ..... 1029.550548: pipe_write: *wake up next writer 118768*
+
+ hackbench-118768 [206] ..... 1029.55055X: pipe_write: *writer wakes up*
+ hackbench-118768 [206] ..... 1029.55055X: pipe_write: head = READ_ONCE(pipe->head) [37]
+ ... CPU 206 interrupted (exact wakeup was not traced but 118768 did read head at 37 in traces)
+
+ hackbench-118740 [057] ..... 1029.550558: pipe_read: *reader wakes up: pipe is not empty*
+ hackbench-118740 [057] ..... 1029.550558: pipe_read: tail: 36 -> 37 [head = 37]
+ hackbench-118740 [057] ..... 1029.550559: pipe_read: *pipe is empty; wakeup writer 118768*
+ hackbench-118740 [057] ..... 1029.550559: pipe_read: *sleeps*
+
+ hackbench-118766 [185] ..... 1029.550592: pipe_write: *New writer comes in*
+ hackbench-118766 [185] ..... 1029.550592: pipe_write: head: 37 -> 38 [tail: 37]
+ hackbench-118766 [185] ..... 1029.550592: pipe_write: *wakes up reader 118766*
+
+ hackbench-118740 [185] ..... 1029.550598: pipe_read: *reader wakes up; pipe not empty*
+ hackbench-118740 [185] ..... 1029.550599: pipe_read: tail: 37 -> 38 [head: 38]
+ hackbench-118740 [185] ..... 1029.550599: pipe_read: *pipe is empty*
+ hackbench-118740 [185] ..... 1029.550599: pipe_read: *reader sleeps; wakeup writer 118768*
+
+ ... CPU 206 switches back to writer
+ hackbench-118768 [206] ..... 1029.550601: pipe_write: tail = READ_ONCE(pipe->tail) [38]
+ hackbench-118768 [206] ..... 1029.550601: pipe_write: pipe_full()? (u32)(37 - 38) >= 16? Yes
+ hackbench-118768 [206] ..... 1029.550601: pipe_write: *writer goes back to sleep*
+
+ [ Tasks 118740 and 118768 can then indefinitely wait on each other. ]
+
+The unsigned arithmetic in pipe_occupancy() wraps around when
+"pipe->tail > pipe->head" leading to pipe_full() returning true despite
+the pipe being empty.
+
+The case of genuine wraparound of "pipe->head" is handled since pipe
+buffer has data allowing readers to make progress until the pipe->tail
+wraps too after which the reader will wakeup a sleeping writer, however,
+mistaking the pipe to be full when it is in fact empty can lead to
+readers and writers waiting on each other indefinitely.
+
+This issue became more problematic and surfaced as a hang in hackbench
+after the optimization in commit aaec5a95d596 ("pipe_read: don't wake up
+the writer if the pipe is still full") significantly reduced the number
+of spurious wakeups of writers that had previously helped mask the
+issue.
+
+To avoid missing any updates between the reads of "pipe->head" and
+"pipe->write", unionize the two with a single unsigned long
+"pipe->head_tail" member that can be loaded atomically.
+
+Using "pipe->head_tail" to read the head and the tail ensures the
+lockless checks do not miss any updates to the head or the tail and
+since those two are only updated under "pipe->mutex", it ensures that
+the head is always ahead of, or equal to the tail resulting in correct
+calculations.
+
+ [ prateek: commit log, testing on x86 platforms. ]
+
+Reported-and-debugged-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
+Closes: https://lore.kernel.org/lkml/e813814e-7094-4673-bc69-731af065a0eb@amd.com/
+Reported-by: Alexey Gladkov <legion@kernel.org>
+Closes: https://lore.kernel.org/all/Z8Wn0nTvevLRG_4m@example.org/
+Fixes: 8cefc107ca54 ("pipe: Use head and tail pointers for the ring, not cursor and length")
+Tested-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
+Reviewed-by: Oleg Nesterov <oleg@redhat.com>
+Tested-by: Alexey Gladkov <legion@kernel.org>
+Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/pipe.c | 19 ++++++++-----------
+ include/linux/pipe_fs_i.h | 39 +++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 45 insertions(+), 13 deletions(-)
+
+diff --git a/fs/pipe.c b/fs/pipe.c
+index 12b22c2723b7e..0b2b6ccb8ec52 100644
+--- a/fs/pipe.c
++++ b/fs/pipe.c
+@@ -210,11 +210,10 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = {
+ /* Done while waiting without holding the pipe lock - thus the READ_ONCE() */
+ static inline bool pipe_readable(const struct pipe_inode_info *pipe)
+ {
+- unsigned int head = READ_ONCE(pipe->head);
+- unsigned int tail = READ_ONCE(pipe->tail);
++ union pipe_index idx = { .head_tail = READ_ONCE(pipe->head_tail) };
+ unsigned int writers = READ_ONCE(pipe->writers);
+
+- return !pipe_empty(head, tail) || !writers;
++ return !pipe_empty(idx.head, idx.tail) || !writers;
+ }
+
+ static inline unsigned int pipe_update_tail(struct pipe_inode_info *pipe,
+@@ -416,11 +415,10 @@ static inline int is_packetized(struct file *file)
+ /* Done while waiting without holding the pipe lock - thus the READ_ONCE() */
+ static inline bool pipe_writable(const struct pipe_inode_info *pipe)
+ {
+- unsigned int head = READ_ONCE(pipe->head);
+- unsigned int tail = READ_ONCE(pipe->tail);
++ union pipe_index idx = { .head_tail = READ_ONCE(pipe->head_tail) };
+ unsigned int max_usage = READ_ONCE(pipe->max_usage);
+
+- return !pipe_full(head, tail, max_usage) ||
++ return !pipe_full(idx.head, idx.tail, max_usage) ||
+ !READ_ONCE(pipe->readers);
+ }
+
+@@ -658,7 +656,7 @@ pipe_poll(struct file *filp, poll_table *wait)
+ {
+ __poll_t mask;
+ struct pipe_inode_info *pipe = filp->private_data;
+- unsigned int head, tail;
++ union pipe_index idx;
+
+ /* Epoll has some historical nasty semantics, this enables them */
+ WRITE_ONCE(pipe->poll_usage, true);
+@@ -679,19 +677,18 @@ pipe_poll(struct file *filp, poll_table *wait)
+ * if something changes and you got it wrong, the poll
+ * table entry will wake you up and fix it.
+ */
+- head = READ_ONCE(pipe->head);
+- tail = READ_ONCE(pipe->tail);
++ idx.head_tail = READ_ONCE(pipe->head_tail);
+
+ mask = 0;
+ if (filp->f_mode & FMODE_READ) {
+- if (!pipe_empty(head, tail))
++ if (!pipe_empty(idx.head, idx.tail))
+ mask |= EPOLLIN | EPOLLRDNORM;
+ if (!pipe->writers && filp->f_pipe != pipe->w_counter)
+ mask |= EPOLLHUP;
+ }
+
+ if (filp->f_mode & FMODE_WRITE) {
+- if (!pipe_full(head, tail, pipe->max_usage))
++ if (!pipe_full(idx.head, idx.tail, pipe->max_usage))
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ /*
+ * Most Unices do not set EPOLLERR for FIFOs but on Linux they
+diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
+index 8ff23bf5a8197..3cc4f8eab853f 100644
+--- a/include/linux/pipe_fs_i.h
++++ b/include/linux/pipe_fs_i.h
+@@ -31,6 +31,33 @@ struct pipe_buffer {
+ unsigned long private;
+ };
+
++/*
++ * Really only alpha needs 32-bit fields, but
++ * might as well do it for 64-bit architectures
++ * since that's what we've historically done,
++ * and it makes 'head_tail' always be a simple
++ * 'unsigned long'.
++ */
++#ifdef CONFIG_64BIT
++typedef unsigned int pipe_index_t;
++#else
++typedef unsigned short pipe_index_t;
++#endif
++
++/*
++ * We have to declare this outside 'struct pipe_inode_info',
++ * but then we can't use 'union pipe_index' for an anonymous
++ * union, so we end up having to duplicate this declaration
++ * below. Annoying.
++ */
++union pipe_index {
++ unsigned long head_tail;
++ struct {
++ pipe_index_t head;
++ pipe_index_t tail;
++ };
++};
++
+ /**
+ * struct pipe_inode_info - a linux kernel pipe
+ * @mutex: mutex protecting the whole thing
+@@ -58,8 +85,16 @@ struct pipe_buffer {
+ struct pipe_inode_info {
+ struct mutex mutex;
+ wait_queue_head_t rd_wait, wr_wait;
+- unsigned int head;
+- unsigned int tail;
++
++ /* This has to match the 'union pipe_index' above */
++ union {
++ unsigned long head_tail;
++ struct {
++ pipe_index_t head;
++ pipe_index_t tail;
++ };
++ };
++
+ unsigned int max_usage;
+ unsigned int ring_size;
+ unsigned int nr_accounted;
+--
+2.39.5
+
--- /dev/null
+From 1a3d4407f07dc48df76d4a347977a5ec3eb3c620 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Mar 2025 16:37:50 +0000
+Subject: gpio: rcar: Fix missing of_node_put() call
+
+From: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+
+[ Upstream commit 391b41f983bf7ff853de44704d8e14e7cc648a9b ]
+
+of_parse_phandle_with_fixed_args() requires its caller to
+call into of_node_put() on the node pointer from the output
+structure, but such a call is currently missing.
+
+Call into of_node_put() to rectify that.
+
+Fixes: 159f8a0209af ("gpio-rcar: Add DT support")
+Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
+Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://lore.kernel.org/r/20250305163753.34913-2-fabrizio.castro.jz@renesas.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpio-rcar.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
+index 436ff763341ae..6641ed5cd8e1c 100644
+--- a/drivers/gpio/gpio-rcar.c
++++ b/drivers/gpio/gpio-rcar.c
+@@ -468,7 +468,12 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
+ p->info = *info;
+
+ ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
+- *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK;
++ if (ret) {
++ *npins = RCAR_MAX_GPIO_PER_BANK;
++ } else {
++ *npins = args.args[2];
++ of_node_put(args.np);
++ }
+
+ if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) {
+ dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n",
+--
+2.39.5
+
--- /dev/null
+From 43436c2243a787a9bae97fde7cd9e1656668bacc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 18 Feb 2025 00:50:13 +0800
+Subject: HID: google: fix unused variable warning under !CONFIG_ACPI
+
+From: Yu-Chun Lin <eleanor15x@gmail.com>
+
+[ Upstream commit 4bd0725c09f377ffaf22b834241f6c050742e4fc ]
+
+As reported by the kernel test robot, the following warning occurs:
+
+>> drivers/hid/hid-google-hammer.c:261:36: warning: 'cbas_ec_acpi_ids' defined but not used [-Wunused-const-variable=]
+ 261 | static const struct acpi_device_id cbas_ec_acpi_ids[] = {
+ | ^~~~~~~~~~~~~~~~
+
+The 'cbas_ec_acpi_ids' array is only used when CONFIG_ACPI is enabled.
+Wrapping its definition and 'MODULE_DEVICE_TABLE' in '#ifdef CONFIG_ACPI'
+prevents a compiler warning when ACPI is disabled.
+
+Fixes: eb1aac4c8744f75 ("HID: google: add support tablet mode switch for Whiskers")
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202501201141.jctFH5eB-lkp@intel.com/
+Signed-off-by: Yu-Chun Lin <eleanor15x@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-google-hammer.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c
+index 22683ec819aac..646ba5b92e0b2 100644
+--- a/drivers/hid/hid-google-hammer.c
++++ b/drivers/hid/hid-google-hammer.c
+@@ -268,11 +268,13 @@ static void cbas_ec_remove(struct platform_device *pdev)
+ mutex_unlock(&cbas_ec_reglock);
+ }
+
++#ifdef CONFIG_ACPI
+ static const struct acpi_device_id cbas_ec_acpi_ids[] = {
+ { "GOOG000B", 0 },
+ { }
+ };
+ MODULE_DEVICE_TABLE(acpi, cbas_ec_acpi_ids);
++#endif
+
+ #ifdef CONFIG_OF
+ static const struct of_device_id cbas_ec_of_match[] = {
+--
+2.39.5
+
--- /dev/null
+From 3d66f4bab1e58ca674edcff2cfa463d2ae75b913 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 15:41:33 -0800
+Subject: HID: hid-steam: Fix use-after-free when detaching device
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit e53fc232a65f7488ab75d03a5b95f06aaada7262 ]
+
+When a hid-steam device is removed it must clean up the client_hdev used for
+intercepting hidraw access. This can lead to scheduling deferred work to
+reattach the input device. Though the cleanup cancels the deferred work, this
+was done before the client_hdev itself is cleaned up, so it gets rescheduled.
+This patch fixes the ordering to make sure the deferred work is properly
+canceled.
+
+Reported-by: syzbot+0154da2d403396b2bd59@syzkaller.appspotmail.com
+Fixes: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work")
+Signed-off-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-steam.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
+index 7b35966898785..19b7bb0c3d7f9 100644
+--- a/drivers/hid/hid-steam.c
++++ b/drivers/hid/hid-steam.c
+@@ -1327,11 +1327,11 @@ static void steam_remove(struct hid_device *hdev)
+ return;
+ }
+
++ hid_destroy_device(steam->client_hdev);
+ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->work_connect);
+ cancel_work_sync(&steam->rumble_work);
+ cancel_work_sync(&steam->unregister_work);
+- hid_destroy_device(steam->client_hdev);
+ steam->client_hdev = NULL;
+ steam->client_opened = 0;
+ if (steam->quirks & STEAM_QUIRK_WIRELESS) {
+--
+2.39.5
+
--- /dev/null
+From 91e909a38359367cdb634d51e01eb05c58595867 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 18 Feb 2025 14:37:29 +0800
+Subject: HID: intel-ish-hid: Fix use-after-free issue in hid_ishtp_cl_remove()
+
+From: Zhang Lixu <lixu.zhang@intel.com>
+
+[ Upstream commit 823987841424289339fdb4ba90e6d2c3792836db ]
+
+During the `rmmod` operation for the `intel_ishtp_hid` driver, a
+use-after-free issue can occur in the hid_ishtp_cl_remove() function.
+The function hid_ishtp_cl_deinit() is called before ishtp_hid_remove(),
+which can lead to accessing freed memory or resources during the
+removal process.
+
+Call Trace:
+ ? ishtp_cl_send+0x168/0x220 [intel_ishtp]
+ ? hid_output_report+0xe3/0x150 [hid]
+ hid_ishtp_set_feature+0xb5/0x120 [intel_ishtp_hid]
+ ishtp_hid_request+0x7b/0xb0 [intel_ishtp_hid]
+ hid_hw_request+0x1f/0x40 [hid]
+ sensor_hub_set_feature+0x11f/0x190 [hid_sensor_hub]
+ _hid_sensor_power_state+0x147/0x1e0 [hid_sensor_trigger]
+ hid_sensor_runtime_resume+0x22/0x30 [hid_sensor_trigger]
+ sensor_hub_remove+0xa8/0xe0 [hid_sensor_hub]
+ hid_device_remove+0x49/0xb0 [hid]
+ hid_destroy_device+0x6f/0x90 [hid]
+ ishtp_hid_remove+0x42/0x70 [intel_ishtp_hid]
+ hid_ishtp_cl_remove+0x6b/0xb0 [intel_ishtp_hid]
+ ishtp_cl_device_remove+0x4a/0x60 [intel_ishtp]
+ ...
+
+Additionally, ishtp_hid_remove() is a HID level power off, which should
+occur before the ISHTP level disconnect.
+
+This patch resolves the issue by reordering the calls in
+hid_ishtp_cl_remove(). The function ishtp_hid_remove() is now
+called before hid_ishtp_cl_deinit().
+
+Fixes: f645a90e8ff7 ("HID: intel-ish-hid: ishtp-hid-client: use helper functions for connection")
+Signed-off-by: Zhang Lixu <lixu.zhang@intel.com>
+Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/intel-ish-hid/ishtp-hid-client.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c
+index fbd4f8ea1951b..af6a5afc1a93e 100644
+--- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c
++++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c
+@@ -833,9 +833,9 @@ static void hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
+ hid_ishtp_cl);
+
+ dev_dbg(ishtp_device(cl_device), "%s\n", __func__);
+- hid_ishtp_cl_deinit(hid_ishtp_cl);
+ ishtp_put_device(cl_device);
+ ishtp_hid_remove(client_data);
++ hid_ishtp_cl_deinit(hid_ishtp_cl);
+
+ hid_ishtp_cl = NULL;
+
+--
+2.39.5
+
--- /dev/null
+From a7fb776496c74904a24d1deee8dccefef1616364 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 18 Feb 2025 14:37:30 +0800
+Subject: HID: intel-ish-hid: Fix use-after-free issue in ishtp_hid_remove()
+
+From: Zhang Lixu <lixu.zhang@intel.com>
+
+[ Upstream commit 07583a0010696a17fb0942e0b499a62785c5fc9f ]
+
+The system can experience a random crash a few minutes after the driver is
+removed. This issue occurs due to improper handling of memory freeing in
+the ishtp_hid_remove() function.
+
+The function currently frees the `driver_data` directly within the loop
+that destroys the HID devices, which can lead to accessing freed memory.
+Specifically, `hid_destroy_device()` uses `driver_data` when it calls
+`hid_ishtp_set_feature()` to power off the sensor, so freeing
+`driver_data` beforehand can result in accessing invalid memory.
+
+This patch resolves the issue by storing the `driver_data` in a temporary
+variable before calling `hid_destroy_device()`, and then freeing the
+`driver_data` after the device is destroyed.
+
+Fixes: 0b28cb4bcb17 ("HID: intel-ish-hid: ISH HID client driver")
+Signed-off-by: Zhang Lixu <lixu.zhang@intel.com>
+Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/intel-ish-hid/ishtp-hid.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c
+index 00c6f0ebf3563..be2c62fc8251d 100644
+--- a/drivers/hid/intel-ish-hid/ishtp-hid.c
++++ b/drivers/hid/intel-ish-hid/ishtp-hid.c
+@@ -261,12 +261,14 @@ int ishtp_hid_probe(unsigned int cur_hid_dev,
+ */
+ void ishtp_hid_remove(struct ishtp_cl_data *client_data)
+ {
++ void *data;
+ int i;
+
+ for (i = 0; i < client_data->num_hid_devices; ++i) {
+ if (client_data->hid_sensor_hubs[i]) {
+- kfree(client_data->hid_sensor_hubs[i]->driver_data);
++ data = client_data->hid_sensor_hubs[i]->driver_data;
+ hid_destroy_device(client_data->hid_sensor_hubs[i]);
++ kfree(data);
+ client_data->hid_sensor_hubs[i] = NULL;
+ }
+ }
+--
+2.39.5
+
--- /dev/null
+From 7d57558e85b5e7ed3bab7479f76f6809d1039efc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Feb 2025 09:19:04 +0000
+Subject: hwmon: (ad7314) Validate leading zero bits and return error
+
+From: Erik Schumacher <erik.schumacher@iris-sensing.com>
+
+[ Upstream commit e278d5e8aef4c0a1d9a9fa8b8910d713a89aa800 ]
+
+Leading zero bits are sent on the bus before the temperature value is
+transmitted. If any of these bits are high, the connection might be
+unstable or there could be no AD7314 / ADT730x (or compatible) at all.
+Return -EIO in that case.
+
+Signed-off-by: Erik Schumacher <erik.schumacher@iris-sensing.com>
+Fixes: 4f3a659581cab ("hwmon: AD7314 driver (ported from IIO)")
+Link: https://lore.kernel.org/r/24a50c2981a318580aca8f50d23be7987b69ea00.camel@iris-sensing.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ad7314.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
+index 7802bbf5f9587..59424103f6348 100644
+--- a/drivers/hwmon/ad7314.c
++++ b/drivers/hwmon/ad7314.c
+@@ -22,11 +22,13 @@
+ */
+ #define AD7314_TEMP_MASK 0x7FE0
+ #define AD7314_TEMP_SHIFT 5
++#define AD7314_LEADING_ZEROS_MASK BIT(15)
+
+ /*
+ * ADT7301 and ADT7302 temperature masks
+ */
+ #define ADT7301_TEMP_MASK 0x3FFF
++#define ADT7301_LEADING_ZEROS_MASK (BIT(15) | BIT(14))
+
+ enum ad7314_variant {
+ adt7301,
+@@ -65,12 +67,20 @@ static ssize_t ad7314_temperature_show(struct device *dev,
+ return ret;
+ switch (spi_get_device_id(chip->spi_dev)->driver_data) {
+ case ad7314:
++ if (ret & AD7314_LEADING_ZEROS_MASK) {
++ /* Invalid read-out, leading zero part is missing */
++ return -EIO;
++ }
+ data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT;
+ data = sign_extend32(data, 9);
+
+ return sprintf(buf, "%d\n", 250 * data);
+ case adt7301:
+ case adt7302:
++ if (ret & ADT7301_LEADING_ZEROS_MASK) {
++ /* Invalid read-out, leading zero part is missing */
++ return -EIO;
++ }
+ /*
+ * Documented as a 13 bit twos complement register
+ * with a sign bit - which is a 14 bit 2's complement
+--
+2.39.5
+
--- /dev/null
+From 969b6cce905831c799605c3c924a3c2965e0d29a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 3 Mar 2025 07:57:33 -0500
+Subject: hwmon: fix a NULL vs IS_ERR_OR_NULL() check in xgene_hwmon_probe()
+
+From: Xinghuo Chen <xinghuo.chen@foxmail.com>
+
+[ Upstream commit 10fce7ebe888fa8c97eee7e317a47e7603e5e78d ]
+
+The devm_memremap() function returns error pointers on error,
+it doesn't return NULL.
+
+Fixes: c7cefce03e69 ("hwmon: (xgene) access mailbox as RAM")
+Signed-off-by: Xinghuo Chen <xinghuo.chen@foxmail.com>
+Link: https://lore.kernel.org/r/tencent_9AD8E7683EC29CAC97496B44F3F865BA070A@qq.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/xgene-hwmon.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c
+index 5e0759a70f6d5..92d82faf237fc 100644
+--- a/drivers/hwmon/xgene-hwmon.c
++++ b/drivers/hwmon/xgene-hwmon.c
+@@ -706,7 +706,7 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
+ goto out;
+ }
+
+- if (!ctx->pcc_comm_addr) {
++ if (IS_ERR_OR_NULL(ctx->pcc_comm_addr)) {
+ dev_err(&pdev->dev,
+ "Failed to ioremap PCC comm region\n");
+ rc = -ENOMEM;
+--
+2.39.5
+
--- /dev/null
+From 261dddb760f72db9946d956ee7e44c9dcaa11f4f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 13:57:53 +0100
+Subject: hwmon: (ntc_thermistor) Fix the ncpXXxh103 sensor table
+
+From: Maud Spierings <maudspierings@gocontroll.com>
+
+[ Upstream commit 1c7932d5ae0f5c22fa52ac811b4c427bbca5aff5 ]
+
+I could not find a single table that has the values currently present in
+the table, change it to the actual values that can be found in [1]/[2]
+and [3] (page 15 column 2)
+
+[1]: https://www.murata.com/products/productdetail?partno=NCP15XH103F03RC
+[2]: https://www.murata.com/products/productdata/8796836626462/NTHCG83.txt?1437969843000
+[3]: https://nl.mouser.com/datasheet/2/281/r44e-522712.pdf
+
+Fixes: 54ce3a0d8011 ("hwmon: (ntc_thermistor) Add support for ncpXXxh103")
+Signed-off-by: Maud Spierings <maudspierings@gocontroll.com>
+Link: https://lore.kernel.org/r/20250227-ntc_thermistor_fixes-v1-3-70fa73200b52@gocontroll.com
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/ntc_thermistor.c | 66 +++++++++++++++++-----------------
+ 1 file changed, 33 insertions(+), 33 deletions(-)
+
+diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
+index b5352900463fb..0d29c8f97ba7c 100644
+--- a/drivers/hwmon/ntc_thermistor.c
++++ b/drivers/hwmon/ntc_thermistor.c
+@@ -181,40 +181,40 @@ static const struct ntc_compensation ncpXXwf104[] = {
+ };
+
+ static const struct ntc_compensation ncpXXxh103[] = {
+- { .temp_c = -40, .ohm = 247565 },
+- { .temp_c = -35, .ohm = 181742 },
+- { .temp_c = -30, .ohm = 135128 },
+- { .temp_c = -25, .ohm = 101678 },
+- { .temp_c = -20, .ohm = 77373 },
+- { .temp_c = -15, .ohm = 59504 },
+- { .temp_c = -10, .ohm = 46222 },
+- { .temp_c = -5, .ohm = 36244 },
+- { .temp_c = 0, .ohm = 28674 },
+- { .temp_c = 5, .ohm = 22878 },
+- { .temp_c = 10, .ohm = 18399 },
+- { .temp_c = 15, .ohm = 14910 },
+- { .temp_c = 20, .ohm = 12169 },
++ { .temp_c = -40, .ohm = 195652 },
++ { .temp_c = -35, .ohm = 148171 },
++ { .temp_c = -30, .ohm = 113347 },
++ { .temp_c = -25, .ohm = 87559 },
++ { .temp_c = -20, .ohm = 68237 },
++ { .temp_c = -15, .ohm = 53650 },
++ { .temp_c = -10, .ohm = 42506 },
++ { .temp_c = -5, .ohm = 33892 },
++ { .temp_c = 0, .ohm = 27219 },
++ { .temp_c = 5, .ohm = 22021 },
++ { .temp_c = 10, .ohm = 17926 },
++ { .temp_c = 15, .ohm = 14674 },
++ { .temp_c = 20, .ohm = 12081 },
+ { .temp_c = 25, .ohm = 10000 },
+- { .temp_c = 30, .ohm = 8271 },
+- { .temp_c = 35, .ohm = 6883 },
+- { .temp_c = 40, .ohm = 5762 },
+- { .temp_c = 45, .ohm = 4851 },
+- { .temp_c = 50, .ohm = 4105 },
+- { .temp_c = 55, .ohm = 3492 },
+- { .temp_c = 60, .ohm = 2985 },
+- { .temp_c = 65, .ohm = 2563 },
+- { .temp_c = 70, .ohm = 2211 },
+- { .temp_c = 75, .ohm = 1915 },
+- { .temp_c = 80, .ohm = 1666 },
+- { .temp_c = 85, .ohm = 1454 },
+- { .temp_c = 90, .ohm = 1275 },
+- { .temp_c = 95, .ohm = 1121 },
+- { .temp_c = 100, .ohm = 990 },
+- { .temp_c = 105, .ohm = 876 },
+- { .temp_c = 110, .ohm = 779 },
+- { .temp_c = 115, .ohm = 694 },
+- { .temp_c = 120, .ohm = 620 },
+- { .temp_c = 125, .ohm = 556 },
++ { .temp_c = 30, .ohm = 8315 },
++ { .temp_c = 35, .ohm = 6948 },
++ { .temp_c = 40, .ohm = 5834 },
++ { .temp_c = 45, .ohm = 4917 },
++ { .temp_c = 50, .ohm = 4161 },
++ { .temp_c = 55, .ohm = 3535 },
++ { .temp_c = 60, .ohm = 3014 },
++ { .temp_c = 65, .ohm = 2586 },
++ { .temp_c = 70, .ohm = 2228 },
++ { .temp_c = 75, .ohm = 1925 },
++ { .temp_c = 80, .ohm = 1669 },
++ { .temp_c = 85, .ohm = 1452 },
++ { .temp_c = 90, .ohm = 1268 },
++ { .temp_c = 95, .ohm = 1110 },
++ { .temp_c = 100, .ohm = 974 },
++ { .temp_c = 105, .ohm = 858 },
++ { .temp_c = 110, .ohm = 758 },
++ { .temp_c = 115, .ohm = 672 },
++ { .temp_c = 120, .ohm = 596 },
++ { .temp_c = 125, .ohm = 531 },
+ };
+
+ /*
+--
+2.39.5
+
--- /dev/null
+From 8f8f1a5c09a13b7adfabd32e00492365d51e950f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 22:24:55 +0000
+Subject: hwmon: (pmbus) Initialise page count in pmbus_identify()
+
+From: Titus Rwantare <titusr@google.com>
+
+[ Upstream commit 6b6e2e8fd0de3fa7c6f4f8fe6841b01770b2e7bc ]
+
+The `pmbus_identify()` function fails to correctly determine the number
+of supported pages on PMBus devices. This occurs because `info->pages`
+is implicitly zero-initialised, and `pmbus_set_page()` does not perform
+writes to the page register if `info->pages` is not yet initialised.
+Without this patch, `info->pages` is always set to the maximum after
+scanning.
+
+This patch initialises `info->pages` to `PMBUS_PAGES` before the probing
+loop, enabling `pmbus_set_page()` writes to make it out onto the bus
+correctly identifying the number of pages. `PMBUS_PAGES` seemed like a
+reasonable non-zero number because that's the current result of the
+identification process.
+
+Testing was done with a PMBus device in QEMU.
+
+Signed-off-by: Titus Rwantare <titusr@google.com>
+Fixes: 442aba78728e7 ("hwmon: PMBus device driver")
+Link: https://lore.kernel.org/r/20250227222455.2583468-1-titusr@google.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hwmon/pmbus/pmbus.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
+index ec40c5c599543..59424dc518c8f 100644
+--- a/drivers/hwmon/pmbus/pmbus.c
++++ b/drivers/hwmon/pmbus/pmbus.c
+@@ -103,6 +103,8 @@ static int pmbus_identify(struct i2c_client *client,
+ if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) {
+ int page;
+
++ info->pages = PMBUS_PAGES;
++
+ for (page = 1; page < PMBUS_PAGES; page++) {
+ if (pmbus_set_page(client, page, 0xff) < 0)
+ break;
+--
+2.39.5
+
--- /dev/null
+From 865886bdbff195032ade4e428c2553e802fe0bd0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 08:26:42 +0000
+Subject: llc: do not use skb_get() before dev_queue_xmit()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 64e6a754d33d31aa844b3ee66fb93ac84ca1565e ]
+
+syzbot is able to crash hosts [1], using llc and devices
+not supporting IFF_TX_SKB_SHARING.
+
+In this case, e1000 driver calls eth_skb_pad(), while
+the skb is shared.
+
+Simply replace skb_get() by skb_clone() in net/llc/llc_s_ac.c
+
+Note that e1000 driver might have an issue with pktgen,
+because it does not clear IFF_TX_SKB_SHARING, this is an
+orthogonal change.
+
+We need to audit other skb_get() uses in net/llc.
+
+[1]
+
+kernel BUG at net/core/skbuff.c:2178 !
+Oops: invalid opcode: 0000 [#1] PREEMPT SMP KASAN NOPTI
+CPU: 0 UID: 0 PID: 16371 Comm: syz.2.2764 Not tainted 6.14.0-rc4-syzkaller-00052-gac9c34d1e45a #0
+Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014
+ RIP: 0010:pskb_expand_head+0x6ce/0x1240 net/core/skbuff.c:2178
+Call Trace:
+ <TASK>
+ __skb_pad+0x18a/0x610 net/core/skbuff.c:2466
+ __skb_put_padto include/linux/skbuff.h:3843 [inline]
+ skb_put_padto include/linux/skbuff.h:3862 [inline]
+ eth_skb_pad include/linux/etherdevice.h:656 [inline]
+ e1000_xmit_frame+0x2d99/0x5800 drivers/net/ethernet/intel/e1000/e1000_main.c:3128
+ __netdev_start_xmit include/linux/netdevice.h:5151 [inline]
+ netdev_start_xmit include/linux/netdevice.h:5160 [inline]
+ xmit_one net/core/dev.c:3806 [inline]
+ dev_hard_start_xmit+0x9a/0x7b0 net/core/dev.c:3822
+ sch_direct_xmit+0x1ae/0xc30 net/sched/sch_generic.c:343
+ __dev_xmit_skb net/core/dev.c:4045 [inline]
+ __dev_queue_xmit+0x13d4/0x43e0 net/core/dev.c:4621
+ dev_queue_xmit include/linux/netdevice.h:3313 [inline]
+ llc_sap_action_send_test_c+0x268/0x320 net/llc/llc_s_ac.c:144
+ llc_exec_sap_trans_actions net/llc/llc_sap.c:153 [inline]
+ llc_sap_next_state net/llc/llc_sap.c:182 [inline]
+ llc_sap_state_process+0x239/0x510 net/llc/llc_sap.c:209
+ llc_ui_sendmsg+0xd0d/0x14e0 net/llc/af_llc.c:993
+ sock_sendmsg_nosec net/socket.c:718 [inline]
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Reported-by: syzbot+da65c993ae113742a25f@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/netdev/67c020c0.050a0220.222324.0011.GAE@google.com/T/#u
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/llc/llc_s_ac.c | 49 +++++++++++++++++++++++++---------------------
+ 1 file changed, 27 insertions(+), 22 deletions(-)
+
+diff --git a/net/llc/llc_s_ac.c b/net/llc/llc_s_ac.c
+index 06fb8e6944b06..7a0cae9a81114 100644
+--- a/net/llc/llc_s_ac.c
++++ b/net/llc/llc_s_ac.c
+@@ -24,7 +24,7 @@
+ #include <net/llc_s_ac.h>
+ #include <net/llc_s_ev.h>
+ #include <net/llc_sap.h>
+-
++#include <net/sock.h>
+
+ /**
+ * llc_sap_action_unitdata_ind - forward UI PDU to network layer
+@@ -40,6 +40,26 @@ int llc_sap_action_unitdata_ind(struct llc_sap *sap, struct sk_buff *skb)
+ return 0;
+ }
+
++static int llc_prepare_and_xmit(struct sk_buff *skb)
++{
++ struct llc_sap_state_ev *ev = llc_sap_ev(skb);
++ struct sk_buff *nskb;
++ int rc;
++
++ rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
++ if (rc)
++ return rc;
++
++ nskb = skb_clone(skb, GFP_ATOMIC);
++ if (!nskb)
++ return -ENOMEM;
++
++ if (skb->sk)
++ skb_set_owner_w(nskb, skb->sk);
++
++ return dev_queue_xmit(nskb);
++}
++
+ /**
+ * llc_sap_action_send_ui - sends UI PDU resp to UNITDATA REQ to MAC layer
+ * @sap: SAP
+@@ -52,17 +72,12 @@ int llc_sap_action_unitdata_ind(struct llc_sap *sap, struct sk_buff *skb)
+ int llc_sap_action_send_ui(struct llc_sap *sap, struct sk_buff *skb)
+ {
+ struct llc_sap_state_ev *ev = llc_sap_ev(skb);
+- int rc;
+
+ llc_pdu_header_init(skb, LLC_PDU_TYPE_U, ev->saddr.lsap,
+ ev->daddr.lsap, LLC_PDU_CMD);
+ llc_pdu_init_as_ui_cmd(skb);
+- rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
+- if (likely(!rc)) {
+- skb_get(skb);
+- rc = dev_queue_xmit(skb);
+- }
+- return rc;
++
++ return llc_prepare_and_xmit(skb);
+ }
+
+ /**
+@@ -77,17 +92,12 @@ int llc_sap_action_send_ui(struct llc_sap *sap, struct sk_buff *skb)
+ int llc_sap_action_send_xid_c(struct llc_sap *sap, struct sk_buff *skb)
+ {
+ struct llc_sap_state_ev *ev = llc_sap_ev(skb);
+- int rc;
+
+ llc_pdu_header_init(skb, LLC_PDU_TYPE_U_XID, ev->saddr.lsap,
+ ev->daddr.lsap, LLC_PDU_CMD);
+ llc_pdu_init_as_xid_cmd(skb, LLC_XID_NULL_CLASS_2, 0);
+- rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
+- if (likely(!rc)) {
+- skb_get(skb);
+- rc = dev_queue_xmit(skb);
+- }
+- return rc;
++
++ return llc_prepare_and_xmit(skb);
+ }
+
+ /**
+@@ -133,17 +143,12 @@ int llc_sap_action_send_xid_r(struct llc_sap *sap, struct sk_buff *skb)
+ int llc_sap_action_send_test_c(struct llc_sap *sap, struct sk_buff *skb)
+ {
+ struct llc_sap_state_ev *ev = llc_sap_ev(skb);
+- int rc;
+
+ llc_pdu_header_init(skb, LLC_PDU_TYPE_U, ev->saddr.lsap,
+ ev->daddr.lsap, LLC_PDU_CMD);
+ llc_pdu_init_as_test_cmd(skb);
+- rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
+- if (likely(!rc)) {
+- skb_get(skb);
+- rc = dev_queue_xmit(skb);
+- }
+- return rc;
++
++ return llc_prepare_and_xmit(skb);
+ }
+
+ int llc_sap_action_send_test_r(struct llc_sap *sap, struct sk_buff *skb)
+--
+2.39.5
+
--- /dev/null
+From 53d66d386d1441b309518ee5ce915a5b1ea30d18 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 4 Mar 2025 13:59:51 +0800
+Subject: mctp i3c: handle NULL header address
+
+From: Matt Johnston <matt@codeconstruct.com.au>
+
+[ Upstream commit cf7ee25e70c6edfac4553d6b671e8b19db1d9573 ]
+
+daddr can be NULL if there is no neighbour table entry present,
+in that case the tx packet should be dropped.
+
+saddr will usually be set by MCTP core, but check for NULL in case a
+packet is transmitted by a different protocol.
+
+Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
+Fixes: c8755b29b58e ("mctp i3c: MCTP I3C driver")
+Link: https://patch.msgid.link/20250304-mctp-i3c-null-v1-1-4416bbd56540@codeconstruct.com.au
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/mctp/mctp-i3c.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c
+index ee9d562f0817c..a2b15cddf46e6 100644
+--- a/drivers/net/mctp/mctp-i3c.c
++++ b/drivers/net/mctp/mctp-i3c.c
+@@ -507,6 +507,9 @@ static int mctp_i3c_header_create(struct sk_buff *skb, struct net_device *dev,
+ {
+ struct mctp_i3c_internal_hdr *ihdr;
+
++ if (!daddr || !saddr)
++ return -EINVAL;
++
+ skb_push(skb, sizeof(struct mctp_i3c_internal_hdr));
+ skb_reset_mac_header(skb);
+ ihdr = (void *)skb_mac_header(skb);
+--
+2.39.5
+
--- /dev/null
+From 1c1e812ef07858990c51cdd6ea36d1a86583319d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 4 Mar 2025 09:50:23 +0100
+Subject: net: dsa: mt7530: Fix traffic flooding for MMIO devices
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+[ Upstream commit ccc2f5a436fbb0ae1fb598932a9b8e48423c1959 ]
+
+On MMIO devices (e.g. MT7988 or EN7581) unicast traffic received on lanX
+port is flooded on all other user ports if the DSA switch is configured
+without VLAN support since PORT_MATRIX in PCR regs contains all user
+ports. Similar to MDIO devices (e.g. MT7530 and MT7531) fix the issue
+defining default VLAN-ID 0 for MT7530 MMIO devices.
+
+Fixes: 110c18bfed414 ("net: dsa: mt7530: introduce driver for MT7988 built-in switch")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/20250304-mt7988-flooding-fix-v1-1-905523ae83e9@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
+index d84ee1b419a61..abc979fbb45d1 100644
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2590,7 +2590,8 @@ mt7531_setup_common(struct dsa_switch *ds)
+ if (ret < 0)
+ return ret;
+
+- return 0;
++ /* Setup VLAN ID 0 for VLAN-unaware bridges */
++ return mt7530_setup_vlan0(priv);
+ }
+
+ static int
+@@ -2686,11 +2687,6 @@ mt7531_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- /* Setup VLAN ID 0 for VLAN-unaware bridges */
+- ret = mt7530_setup_vlan0(priv);
+- if (ret)
+- return ret;
+-
+ ds->assisted_learning_on_cpu_port = true;
+ ds->mtu_enforcement_ingress = true;
+
+--
+2.39.5
+
--- /dev/null
+From ea153e4250074fcf797180f8927c3a4467905965 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Mar 2025 15:11:13 +0100
+Subject: net: ethtool: netlink: Allow NULL nlattrs when getting a phy_device
+
+From: Maxime Chevallier <maxime.chevallier@bootlin.com>
+
+[ Upstream commit 637399bf7e77797811adf340090b561a8f9d1213 ]
+
+ethnl_req_get_phydev() is used to lookup a phy_device, in the case an
+ethtool netlink command targets a specific phydev within a netdev's
+topology.
+
+It takes as a parameter a const struct nlattr *header that's used for
+error handling :
+
+ if (!phydev) {
+ NL_SET_ERR_MSG_ATTR(extack, header,
+ "no phy matching phyindex");
+ return ERR_PTR(-ENODEV);
+ }
+
+In the notify path after a ->set operation however, there's no request
+attributes available.
+
+The typical callsite for the above function looks like:
+
+ phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_XXX_HEADER],
+ info->extack);
+
+So, when tb is NULL (such as in the ethnl notify path), we have a nice
+crash.
+
+It turns out that there's only the PLCA command that is in that case, as
+the other phydev-specific commands don't have a notification.
+
+This commit fixes the crash by passing the cmd index and the nlattr
+array separately, allowing NULL-checking it directly inside the helper.
+
+Fixes: c15e065b46dc ("net: ethtool: Allow passing a phy index for some commands")
+Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
+Reported-by: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
+Link: https://patch.msgid.link/20250301141114.97204-1-maxime.chevallier@bootlin.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ethtool/cabletest.c | 8 ++++----
+ net/ethtool/linkstate.c | 2 +-
+ net/ethtool/netlink.c | 6 +++---
+ net/ethtool/netlink.h | 5 +++--
+ net/ethtool/phy.c | 2 +-
+ net/ethtool/plca.c | 6 +++---
+ net/ethtool/pse-pd.c | 4 ++--
+ net/ethtool/stats.c | 2 +-
+ net/ethtool/strset.c | 2 +-
+ 9 files changed, 19 insertions(+), 18 deletions(-)
+
+diff --git a/net/ethtool/cabletest.c b/net/ethtool/cabletest.c
+index f22051f33868a..84096f6b0236e 100644
+--- a/net/ethtool/cabletest.c
++++ b/net/ethtool/cabletest.c
+@@ -72,8 +72,8 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
+ dev = req_info.dev;
+
+ rtnl_lock();
+- phydev = ethnl_req_get_phydev(&req_info,
+- tb[ETHTOOL_A_CABLE_TEST_HEADER],
++ phydev = ethnl_req_get_phydev(&req_info, tb,
++ ETHTOOL_A_CABLE_TEST_HEADER,
+ info->extack);
+ if (IS_ERR_OR_NULL(phydev)) {
+ ret = -EOPNOTSUPP;
+@@ -339,8 +339,8 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
+ goto out_dev_put;
+
+ rtnl_lock();
+- phydev = ethnl_req_get_phydev(&req_info,
+- tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
++ phydev = ethnl_req_get_phydev(&req_info, tb,
++ ETHTOOL_A_CABLE_TEST_TDR_HEADER,
+ info->extack);
+ if (IS_ERR_OR_NULL(phydev)) {
+ ret = -EOPNOTSUPP;
+diff --git a/net/ethtool/linkstate.c b/net/ethtool/linkstate.c
+index af19e1bed303f..05a5f72c99fab 100644
+--- a/net/ethtool/linkstate.c
++++ b/net/ethtool/linkstate.c
+@@ -103,7 +103,7 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
+ struct phy_device *phydev;
+ int ret;
+
+- phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_LINKSTATE_HEADER],
++ phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_LINKSTATE_HEADER,
+ info->extack);
+ if (IS_ERR(phydev)) {
+ ret = PTR_ERR(phydev);
+diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
+index 4d18dc29b3043..e233dfc8ca4be 100644
+--- a/net/ethtool/netlink.c
++++ b/net/ethtool/netlink.c
+@@ -210,7 +210,7 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
+ }
+
+ struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info,
+- const struct nlattr *header,
++ struct nlattr **tb, unsigned int header,
+ struct netlink_ext_ack *extack)
+ {
+ struct phy_device *phydev;
+@@ -224,8 +224,8 @@ struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info,
+ return req_info->dev->phydev;
+
+ phydev = phy_link_topo_get_phy(req_info->dev, req_info->phy_index);
+- if (!phydev) {
+- NL_SET_ERR_MSG_ATTR(extack, header,
++ if (!phydev && tb) {
++ NL_SET_ERR_MSG_ATTR(extack, tb[header],
+ "no phy matching phyindex");
+ return ERR_PTR(-ENODEV);
+ }
+diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
+index 203b08eb6c6f6..5e176938d6d22 100644
+--- a/net/ethtool/netlink.h
++++ b/net/ethtool/netlink.h
+@@ -275,7 +275,8 @@ static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info)
+ * ethnl_req_get_phydev() - Gets the phy_device targeted by this request,
+ * if any. Must be called under rntl_lock().
+ * @req_info: The ethnl request to get the phy from.
+- * @header: The netlink header, used for error reporting.
++ * @tb: The netlink attributes array, for error reporting.
++ * @header: The netlink header index, used for error reporting.
+ * @extack: The netlink extended ACK, for error reporting.
+ *
+ * The caller must hold RTNL, until it's done interacting with the returned
+@@ -289,7 +290,7 @@ static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info)
+ * is returned.
+ */
+ struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info,
+- const struct nlattr *header,
++ struct nlattr **tb, unsigned int header,
+ struct netlink_ext_ack *extack);
+
+ /**
+diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c
+index ed8f690f6bac8..e067cc234419d 100644
+--- a/net/ethtool/phy.c
++++ b/net/ethtool/phy.c
+@@ -125,7 +125,7 @@ static int ethnl_phy_parse_request(struct ethnl_req_info *req_base,
+ struct phy_req_info *req_info = PHY_REQINFO(req_base);
+ struct phy_device *phydev;
+
+- phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PHY_HEADER],
++ phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PHY_HEADER,
+ extack);
+ if (!phydev)
+ return 0;
+diff --git a/net/ethtool/plca.c b/net/ethtool/plca.c
+index d95d92f173a6d..e1f7820a6158f 100644
+--- a/net/ethtool/plca.c
++++ b/net/ethtool/plca.c
+@@ -62,7 +62,7 @@ static int plca_get_cfg_prepare_data(const struct ethnl_req_info *req_base,
+ struct phy_device *phydev;
+ int ret;
+
+- phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PLCA_HEADER],
++ phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PLCA_HEADER,
+ info->extack);
+ // check that the PHY device is available and connected
+ if (IS_ERR_OR_NULL(phydev)) {
+@@ -152,7 +152,7 @@ ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info)
+ bool mod = false;
+ int ret;
+
+- phydev = ethnl_req_get_phydev(req_info, tb[ETHTOOL_A_PLCA_HEADER],
++ phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PLCA_HEADER,
+ info->extack);
+ // check that the PHY device is available and connected
+ if (IS_ERR_OR_NULL(phydev))
+@@ -211,7 +211,7 @@ static int plca_get_status_prepare_data(const struct ethnl_req_info *req_base,
+ struct phy_device *phydev;
+ int ret;
+
+- phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PLCA_HEADER],
++ phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PLCA_HEADER,
+ info->extack);
+ // check that the PHY device is available and connected
+ if (IS_ERR_OR_NULL(phydev)) {
+diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c
+index a0705edca22a1..71843de832cca 100644
+--- a/net/ethtool/pse-pd.c
++++ b/net/ethtool/pse-pd.c
+@@ -64,7 +64,7 @@ static int pse_prepare_data(const struct ethnl_req_info *req_base,
+ if (ret < 0)
+ return ret;
+
+- phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PSE_HEADER],
++ phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PSE_HEADER,
+ info->extack);
+ if (IS_ERR(phydev))
+ return -ENODEV;
+@@ -261,7 +261,7 @@ ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
+ struct phy_device *phydev;
+ int ret;
+
+- phydev = ethnl_req_get_phydev(req_info, tb[ETHTOOL_A_PSE_HEADER],
++ phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PSE_HEADER,
+ info->extack);
+ ret = ethnl_set_pse_validate(phydev, info);
+ if (ret)
+diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c
+index f4d822c225db6..273ae4ff343fe 100644
+--- a/net/ethtool/stats.c
++++ b/net/ethtool/stats.c
+@@ -128,7 +128,7 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
+ struct phy_device *phydev;
+ int ret;
+
+- phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_STATS_HEADER],
++ phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_STATS_HEADER,
+ info->extack);
+ if (IS_ERR(phydev))
+ return PTR_ERR(phydev);
+diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c
+index b3382b3cf325c..b9400d18f01d5 100644
+--- a/net/ethtool/strset.c
++++ b/net/ethtool/strset.c
+@@ -299,7 +299,7 @@ static int strset_prepare_data(const struct ethnl_req_info *req_base,
+ return 0;
+ }
+
+- phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_HEADER_FLAGS],
++ phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_HEADER_FLAGS,
+ info->extack);
+
+ /* phydev can be NULL, check for errors only */
+--
+2.39.5
+
--- /dev/null
+From 89428d7359f8da4806dc4b3c5328ba4d5e4d3420 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jan 2025 07:05:12 +0100
+Subject: net: ethtool: plumb PHY stats to PHY drivers
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ Upstream commit b7a2c1fe6b55364e61b4b54b991eb43a47bb1104 ]
+
+Introduce support for standardized PHY statistics reporting in ethtool
+by extending the PHYLIB framework. Add the functions
+phy_ethtool_get_phy_stats() and phy_ethtool_get_link_ext_stats() to
+provide a consistent interface for retrieving PHY-level and
+link-specific statistics. These functions are used within the ethtool
+implementation to avoid direct access to the phy_device structure
+outside of the PHYLIB framework.
+
+A new structure, ethtool_phy_stats, is introduced to standardize PHY
+statistics such as packet counts, byte counts, and error counters.
+Drivers are updated to include callbacks for retrieving PHY and
+link-specific statistics, ensuring values are explicitly set only for
+supported fields, initialized with ETHTOOL_STAT_NOT_SET to avoid
+ambiguity.
+
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Stable-dep-of: 637399bf7e77 ("net: ethtool: netlink: Allow NULL nlattrs when getting a phy_device")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/phy.c | 43 ++++++++++++++++++++++++++++++++++++
+ drivers/net/phy/phy_device.c | 2 ++
+ include/linux/ethtool.h | 23 +++++++++++++++++++
+ include/linux/phy.h | 36 ++++++++++++++++++++++++++++++
+ include/linux/phylib_stubs.h | 42 +++++++++++++++++++++++++++++++++++
+ net/ethtool/linkstate.c | 5 +++--
+ net/ethtool/stats.c | 18 +++++++++++++++
+ 7 files changed, 167 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
+index 4f3e742907cb6..c9cfdc33fc5f1 100644
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -615,6 +615,49 @@ int phy_ethtool_get_stats(struct phy_device *phydev,
+ }
+ EXPORT_SYMBOL(phy_ethtool_get_stats);
+
++/**
++ * __phy_ethtool_get_phy_stats - Retrieve standardized PHY statistics
++ * @phydev: Pointer to the PHY device
++ * @phy_stats: Pointer to ethtool_eth_phy_stats structure
++ * @phydev_stats: Pointer to ethtool_phy_stats structure
++ *
++ * Fetches PHY statistics using a kernel-defined interface for consistent
++ * diagnostics. Unlike phy_ethtool_get_stats(), which allows custom stats,
++ * this function enforces a standardized format for better interoperability.
++ */
++void __phy_ethtool_get_phy_stats(struct phy_device *phydev,
++ struct ethtool_eth_phy_stats *phy_stats,
++ struct ethtool_phy_stats *phydev_stats)
++{
++ if (!phydev->drv || !phydev->drv->get_phy_stats)
++ return;
++
++ mutex_lock(&phydev->lock);
++ phydev->drv->get_phy_stats(phydev, phy_stats, phydev_stats);
++ mutex_unlock(&phydev->lock);
++}
++
++/**
++ * __phy_ethtool_get_link_ext_stats - Retrieve extended link statistics for a PHY
++ * @phydev: Pointer to the PHY device
++ * @link_stats: Pointer to the structure to store extended link statistics
++ *
++ * Populates the ethtool_link_ext_stats structure with link down event counts
++ * and additional driver-specific link statistics, if available.
++ */
++void __phy_ethtool_get_link_ext_stats(struct phy_device *phydev,
++ struct ethtool_link_ext_stats *link_stats)
++{
++ link_stats->link_down_events = READ_ONCE(phydev->link_down_events);
++
++ if (!phydev->drv || !phydev->drv->get_link_stats)
++ return;
++
++ mutex_lock(&phydev->lock);
++ phydev->drv->get_link_stats(phydev, link_stats);
++ mutex_unlock(&phydev->lock);
++}
++
+ /**
+ * phy_ethtool_get_plca_cfg - Get PLCA RS configuration
+ * @phydev: the phy_device struct
+diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
+index 499797646580e..119dfa2d6643a 100644
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -3776,6 +3776,8 @@ static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
+ static const struct phylib_stubs __phylib_stubs = {
+ .hwtstamp_get = __phy_hwtstamp_get,
+ .hwtstamp_set = __phy_hwtstamp_set,
++ .get_phy_stats = __phy_ethtool_get_phy_stats,
++ .get_link_ext_stats = __phy_ethtool_get_link_ext_stats,
+ };
+
+ static void phylib_register_stubs(void)
+diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
+index b8b935b526033..b0ed740ca749b 100644
+--- a/include/linux/ethtool.h
++++ b/include/linux/ethtool.h
+@@ -412,6 +412,29 @@ struct ethtool_eth_phy_stats {
+ );
+ };
+
++/**
++ * struct ethtool_phy_stats - PHY-level statistics counters
++ * @rx_packets: Total successfully received frames
++ * @rx_bytes: Total successfully received bytes
++ * @rx_errors: Total received frames with errors (e.g., CRC errors)
++ * @tx_packets: Total successfully transmitted frames
++ * @tx_bytes: Total successfully transmitted bytes
++ * @tx_errors: Total transmitted frames with errors
++ *
++ * This structure provides a standardized interface for reporting
++ * PHY-level statistics counters. It is designed to expose statistics
++ * commonly provided by PHYs but not explicitly defined in the IEEE
++ * 802.3 standard.
++ */
++struct ethtool_phy_stats {
++ u64 rx_packets;
++ u64 rx_bytes;
++ u64 rx_errors;
++ u64 tx_packets;
++ u64 tx_bytes;
++ u64 tx_errors;
++};
++
+ /* Basic IEEE 802.3 MAC Ctrl statistics (30.3.3.*), not otherwise exposed
+ * via a more targeted API.
+ */
+diff --git a/include/linux/phy.h b/include/linux/phy.h
+index a98bc91a0cde9..945264f457d8a 100644
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1090,6 +1090,35 @@ struct phy_driver {
+ int (*cable_test_get_status)(struct phy_device *dev, bool *finished);
+
+ /* Get statistics from the PHY using ethtool */
++ /**
++ * @get_phy_stats: Retrieve PHY statistics.
++ * @dev: The PHY device for which the statistics are retrieved.
++ * @eth_stats: structure where Ethernet PHY stats will be stored.
++ * @stats: structure where additional PHY-specific stats will be stored.
++ *
++ * Retrieves the supported PHY statistics and populates the provided
++ * structures. The input structures are pre-initialized with
++ * `ETHTOOL_STAT_NOT_SET`, and the driver must only modify members
++ * corresponding to supported statistics. Unmodified members will remain
++ * set to `ETHTOOL_STAT_NOT_SET` and will not be returned to userspace.
++ */
++ void (*get_phy_stats)(struct phy_device *dev,
++ struct ethtool_eth_phy_stats *eth_stats,
++ struct ethtool_phy_stats *stats);
++
++ /**
++ * @get_link_stats: Retrieve link statistics.
++ * @dev: The PHY device for which the statistics are retrieved.
++ * @link_stats: structure where link-specific stats will be stored.
++ *
++ * Retrieves link-related statistics for the given PHY device. The input
++ * structure is pre-initialized with `ETHTOOL_STAT_NOT_SET`, and the
++ * driver must only modify members corresponding to supported
++ * statistics. Unmodified members will remain set to
++ * `ETHTOOL_STAT_NOT_SET` and will not be returned to userspace.
++ */
++ void (*get_link_stats)(struct phy_device *dev,
++ struct ethtool_link_ext_stats *link_stats);
+ /** @get_sset_count: Number of statistic counters */
+ int (*get_sset_count)(struct phy_device *dev);
+ /** @get_strings: Names of the statistic counters */
+@@ -2055,6 +2084,13 @@ int phy_ethtool_get_strings(struct phy_device *phydev, u8 *data);
+ int phy_ethtool_get_sset_count(struct phy_device *phydev);
+ int phy_ethtool_get_stats(struct phy_device *phydev,
+ struct ethtool_stats *stats, u64 *data);
++
++void __phy_ethtool_get_phy_stats(struct phy_device *phydev,
++ struct ethtool_eth_phy_stats *phy_stats,
++ struct ethtool_phy_stats *phydev_stats);
++void __phy_ethtool_get_link_ext_stats(struct phy_device *phydev,
++ struct ethtool_link_ext_stats *link_stats);
++
+ int phy_ethtool_get_plca_cfg(struct phy_device *phydev,
+ struct phy_plca_cfg *plca_cfg);
+ int phy_ethtool_set_plca_cfg(struct phy_device *phydev,
+diff --git a/include/linux/phylib_stubs.h b/include/linux/phylib_stubs.h
+index 1279f48c8a707..9d2d6090c86d1 100644
+--- a/include/linux/phylib_stubs.h
++++ b/include/linux/phylib_stubs.h
+@@ -5,6 +5,9 @@
+
+ #include <linux/rtnetlink.h>
+
++struct ethtool_eth_phy_stats;
++struct ethtool_link_ext_stats;
++struct ethtool_phy_stats;
+ struct kernel_hwtstamp_config;
+ struct netlink_ext_ack;
+ struct phy_device;
+@@ -19,6 +22,11 @@ struct phylib_stubs {
+ int (*hwtstamp_set)(struct phy_device *phydev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
++ void (*get_phy_stats)(struct phy_device *phydev,
++ struct ethtool_eth_phy_stats *phy_stats,
++ struct ethtool_phy_stats *phydev_stats);
++ void (*get_link_ext_stats)(struct phy_device *phydev,
++ struct ethtool_link_ext_stats *link_stats);
+ };
+
+ static inline int phy_hwtstamp_get(struct phy_device *phydev,
+@@ -50,6 +58,29 @@ static inline int phy_hwtstamp_set(struct phy_device *phydev,
+ return phylib_stubs->hwtstamp_set(phydev, config, extack);
+ }
+
++static inline void phy_ethtool_get_phy_stats(struct phy_device *phydev,
++ struct ethtool_eth_phy_stats *phy_stats,
++ struct ethtool_phy_stats *phydev_stats)
++{
++ ASSERT_RTNL();
++
++ if (!phylib_stubs)
++ return;
++
++ phylib_stubs->get_phy_stats(phydev, phy_stats, phydev_stats);
++}
++
++static inline void phy_ethtool_get_link_ext_stats(struct phy_device *phydev,
++ struct ethtool_link_ext_stats *link_stats)
++{
++ ASSERT_RTNL();
++
++ if (!phylib_stubs)
++ return;
++
++ phylib_stubs->get_link_ext_stats(phydev, link_stats);
++}
++
+ #else
+
+ static inline int phy_hwtstamp_get(struct phy_device *phydev,
+@@ -65,4 +96,15 @@ static inline int phy_hwtstamp_set(struct phy_device *phydev,
+ return -EOPNOTSUPP;
+ }
+
++static inline void phy_ethtool_get_phy_stats(struct phy_device *phydev,
++ struct ethtool_eth_phy_stats *phy_stats,
++ struct ethtool_phy_stats *phydev_stats)
++{
++}
++
++static inline void phy_ethtool_get_link_ext_stats(struct phy_device *phydev,
++ struct ethtool_link_ext_stats *link_stats)
++{
++}
++
+ #endif
+diff --git a/net/ethtool/linkstate.c b/net/ethtool/linkstate.c
+index 459cfea7652d4..af19e1bed303f 100644
+--- a/net/ethtool/linkstate.c
++++ b/net/ethtool/linkstate.c
+@@ -3,6 +3,7 @@
+ #include "netlink.h"
+ #include "common.h"
+ #include <linux/phy.h>
++#include <linux/phylib_stubs.h>
+
+ struct linkstate_req_info {
+ struct ethnl_req_info base;
+@@ -135,8 +136,8 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
+
+ if (req_base->flags & ETHTOOL_FLAG_STATS) {
+ if (phydev)
+- data->link_stats.link_down_events =
+- READ_ONCE(phydev->link_down_events);
++ phy_ethtool_get_link_ext_stats(phydev,
++ &data->link_stats);
+
+ if (dev->ethtool_ops->get_link_ext_stats)
+ dev->ethtool_ops->get_link_ext_stats(dev,
+diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c
+index 912f0c4fff2fb..f4d822c225db6 100644
+--- a/net/ethtool/stats.c
++++ b/net/ethtool/stats.c
+@@ -1,5 +1,8 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+
++#include <linux/phy.h>
++#include <linux/phylib_stubs.h>
++
+ #include "netlink.h"
+ #include "common.h"
+ #include "bitset.h"
+@@ -20,6 +23,7 @@ struct stats_reply_data {
+ struct ethtool_eth_mac_stats mac_stats;
+ struct ethtool_eth_ctrl_stats ctrl_stats;
+ struct ethtool_rmon_stats rmon_stats;
++ struct ethtool_phy_stats phydev_stats;
+ );
+ const struct ethtool_rmon_hist_range *rmon_ranges;
+ };
+@@ -120,8 +124,15 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
+ struct stats_reply_data *data = STATS_REPDATA(reply_base);
+ enum ethtool_mac_stats_src src = req_info->src;
+ struct net_device *dev = reply_base->dev;
++ struct nlattr **tb = info->attrs;
++ struct phy_device *phydev;
+ int ret;
+
++ phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_STATS_HEADER],
++ info->extack);
++ if (IS_ERR(phydev))
++ return PTR_ERR(phydev);
++
+ ret = ethnl_ops_begin(dev);
+ if (ret < 0)
+ return ret;
+@@ -145,6 +156,13 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
+ data->ctrl_stats.src = src;
+ data->rmon_stats.src = src;
+
++ if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
++ src == ETHTOOL_MAC_STATS_SRC_AGGREGATE) {
++ if (phydev)
++ phy_ethtool_get_phy_stats(phydev, &data->phy_stats,
++ &data->phydev_stats);
++ }
++
+ if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
+ dev->ethtool_ops->get_eth_phy_stats)
+ dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats);
+--
+2.39.5
+
--- /dev/null
+From 7040c5dc5c6b5146c269c947fdfea745872ae534 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 26 Feb 2025 18:13:42 +0100
+Subject: net: gso: fix ownership in __udp_gso_segment
+
+From: Antoine Tenart <atenart@kernel.org>
+
+[ Upstream commit ee01b2f2d7d0010787c2343463965bbc283a497f ]
+
+In __udp_gso_segment the skb destructor is removed before segmenting the
+skb but the socket reference is kept as-is. This is an issue if the
+original skb is later orphaned as we can hit the following bug:
+
+ kernel BUG at ./include/linux/skbuff.h:3312! (skb_orphan)
+ RIP: 0010:ip_rcv_core+0x8b2/0xca0
+ Call Trace:
+ ip_rcv+0xab/0x6e0
+ __netif_receive_skb_one_core+0x168/0x1b0
+ process_backlog+0x384/0x1100
+ __napi_poll.constprop.0+0xa1/0x370
+ net_rx_action+0x925/0xe50
+
+The above can happen following a sequence of events when using
+OpenVSwitch, when an OVS_ACTION_ATTR_USERSPACE action precedes an
+OVS_ACTION_ATTR_OUTPUT action:
+
+1. OVS_ACTION_ATTR_USERSPACE is handled (in do_execute_actions): the skb
+ goes through queue_gso_packets and then __udp_gso_segment, where its
+ destructor is removed.
+2. The segments' data are copied and sent to userspace.
+3. OVS_ACTION_ATTR_OUTPUT is handled (in do_execute_actions) and the
+ same original skb is sent to its path.
+4. If it later hits skb_orphan, we hit the bug.
+
+Fix this by also removing the reference to the socket in
+__udp_gso_segment.
+
+Fixes: ad405857b174 ("udp: better wmem accounting on gso")
+Signed-off-by: Antoine Tenart <atenart@kernel.org>
+Link: https://patch.msgid.link/20250226171352.258045-1-atenart@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/udp_offload.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
+index a5be6e4ed326f..ecfca59f31f13 100644
+--- a/net/ipv4/udp_offload.c
++++ b/net/ipv4/udp_offload.c
+@@ -321,13 +321,17 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
+
+ /* clear destructor to avoid skb_segment assigning it to tail */
+ copy_dtor = gso_skb->destructor == sock_wfree;
+- if (copy_dtor)
++ if (copy_dtor) {
+ gso_skb->destructor = NULL;
++ gso_skb->sk = NULL;
++ }
+
+ segs = skb_segment(gso_skb, features);
+ if (IS_ERR_OR_NULL(segs)) {
+- if (copy_dtor)
++ if (copy_dtor) {
+ gso_skb->destructor = sock_wfree;
++ gso_skb->sk = sk;
++ }
+ return segs;
+ }
+
+--
+2.39.5
+
--- /dev/null
+From 366f37203c6b18ac65467809849ee6777693deda Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 28 Feb 2025 18:52:58 +0800
+Subject: net: hns3: make sure ptp clock is unregister and freed if
+ hclge_ptp_get_cycle returns an error
+
+From: Peiyang Wang <wangpeiyang1@huawei.com>
+
+[ Upstream commit b7365eab39831487a84e63a9638209b68dc54008 ]
+
+During the initialization of ptp, hclge_ptp_get_cycle might return an error
+and returned directly without unregister clock and free it. To avoid that,
+call hclge_ptp_destroy_clock to unregist and free clock if
+hclge_ptp_get_cycle failed.
+
+Fixes: 8373cd38a888 ("net: hns3: change the method of obtaining default ptp cycle")
+Signed-off-by: Peiyang Wang <wangpeiyang1@huawei.com>
+Signed-off-by: Jijie Shao <shaojijie@huawei.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20250228105258.1243461-1-shaojijie@huawei.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
+index bab16c2191b2f..181af419b878d 100644
+--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
++++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
+@@ -483,7 +483,7 @@ int hclge_ptp_init(struct hclge_dev *hdev)
+
+ ret = hclge_ptp_get_cycle(hdev);
+ if (ret)
+- return ret;
++ goto out;
+ }
+
+ ret = hclge_ptp_int_en(hdev, true);
+--
+2.39.5
+
--- /dev/null
+From 815c73abcbe3f4266a233b68858a7165a7a1a414 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 11:33:42 +0100
+Subject: net: ipa: Enable checksum for IPA_ENDPOINT_AP_MODEM_{RX,TX} for v4.7
+
+From: Luca Weiss <luca.weiss@fairphone.com>
+
+[ Upstream commit 934e69669e32eb653234898424ae007bae2f636e ]
+
+Enable the checksum option for these two endpoints in order to allow
+mobile data to actually work. Without this, no packets seem to make it
+through the IPA.
+
+Fixes: b310de784bac ("net: ipa: add IPA v4.7 support")
+Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
+Reviewed-by: Alex Elder <elder@riscstar.com>
+Link: https://patch.msgid.link/20250227-ipa-v4-7-fixes-v1-3-a88dd8249d8a@fairphone.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ipa/data/ipa_data-v4.7.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/ipa/data/ipa_data-v4.7.c b/drivers/net/ipa/data/ipa_data-v4.7.c
+index e63dcf8d45567..41f212209993f 100644
+--- a/drivers/net/ipa/data/ipa_data-v4.7.c
++++ b/drivers/net/ipa/data/ipa_data-v4.7.c
+@@ -104,6 +104,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = {
+ .filter_support = true,
+ .config = {
+ .resource_group = IPA_RSRC_GROUP_SRC_UL_DL,
++ .checksum = true,
+ .qmap = true,
+ .status_enable = true,
+ .tx = {
+@@ -127,6 +128,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = {
+ .endpoint = {
+ .config = {
+ .resource_group = IPA_RSRC_GROUP_DST_UL_DL,
++ .checksum = true,
+ .qmap = true,
+ .aggregation = true,
+ .rx = {
+--
+2.39.5
+
--- /dev/null
+From e05ab8d5f8f2d8ee80e78a24eeaa41bcedf8af55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 11:33:41 +0100
+Subject: net: ipa: Fix QSB data for v4.7
+
+From: Luca Weiss <luca.weiss@fairphone.com>
+
+[ Upstream commit 6a2843aaf551d87beb92d774f7d5b8ae007fe774 ]
+
+As per downstream reference, max_writes should be 12 and max_reads
+should be 13.
+
+Fixes: b310de784bac ("net: ipa: add IPA v4.7 support")
+Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
+Reviewed-by: Alex Elder <elder@riscstar.com>
+Link: https://patch.msgid.link/20250227-ipa-v4-7-fixes-v1-2-a88dd8249d8a@fairphone.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ipa/data/ipa_data-v4.7.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ipa/data/ipa_data-v4.7.c b/drivers/net/ipa/data/ipa_data-v4.7.c
+index 7e315779e6648..e63dcf8d45567 100644
+--- a/drivers/net/ipa/data/ipa_data-v4.7.c
++++ b/drivers/net/ipa/data/ipa_data-v4.7.c
+@@ -38,8 +38,8 @@ enum ipa_rsrc_group_id {
+ /* QSB configuration data for an SoC having IPA v4.7 */
+ static const struct ipa_qsb_data ipa_qsb_data[] = {
+ [IPA_QSB_MASTER_DDR] = {
+- .max_writes = 8,
+- .max_reads = 0, /* no limit (hardware max) */
++ .max_writes = 12,
++ .max_reads = 13,
+ .max_reads_beats = 120,
+ },
+ };
+--
+2.39.5
+
--- /dev/null
+From 28140960dd00710013a736c37dda4f2aa9d54001 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Feb 2025 11:33:40 +0100
+Subject: net: ipa: Fix v4.7 resource group names
+
+From: Luca Weiss <luca.weiss@fairphone.com>
+
+[ Upstream commit 5eb3dc1396aa7e315486b24df80df782912334b7 ]
+
+In the downstream IPA driver there's only one group defined for source
+and destination, and the destination group doesn't have a _DPL suffix.
+
+Fixes: b310de784bac ("net: ipa: add IPA v4.7 support")
+Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
+Reviewed-by: Alex Elder <elder@riscstar.com>
+Link: https://patch.msgid.link/20250227-ipa-v4-7-fixes-v1-1-a88dd8249d8a@fairphone.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ipa/data/ipa_data-v4.7.c | 12 +++++-------
+ 1 file changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/net/ipa/data/ipa_data-v4.7.c b/drivers/net/ipa/data/ipa_data-v4.7.c
+index c8c23d9be961b..7e315779e6648 100644
+--- a/drivers/net/ipa/data/ipa_data-v4.7.c
++++ b/drivers/net/ipa/data/ipa_data-v4.7.c
+@@ -28,12 +28,10 @@ enum ipa_resource_type {
+ enum ipa_rsrc_group_id {
+ /* Source resource group identifiers */
+ IPA_RSRC_GROUP_SRC_UL_DL = 0,
+- IPA_RSRC_GROUP_SRC_UC_RX_Q,
+ IPA_RSRC_GROUP_SRC_COUNT, /* Last in set; not a source group */
+
+ /* Destination resource group identifiers */
+- IPA_RSRC_GROUP_DST_UL_DL_DPL = 0,
+- IPA_RSRC_GROUP_DST_UNUSED_1,
++ IPA_RSRC_GROUP_DST_UL_DL = 0,
+ IPA_RSRC_GROUP_DST_COUNT, /* Last; not a destination group */
+ };
+
+@@ -81,7 +79,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = {
+ },
+ .endpoint = {
+ .config = {
+- .resource_group = IPA_RSRC_GROUP_DST_UL_DL_DPL,
++ .resource_group = IPA_RSRC_GROUP_DST_UL_DL,
+ .aggregation = true,
+ .status_enable = true,
+ .rx = {
+@@ -128,7 +126,7 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = {
+ },
+ .endpoint = {
+ .config = {
+- .resource_group = IPA_RSRC_GROUP_DST_UL_DL_DPL,
++ .resource_group = IPA_RSRC_GROUP_DST_UL_DL,
+ .qmap = true,
+ .aggregation = true,
+ .rx = {
+@@ -197,12 +195,12 @@ static const struct ipa_resource ipa_resource_src[] = {
+ /* Destination resource configuration data for an SoC having IPA v4.7 */
+ static const struct ipa_resource ipa_resource_dst[] = {
+ [IPA_RESOURCE_TYPE_DST_DATA_SECTORS] = {
+- .limits[IPA_RSRC_GROUP_DST_UL_DL_DPL] = {
++ .limits[IPA_RSRC_GROUP_DST_UL_DL] = {
+ .min = 7, .max = 7,
+ },
+ },
+ [IPA_RESOURCE_TYPE_DST_DPS_DMARS] = {
+- .limits[IPA_RSRC_GROUP_DST_UL_DL_DPL] = {
++ .limits[IPA_RSRC_GROUP_DST_UL_DL] = {
+ .min = 2, .max = 2,
+ },
+ },
+--
+2.39.5
+
--- /dev/null
+From 677613854ff4621e079fc55cb3739d9100d5d48a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 4 Mar 2025 19:10:39 +0100
+Subject: net: ipv6: fix dst ref loop in ila lwtunnel
+
+From: Justin Iurman <justin.iurman@uliege.be>
+
+[ Upstream commit 0e7633d7b95b67f1758aea19f8e85621c5f506a3 ]
+
+This patch follows commit 92191dd10730 ("net: ipv6: fix dst ref loops in
+rpl, seg6 and ioam6 lwtunnels") and, on a second thought, the same patch
+is also needed for ila (even though the config that triggered the issue
+was pathological, but still, we don't want that to happen).
+
+Fixes: 79ff2fc31e0f ("ila: Cache a route to translated address")
+Cc: Tom Herbert <tom@herbertland.com>
+Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
+Link: https://patch.msgid.link/20250304181039.35951-1-justin.iurman@uliege.be
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/ila/ila_lwt.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
+index ff7e734e335b0..ac4bcc623603a 100644
+--- a/net/ipv6/ila/ila_lwt.c
++++ b/net/ipv6/ila/ila_lwt.c
+@@ -88,7 +88,8 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+ goto drop;
+ }
+
+- if (ilwt->connected) {
++ /* cache only if we don't create a dst reference loop */
++ if (ilwt->connected && orig_dst->lwtstate != dst->lwtstate) {
+ local_bh_disable();
+ dst_cache_set_ip6(&ilwt->dst_cache, dst, &fl6.saddr);
+ local_bh_enable();
+--
+2.39.5
+
--- /dev/null
+From 0e2249a8385fc0c320def23b52569168fcb51726 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Mar 2025 09:16:55 +0100
+Subject: net: ipv6: fix missing dst ref drop in ila lwtunnel
+
+From: Justin Iurman <justin.iurman@uliege.be>
+
+[ Upstream commit 5da15a9c11c1c47ef573e6805b60a7d8a1687a2a ]
+
+Add missing skb_dst_drop() to drop reference to the old dst before
+adding the new dst to the skb.
+
+Fixes: 79ff2fc31e0f ("ila: Cache a route to translated address")
+Cc: Tom Herbert <tom@herbertland.com>
+Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
+Link: https://patch.msgid.link/20250305081655.19032-1-justin.iurman@uliege.be
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/ila/ila_lwt.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
+index ac4bcc623603a..7d574f5132e2f 100644
+--- a/net/ipv6/ila/ila_lwt.c
++++ b/net/ipv6/ila/ila_lwt.c
+@@ -96,6 +96,7 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+ }
+ }
+
++ skb_dst_drop(skb);
+ skb_dst_set(skb, dst);
+ return dst_output(net, sk, skb);
+
+--
+2.39.5
+
--- /dev/null
+From fcca48ef504eca8e3ebe6e9d0ee07823780c63f8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 4 Mar 2025 08:44:29 +0800
+Subject: net-timestamp: support TCP GSO case for a few missing flags
+
+From: Jason Xing <kerneljasonxing@gmail.com>
+
+[ Upstream commit 3c9231ea6497dfc50ac0ef69fff484da27d0df66 ]
+
+When I read through the TSO codes, I found out that we probably
+miss initializing the tx_flags of last seg when TSO is turned
+off, which means at the following points no more timestamp
+(for this last one) will be generated. There are three flags
+to be handled in this patch:
+1. SKBTX_HW_TSTAMP
+2. SKBTX_BPF
+3. SKBTX_SCHED_TSTAMP
+Note that SKBTX_BPF[1] was added in 6.14.0-rc2 by commit
+6b98ec7e882af ("bpf: Add BPF_SOCK_OPS_TSTAMP_SCHED_CB callback")
+and only belongs to net-next branch material for now. The common
+issue of the above three flags can be fixed by this single patch.
+
+This patch initializes the tx_flags to SKBTX_ANY_TSTAMP like what
+the UDP GSO does to make the newly segmented last skb inherit the
+tx_flags so that requested timestamp will be generated in each
+certain layer, or else that last one has zero value of tx_flags
+which leads to no timestamp at all.
+
+Fixes: 4ed2d765dfacc ("net-timestamp: TCP timestamping")
+Signed-off-by: Jason Xing <kerneljasonxing@gmail.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv4/tcp_offload.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
+index 2308665b51c53..2dfac79dc78b8 100644
+--- a/net/ipv4/tcp_offload.c
++++ b/net/ipv4/tcp_offload.c
+@@ -13,12 +13,15 @@
+ #include <net/tcp.h>
+ #include <net/protocol.h>
+
+-static void tcp_gso_tstamp(struct sk_buff *skb, unsigned int ts_seq,
++static void tcp_gso_tstamp(struct sk_buff *skb, struct sk_buff *gso_skb,
+ unsigned int seq, unsigned int mss)
+ {
++ u32 flags = skb_shinfo(gso_skb)->tx_flags & SKBTX_ANY_TSTAMP;
++ u32 ts_seq = skb_shinfo(gso_skb)->tskey;
++
+ while (skb) {
+ if (before(ts_seq, seq + mss)) {
+- skb_shinfo(skb)->tx_flags |= SKBTX_SW_TSTAMP;
++ skb_shinfo(skb)->tx_flags |= flags;
+ skb_shinfo(skb)->tskey = ts_seq;
+ return;
+ }
+@@ -193,8 +196,8 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
+ th = tcp_hdr(skb);
+ seq = ntohl(th->seq);
+
+- if (unlikely(skb_shinfo(gso_skb)->tx_flags & SKBTX_SW_TSTAMP))
+- tcp_gso_tstamp(segs, skb_shinfo(gso_skb)->tskey, seq, mss);
++ if (unlikely(skb_shinfo(gso_skb)->tx_flags & SKBTX_ANY_TSTAMP))
++ tcp_gso_tstamp(segs, gso_skb, seq, mss);
+
+ newcheck = ~csum_fold(csum_add(csum_unfold(th->check), delta));
+
+--
+2.39.5
+
--- /dev/null
+From 8db9d90cfdcaf2b58c63c8e91026158b0a0cee81 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Feb 2025 17:13:30 -0800
+Subject: nvme-ioctl: fix leaked requests on mapping error
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ Upstream commit 00817f0f1c45b007965f5676b9a2013bb39c7228 ]
+
+All the callers assume nvme_map_user_request() frees the request on a
+failure. This wasn't happening on invalid metadata or io_uring command
+flags, so we've been leaking those requests.
+
+Fixes: 23fd22e55b767b ("nvme: wire up fixed buffer support for nvme passthrough")
+Fixes: 7c2fd76048e95d ("nvme: fix metadata handling in nvme-passthrough")
+Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
+Reviewed-by: Kanchan Joshi <joshi.k@samsung.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/ioctl.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
+index d4b80938de09c..e4daac9c24401 100644
+--- a/drivers/nvme/host/ioctl.c
++++ b/drivers/nvme/host/ioctl.c
+@@ -128,8 +128,10 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
+ if (!nvme_ctrl_sgl_supported(ctrl))
+ dev_warn_once(ctrl->device, "using unchecked data buffer\n");
+ if (has_metadata) {
+- if (!supports_metadata)
+- return -EINVAL;
++ if (!supports_metadata) {
++ ret = -EINVAL;
++ goto out;
++ }
+ if (!nvme_ctrl_meta_sgl_supported(ctrl))
+ dev_warn_once(ctrl->device,
+ "using unchecked metadata buffer\n");
+@@ -139,8 +141,10 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
+ struct iov_iter iter;
+
+ /* fixedbufs is only for non-vectored io */
+- if (WARN_ON_ONCE(flags & NVME_IOCTL_VEC))
+- return -EINVAL;
++ if (WARN_ON_ONCE(flags & NVME_IOCTL_VEC)) {
++ ret = -EINVAL;
++ goto out;
++ }
+ ret = io_uring_cmd_import_fixed(ubuffer, bufflen,
+ rq_data_dir(req), &iter, ioucmd);
+ if (ret < 0)
+--
+2.39.5
+
--- /dev/null
+From e3ef99e0f3392e12fcb8e66b926be91f0150735e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 Nov 2024 13:41:21 -0800
+Subject: nvme-pci: add support for sgl metadata
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ Upstream commit 979c6342f9c0a48696a6420f14f9dd409591657f ]
+
+Supporting this mode allows creating and merging multi-segment metadata
+requests that wouldn't be possible otherwise. It also allows directly
+using user space requests that straddle physically discontiguous pages.
+
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Stable-dep-of: 00817f0f1c45 ("nvme-ioctl: fix leaked requests on mapping error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/nvme.h | 7 ++
+ drivers/nvme/host/pci.c | 144 +++++++++++++++++++++++++++++++++++----
+ include/linux/nvme.h | 1 +
+ 3 files changed, 137 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
+index 61bba5513de05..dcdce7d12e441 100644
+--- a/drivers/nvme/host/nvme.h
++++ b/drivers/nvme/host/nvme.h
+@@ -1130,6 +1130,13 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)
+ return ctrl->sgls & ((1 << 0) | (1 << 1));
+ }
+
++static inline bool nvme_ctrl_meta_sgl_supported(struct nvme_ctrl *ctrl)
++{
++ if (ctrl->ops->flags & NVME_F_FABRICS)
++ return true;
++ return ctrl->sgls & NVME_CTRL_SGLS_MSDS;
++}
++
+ #ifdef CONFIG_NVME_HOST_AUTH
+ int __init nvme_init_auth(void);
+ void __exit nvme_exit_auth(void);
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index cc74682dc0d4e..58bdd0da6b658 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -43,6 +43,7 @@
+ */
+ #define NVME_MAX_KB_SZ 8192
+ #define NVME_MAX_SEGS 128
++#define NVME_MAX_META_SEGS 15
+ #define NVME_MAX_NR_ALLOCATIONS 5
+
+ static int use_threaded_interrupts;
+@@ -143,6 +144,7 @@ struct nvme_dev {
+ bool hmb;
+
+ mempool_t *iod_mempool;
++ mempool_t *iod_meta_mempool;
+
+ /* shadow doorbell buffer support: */
+ __le32 *dbbuf_dbs;
+@@ -238,6 +240,8 @@ struct nvme_iod {
+ dma_addr_t first_dma;
+ dma_addr_t meta_dma;
+ struct sg_table sgt;
++ struct sg_table meta_sgt;
++ union nvme_descriptor meta_list;
+ union nvme_descriptor list[NVME_MAX_NR_ALLOCATIONS];
+ };
+
+@@ -505,6 +509,14 @@ static void nvme_commit_rqs(struct blk_mq_hw_ctx *hctx)
+ spin_unlock(&nvmeq->sq_lock);
+ }
+
++static inline bool nvme_pci_metadata_use_sgls(struct nvme_dev *dev,
++ struct request *req)
++{
++ if (!nvme_ctrl_meta_sgl_supported(&dev->ctrl))
++ return false;
++ return req->nr_integrity_segments > 1;
++}
++
+ static inline bool nvme_pci_use_sgls(struct nvme_dev *dev, struct request *req,
+ int nseg)
+ {
+@@ -517,6 +529,8 @@ static inline bool nvme_pci_use_sgls(struct nvme_dev *dev, struct request *req,
+ return false;
+ if (!nvmeq->qid)
+ return false;
++ if (nvme_pci_metadata_use_sgls(dev, req))
++ return true;
+ if (!sgl_threshold || avg_seg_size < sgl_threshold)
+ return false;
+ return true;
+@@ -779,7 +793,8 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
+ struct bio_vec bv = req_bvec(req);
+
+ if (!is_pci_p2pdma_page(bv.bv_page)) {
+- if ((bv.bv_offset & (NVME_CTRL_PAGE_SIZE - 1)) +
++ if (!nvme_pci_metadata_use_sgls(dev, req) &&
++ (bv.bv_offset & (NVME_CTRL_PAGE_SIZE - 1)) +
+ bv.bv_len <= NVME_CTRL_PAGE_SIZE * 2)
+ return nvme_setup_prp_simple(dev, req,
+ &cmnd->rw, &bv);
+@@ -823,11 +838,69 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
+ return ret;
+ }
+
+-static blk_status_t nvme_map_metadata(struct nvme_dev *dev, struct request *req,
+- struct nvme_command *cmnd)
++static blk_status_t nvme_pci_setup_meta_sgls(struct nvme_dev *dev,
++ struct request *req)
++{
++ struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
++ struct nvme_rw_command *cmnd = &iod->cmd.rw;
++ struct nvme_sgl_desc *sg_list;
++ struct scatterlist *sgl, *sg;
++ unsigned int entries;
++ dma_addr_t sgl_dma;
++ int rc, i;
++
++ iod->meta_sgt.sgl = mempool_alloc(dev->iod_meta_mempool, GFP_ATOMIC);
++ if (!iod->meta_sgt.sgl)
++ return BLK_STS_RESOURCE;
++
++ sg_init_table(iod->meta_sgt.sgl, req->nr_integrity_segments);
++ iod->meta_sgt.orig_nents = blk_rq_map_integrity_sg(req,
++ iod->meta_sgt.sgl);
++ if (!iod->meta_sgt.orig_nents)
++ goto out_free_sg;
++
++ rc = dma_map_sgtable(dev->dev, &iod->meta_sgt, rq_dma_dir(req),
++ DMA_ATTR_NO_WARN);
++ if (rc)
++ goto out_free_sg;
++
++ sg_list = dma_pool_alloc(dev->prp_small_pool, GFP_ATOMIC, &sgl_dma);
++ if (!sg_list)
++ goto out_unmap_sg;
++
++ entries = iod->meta_sgt.nents;
++ iod->meta_list.sg_list = sg_list;
++ iod->meta_dma = sgl_dma;
++
++ cmnd->flags = NVME_CMD_SGL_METASEG;
++ cmnd->metadata = cpu_to_le64(sgl_dma);
++
++ sgl = iod->meta_sgt.sgl;
++ if (entries == 1) {
++ nvme_pci_sgl_set_data(sg_list, sgl);
++ return BLK_STS_OK;
++ }
++
++ sgl_dma += sizeof(*sg_list);
++ nvme_pci_sgl_set_seg(sg_list, sgl_dma, entries);
++ for_each_sg(sgl, sg, entries, i)
++ nvme_pci_sgl_set_data(&sg_list[i + 1], sg);
++
++ return BLK_STS_OK;
++
++out_unmap_sg:
++ dma_unmap_sgtable(dev->dev, &iod->meta_sgt, rq_dma_dir(req), 0);
++out_free_sg:
++ mempool_free(iod->meta_sgt.sgl, dev->iod_meta_mempool);
++ return BLK_STS_RESOURCE;
++}
++
++static blk_status_t nvme_pci_setup_meta_mptr(struct nvme_dev *dev,
++ struct request *req)
+ {
+ struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
+ struct bio_vec bv = rq_integrity_vec(req);
++ struct nvme_command *cmnd = &iod->cmd;
+
+ iod->meta_dma = dma_map_bvec(dev->dev, &bv, rq_dma_dir(req), 0);
+ if (dma_mapping_error(dev->dev, iod->meta_dma))
+@@ -836,6 +909,13 @@ static blk_status_t nvme_map_metadata(struct nvme_dev *dev, struct request *req,
+ return BLK_STS_OK;
+ }
+
++static blk_status_t nvme_map_metadata(struct nvme_dev *dev, struct request *req)
++{
++ if (nvme_pci_metadata_use_sgls(dev, req))
++ return nvme_pci_setup_meta_sgls(dev, req);
++ return nvme_pci_setup_meta_mptr(dev, req);
++}
++
+ static blk_status_t nvme_prep_rq(struct nvme_dev *dev, struct request *req)
+ {
+ struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
+@@ -844,6 +924,7 @@ static blk_status_t nvme_prep_rq(struct nvme_dev *dev, struct request *req)
+ iod->aborted = false;
+ iod->nr_allocations = -1;
+ iod->sgt.nents = 0;
++ iod->meta_sgt.nents = 0;
+
+ ret = nvme_setup_cmd(req->q->queuedata, req);
+ if (ret)
+@@ -856,7 +937,7 @@ static blk_status_t nvme_prep_rq(struct nvme_dev *dev, struct request *req)
+ }
+
+ if (blk_integrity_rq(req)) {
+- ret = nvme_map_metadata(dev, req, &iod->cmd);
++ ret = nvme_map_metadata(dev, req);
+ if (ret)
+ goto out_unmap_data;
+ }
+@@ -955,17 +1036,31 @@ static void nvme_queue_rqs(struct request **rqlist)
+ *rqlist = requeue_list;
+ }
+
++static __always_inline void nvme_unmap_metadata(struct nvme_dev *dev,
++ struct request *req)
++{
++ struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
++
++ if (!iod->meta_sgt.nents) {
++ dma_unmap_page(dev->dev, iod->meta_dma,
++ rq_integrity_vec(req).bv_len,
++ rq_dma_dir(req));
++ return;
++ }
++
++ dma_pool_free(dev->prp_small_pool, iod->meta_list.sg_list,
++ iod->meta_dma);
++ dma_unmap_sgtable(dev->dev, &iod->meta_sgt, rq_dma_dir(req), 0);
++ mempool_free(iod->meta_sgt.sgl, dev->iod_meta_mempool);
++}
++
+ static __always_inline void nvme_pci_unmap_rq(struct request *req)
+ {
+ struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
+ struct nvme_dev *dev = nvmeq->dev;
+
+- if (blk_integrity_rq(req)) {
+- struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
+-
+- dma_unmap_page(dev->dev, iod->meta_dma,
+- rq_integrity_vec(req).bv_len, rq_dma_dir(req));
+- }
++ if (blk_integrity_rq(req))
++ nvme_unmap_metadata(dev, req);
+
+ if (blk_rq_nr_phys_segments(req))
+ nvme_unmap_data(dev, req);
+@@ -2719,6 +2814,7 @@ static void nvme_release_prp_pools(struct nvme_dev *dev)
+
+ static int nvme_pci_alloc_iod_mempool(struct nvme_dev *dev)
+ {
++ size_t meta_size = sizeof(struct scatterlist) * (NVME_MAX_META_SEGS + 1);
+ size_t alloc_size = sizeof(struct scatterlist) * NVME_MAX_SEGS;
+
+ dev->iod_mempool = mempool_create_node(1,
+@@ -2727,7 +2823,18 @@ static int nvme_pci_alloc_iod_mempool(struct nvme_dev *dev)
+ dev_to_node(dev->dev));
+ if (!dev->iod_mempool)
+ return -ENOMEM;
++
++ dev->iod_meta_mempool = mempool_create_node(1,
++ mempool_kmalloc, mempool_kfree,
++ (void *)meta_size, GFP_KERNEL,
++ dev_to_node(dev->dev));
++ if (!dev->iod_meta_mempool)
++ goto free;
++
+ return 0;
++free:
++ mempool_destroy(dev->iod_mempool);
++ return -ENOMEM;
+ }
+
+ static void nvme_free_tagset(struct nvme_dev *dev)
+@@ -2792,6 +2899,11 @@ static void nvme_reset_work(struct work_struct *work)
+ if (result)
+ goto out;
+
++ if (nvme_ctrl_meta_sgl_supported(&dev->ctrl))
++ dev->ctrl.max_integrity_segments = NVME_MAX_META_SEGS;
++ else
++ dev->ctrl.max_integrity_segments = 1;
++
+ nvme_dbbuf_dma_alloc(dev);
+
+ result = nvme_setup_host_mem(dev);
+@@ -3061,11 +3173,6 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev,
+ dev->ctrl.max_hw_sectors = min_t(u32,
+ NVME_MAX_KB_SZ << 1, dma_opt_mapping_size(&pdev->dev) >> 9);
+ dev->ctrl.max_segments = NVME_MAX_SEGS;
+-
+- /*
+- * There is no support for SGLs for metadata (yet), so we are limited to
+- * a single integrity segment for the separate metadata pointer.
+- */
+ dev->ctrl.max_integrity_segments = 1;
+ return dev;
+
+@@ -3128,6 +3235,11 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+ if (result)
+ goto out_disable;
+
++ if (nvme_ctrl_meta_sgl_supported(&dev->ctrl))
++ dev->ctrl.max_integrity_segments = NVME_MAX_META_SEGS;
++ else
++ dev->ctrl.max_integrity_segments = 1;
++
+ nvme_dbbuf_dma_alloc(dev);
+
+ result = nvme_setup_host_mem(dev);
+@@ -3170,6 +3282,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+ nvme_free_queues(dev, 0);
+ out_release_iod_mempool:
+ mempool_destroy(dev->iod_mempool);
++ mempool_destroy(dev->iod_meta_mempool);
+ out_release_prp_pools:
+ nvme_release_prp_pools(dev);
+ out_dev_unmap:
+@@ -3235,6 +3348,7 @@ static void nvme_remove(struct pci_dev *pdev)
+ nvme_dbbuf_dma_free(dev);
+ nvme_free_queues(dev, 0);
+ mempool_destroy(dev->iod_mempool);
++ mempool_destroy(dev->iod_meta_mempool);
+ nvme_release_prp_pools(dev);
+ nvme_dev_unmap(dev);
+ nvme_uninit_ctrl(&dev->ctrl);
+diff --git a/include/linux/nvme.h b/include/linux/nvme.h
+index b58d9405d65e0..1c101f6fad2f3 100644
+--- a/include/linux/nvme.h
++++ b/include/linux/nvme.h
+@@ -388,6 +388,7 @@ enum {
+ NVME_CTRL_CTRATT_PREDICTABLE_LAT = 1 << 5,
+ NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY = 1 << 7,
+ NVME_CTRL_CTRATT_UUID_LIST = 1 << 9,
++ NVME_CTRL_SGLS_MSDS = 1 << 19,
+ };
+
+ struct nvme_lbaf {
+--
+2.39.5
+
--- /dev/null
+From e958b0165c3bdf8fea8906e68c8efd8eb11f4b7c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 Nov 2024 15:41:08 -0800
+Subject: nvme-pci: use sgls for all user requests if possible
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ Upstream commit 6fad84a4d624c300d03ebba457cc641765050c43 ]
+
+If the device supports SGLs, use these for all user requests. This
+format encodes the expected transfer length so it can catch short buffer
+errors in a user command, whether it occurred accidently or maliciously.
+
+For controllers that support SGL data mode, this is a viable mitigation
+to CVE-2023-6238. For controllers that don't support SGLs, log a warning
+in the passthrough path since not having the capability can corrupt
+data if the interface is not used correctly.
+
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Stable-dep-of: 00817f0f1c45 ("nvme-ioctl: fix leaked requests on mapping error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/ioctl.c | 12 ++++++++++--
+ drivers/nvme/host/pci.c | 5 +++--
+ 2 files changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
+index 61af1583356c2..d4b80938de09c 100644
+--- a/drivers/nvme/host/ioctl.c
++++ b/drivers/nvme/host/ioctl.c
+@@ -120,12 +120,20 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
+ struct nvme_ns *ns = q->queuedata;
+ struct block_device *bdev = ns ? ns->disk->part0 : NULL;
+ bool supports_metadata = bdev && blk_get_integrity(bdev->bd_disk);
++ struct nvme_ctrl *ctrl = nvme_req(req)->ctrl;
+ bool has_metadata = meta_buffer && meta_len;
+ struct bio *bio = NULL;
+ int ret;
+
+- if (has_metadata && !supports_metadata)
+- return -EINVAL;
++ if (!nvme_ctrl_sgl_supported(ctrl))
++ dev_warn_once(ctrl->device, "using unchecked data buffer\n");
++ if (has_metadata) {
++ if (!supports_metadata)
++ return -EINVAL;
++ if (!nvme_ctrl_meta_sgl_supported(ctrl))
++ dev_warn_once(ctrl->device,
++ "using unchecked metadata buffer\n");
++ }
+
+ if (ioucmd && (ioucmd->flags & IORING_URING_CMD_FIXED)) {
+ struct iov_iter iter;
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index 58bdd0da6b658..e1329d4974fd6 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -514,7 +514,8 @@ static inline bool nvme_pci_metadata_use_sgls(struct nvme_dev *dev,
+ {
+ if (!nvme_ctrl_meta_sgl_supported(&dev->ctrl))
+ return false;
+- return req->nr_integrity_segments > 1;
++ return req->nr_integrity_segments > 1 ||
++ nvme_req(req)->flags & NVME_REQ_USERCMD;
+ }
+
+ static inline bool nvme_pci_use_sgls(struct nvme_dev *dev, struct request *req,
+@@ -532,7 +533,7 @@ static inline bool nvme_pci_use_sgls(struct nvme_dev *dev, struct request *req,
+ if (nvme_pci_metadata_use_sgls(dev, req))
+ return true;
+ if (!sgl_threshold || avg_seg_size < sgl_threshold)
+- return false;
++ return nvme_req(req)->flags & NVME_REQ_USERCMD;
+ return true;
+ }
+
+--
+2.39.5
+
--- /dev/null
+From 437dd34325af7cd358d715640c0b0423ba80d211 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 17 Feb 2025 17:08:27 +0100
+Subject: nvme-tcp: add basic support for the C2HTermReq PDU
+
+From: Maurizio Lombardi <mlombard@redhat.com>
+
+[ Upstream commit 84e009042d0f3dfe91bec60bcd208ee3f866cbcd ]
+
+Previously, the NVMe/TCP host driver did not handle the C2HTermReq PDU,
+instead printing "unsupported pdu type (3)" when received. This patch adds
+support for processing the C2HTermReq PDU, allowing the driver
+to print the Fatal Error Status field.
+
+Example of output:
+nvme nvme4: Received C2HTermReq (FES = Invalid PDU Header Field)
+
+Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
+Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Stable-dep-of: ad95bab0cd28 ("nvme-tcp: fix potential memory corruption in nvme_tcp_recv_pdu()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/tcp.c | 43 ++++++++++++++++++++++++++++++++++++++++
+ include/linux/nvme-tcp.h | 2 ++
+ 2 files changed, 45 insertions(+)
+
+diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
+index 840ae475074d0..749d464bef9f7 100644
+--- a/drivers/nvme/host/tcp.c
++++ b/drivers/nvme/host/tcp.c
+@@ -763,6 +763,40 @@ static int nvme_tcp_handle_r2t(struct nvme_tcp_queue *queue,
+ return 0;
+ }
+
++static void nvme_tcp_handle_c2h_term(struct nvme_tcp_queue *queue,
++ struct nvme_tcp_term_pdu *pdu)
++{
++ u16 fes;
++ const char *msg;
++ u32 plen = le32_to_cpu(pdu->hdr.plen);
++
++ static const char * const msg_table[] = {
++ [NVME_TCP_FES_INVALID_PDU_HDR] = "Invalid PDU Header Field",
++ [NVME_TCP_FES_PDU_SEQ_ERR] = "PDU Sequence Error",
++ [NVME_TCP_FES_HDR_DIGEST_ERR] = "Header Digest Error",
++ [NVME_TCP_FES_DATA_OUT_OF_RANGE] = "Data Transfer Out Of Range",
++ [NVME_TCP_FES_R2T_LIMIT_EXCEEDED] = "R2T Limit Exceeded",
++ [NVME_TCP_FES_UNSUPPORTED_PARAM] = "Unsupported Parameter",
++ };
++
++ if (plen < NVME_TCP_MIN_C2HTERM_PLEN ||
++ plen > NVME_TCP_MAX_C2HTERM_PLEN) {
++ dev_err(queue->ctrl->ctrl.device,
++ "Received a malformed C2HTermReq PDU (plen = %u)\n",
++ plen);
++ return;
++ }
++
++ fes = le16_to_cpu(pdu->fes);
++ if (fes && fes < ARRAY_SIZE(msg_table))
++ msg = msg_table[fes];
++ else
++ msg = "Unknown";
++
++ dev_err(queue->ctrl->ctrl.device,
++ "Received C2HTermReq (FES = %s)\n", msg);
++}
++
+ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb,
+ unsigned int *offset, size_t *len)
+ {
+@@ -784,6 +818,15 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb,
+ return 0;
+
+ hdr = queue->pdu;
++ if (unlikely(hdr->type == nvme_tcp_c2h_term)) {
++ /*
++ * C2HTermReq never includes Header or Data digests.
++ * Skip the checks.
++ */
++ nvme_tcp_handle_c2h_term(queue, (void *)queue->pdu);
++ return -EINVAL;
++ }
++
+ if (queue->hdr_digest) {
+ ret = nvme_tcp_verify_hdgst(queue, queue->pdu, hdr->hlen);
+ if (unlikely(ret))
+diff --git a/include/linux/nvme-tcp.h b/include/linux/nvme-tcp.h
+index e07e8978d691b..e435250fcb4d0 100644
+--- a/include/linux/nvme-tcp.h
++++ b/include/linux/nvme-tcp.h
+@@ -13,6 +13,8 @@
+ #define NVME_TCP_ADMIN_CCSZ SZ_8K
+ #define NVME_TCP_DIGEST_LENGTH 4
+ #define NVME_TCP_MIN_MAXH2CDATA 4096
++#define NVME_TCP_MIN_C2HTERM_PLEN 24
++#define NVME_TCP_MAX_C2HTERM_PLEN 152
+
+ enum nvme_tcp_pfv {
+ NVME_TCP_PFV_1_0 = 0x0,
+--
+2.39.5
+
--- /dev/null
+From 68a0ab0407010748a7d7eacfe289b4018b729974 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 26 Feb 2025 14:42:18 +0100
+Subject: nvme-tcp: fix potential memory corruption in nvme_tcp_recv_pdu()
+
+From: Maurizio Lombardi <mlombard@redhat.com>
+
+[ Upstream commit ad95bab0cd28ed77c2c0d0b6e76e03e031391064 ]
+
+nvme_tcp_recv_pdu() doesn't check the validity of the header length.
+When header digests are enabled, a target might send a packet with an
+invalid header length (e.g. 255), causing nvme_tcp_verify_hdgst()
+to access memory outside the allocated area and cause memory corruptions
+by overwriting it with the calculated digest.
+
+Fix this by rejecting packets with an unexpected header length.
+
+Fixes: 3f2304f8c6d6 ("nvme-tcp: add NVMe over TCP host driver")
+Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
+Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/tcp.c | 32 +++++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
+index 749d464bef9f7..0bcc9bf57d1d0 100644
+--- a/drivers/nvme/host/tcp.c
++++ b/drivers/nvme/host/tcp.c
+@@ -217,6 +217,19 @@ static inline int nvme_tcp_queue_id(struct nvme_tcp_queue *queue)
+ return queue - queue->ctrl->queues;
+ }
+
++static inline bool nvme_tcp_recv_pdu_supported(enum nvme_tcp_pdu_type type)
++{
++ switch (type) {
++ case nvme_tcp_c2h_term:
++ case nvme_tcp_c2h_data:
++ case nvme_tcp_r2t:
++ case nvme_tcp_rsp:
++ return true;
++ default:
++ return false;
++ }
++}
++
+ /*
+ * Check if the queue is TLS encrypted
+ */
+@@ -818,6 +831,16 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb,
+ return 0;
+
+ hdr = queue->pdu;
++ if (unlikely(hdr->hlen != sizeof(struct nvme_tcp_rsp_pdu))) {
++ if (!nvme_tcp_recv_pdu_supported(hdr->type))
++ goto unsupported_pdu;
++
++ dev_err(queue->ctrl->ctrl.device,
++ "pdu type %d has unexpected header length (%d)\n",
++ hdr->type, hdr->hlen);
++ return -EPROTO;
++ }
++
+ if (unlikely(hdr->type == nvme_tcp_c2h_term)) {
+ /*
+ * C2HTermReq never includes Header or Data digests.
+@@ -850,10 +873,13 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb,
+ nvme_tcp_init_recv_ctx(queue);
+ return nvme_tcp_handle_r2t(queue, (void *)queue->pdu);
+ default:
+- dev_err(queue->ctrl->ctrl.device,
+- "unsupported pdu type (%d)\n", hdr->type);
+- return -EINVAL;
++ goto unsupported_pdu;
+ }
++
++unsupported_pdu:
++ dev_err(queue->ctrl->ctrl.device,
++ "unsupported pdu type (%d)\n", hdr->type);
++ return -EINVAL;
+ }
+
+ static inline void nvme_tcp_end_request(struct request *rq, u16 status)
+--
+2.39.5
+
--- /dev/null
+From 6f1216d80eb5db6cb1d127f5c0690fd3ea5990eb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Mar 2025 18:52:59 +0300
+Subject: nvme-tcp: fix signedness bug in nvme_tcp_init_connection()
+
+From: Dan Carpenter <dan.carpenter@linaro.org>
+
+[ Upstream commit 528361c49962708a60f51a1afafeb00987cebedf ]
+
+The kernel_recvmsg() function returns an int which could be either
+negative error codes or the number of bytes received. The problem is
+that the condition:
+
+ if (ret < sizeof(*icresp)) {
+
+is type promoted to type unsigned long and negative values are treated
+as high positive values which is success, when they should be treated as
+failure. Handle invalid positive returns separately from negative
+error codes to avoid this problem.
+
+Fixes: 578539e09690 ("nvme-tcp: fix connect failure on receiving partial ICResp PDU")
+Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
+Reviewed-by: Caleb Sander Mateos <csander@purestorage.com>
+Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
+Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/tcp.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
+index 0bcc9bf57d1d0..3ee35f94660f9 100644
+--- a/drivers/nvme/host/tcp.c
++++ b/drivers/nvme/host/tcp.c
+@@ -1521,11 +1521,11 @@ static int nvme_tcp_init_connection(struct nvme_tcp_queue *queue)
+ msg.msg_flags = MSG_WAITALL;
+ ret = kernel_recvmsg(queue->sock, &msg, &iov, 1,
+ iov.iov_len, msg.msg_flags);
+- if (ret < sizeof(*icresp)) {
++ if (ret >= 0 && ret < sizeof(*icresp))
++ ret = -ECONNRESET;
++ if (ret < 0) {
+ pr_warn("queue %d: failed to receive icresp, error %d\n",
+ nvme_tcp_queue_id(queue), ret);
+- if (ret >= 0)
+- ret = -ECONNRESET;
+ goto free_icresp;
+ }
+ ret = -ENOTCONN;
+--
+2.39.5
+
--- /dev/null
+From db56c37619643d6ed38c4bead463d41f52c77e10 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 26 Feb 2025 09:28:12 +0200
+Subject: nvmet-tcp: Fix a possible sporadic response drops in weakly ordered
+ arch
+
+From: Meir Elisha <meir.elisha@volumez.com>
+
+[ Upstream commit a16f88964c647103dad7743a484b216d488a6352 ]
+
+The order in which queue->cmd and rcv_state are updated is crucial.
+If these assignments are reordered by the compiler, the worker might not
+get queued in nvmet_tcp_queue_response(), hanging the IO. to enforce the
+the correct reordering, set rcv_state using smp_store_release().
+
+Fixes: bdaf13279192 ("nvmet-tcp: fix a segmentation fault during io parsing error")
+
+Signed-off-by: Meir Elisha <meir.elisha@volumez.com>
+Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/target/tcp.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
+index 7c51c2a8c109a..4f9cac8a5abe0 100644
+--- a/drivers/nvme/target/tcp.c
++++ b/drivers/nvme/target/tcp.c
+@@ -571,10 +571,16 @@ static void nvmet_tcp_queue_response(struct nvmet_req *req)
+ struct nvmet_tcp_cmd *cmd =
+ container_of(req, struct nvmet_tcp_cmd, req);
+ struct nvmet_tcp_queue *queue = cmd->queue;
++ enum nvmet_tcp_recv_state queue_state;
++ struct nvmet_tcp_cmd *queue_cmd;
+ struct nvme_sgl_desc *sgl;
+ u32 len;
+
+- if (unlikely(cmd == queue->cmd)) {
++ /* Pairs with store_release in nvmet_prepare_receive_pdu() */
++ queue_state = smp_load_acquire(&queue->rcv_state);
++ queue_cmd = READ_ONCE(queue->cmd);
++
++ if (unlikely(cmd == queue_cmd)) {
+ sgl = &cmd->req.cmd->common.dptr.sgl;
+ len = le32_to_cpu(sgl->length);
+
+@@ -583,7 +589,7 @@ static void nvmet_tcp_queue_response(struct nvmet_req *req)
+ * Avoid using helpers, this might happen before
+ * nvmet_req_init is completed.
+ */
+- if (queue->rcv_state == NVMET_TCP_RECV_PDU &&
++ if (queue_state == NVMET_TCP_RECV_PDU &&
+ len && len <= cmd->req.port->inline_data_size &&
+ nvme_is_write(cmd->req.cmd))
+ return;
+@@ -847,8 +853,9 @@ static void nvmet_prepare_receive_pdu(struct nvmet_tcp_queue *queue)
+ {
+ queue->offset = 0;
+ queue->left = sizeof(struct nvme_tcp_hdr);
+- queue->cmd = NULL;
+- queue->rcv_state = NVMET_TCP_RECV_PDU;
++ WRITE_ONCE(queue->cmd, NULL);
++ /* Ensure rcv_state is visible only after queue->cmd is set */
++ smp_store_release(&queue->rcv_state, NVMET_TCP_RECV_PDU);
+ }
+
+ static void nvmet_tcp_free_crypto(struct nvmet_tcp_queue *queue)
+--
+2.39.5
+
--- /dev/null
+From 2e9d0f681285b172226a8e222b8dad68111555f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 4 Nov 2024 14:39:11 +0100
+Subject: perf/core: Fix pmus_lock vs. pmus_srcu ordering
+
+From: Peter Zijlstra <peterz@infradead.org>
+
+[ Upstream commit 2565e42539b120b81a68a58da961ce5d1e34eac8 ]
+
+Commit a63fbed776c7 ("perf/tracing/cpuhotplug: Fix locking order")
+placed pmus_lock inside pmus_srcu, this makes perf_pmu_unregister()
+trip lockdep.
+
+Move the locking about such that only pmu_idr and pmus (list) are
+modified while holding pmus_lock. This avoids doing synchronize_srcu()
+while holding pmus_lock and all is well again.
+
+Fixes: a63fbed776c7 ("perf/tracing/cpuhotplug: Fix locking order")
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Signed-off-by: Ingo Molnar <mingo@kernel.org>
+Link: https://lore.kernel.org/r/20241104135517.679556858@infradead.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/events/core.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/kernel/events/core.c b/kernel/events/core.c
+index a0e1d2124727e..5fff74c736063 100644
+--- a/kernel/events/core.c
++++ b/kernel/events/core.c
+@@ -11846,6 +11846,8 @@ void perf_pmu_unregister(struct pmu *pmu)
+ {
+ mutex_lock(&pmus_lock);
+ list_del_rcu(&pmu->entry);
++ idr_remove(&pmu_idr, pmu->type);
++ mutex_unlock(&pmus_lock);
+
+ /*
+ * We dereference the pmu list under both SRCU and regular RCU, so
+@@ -11855,7 +11857,6 @@ void perf_pmu_unregister(struct pmu *pmu)
+ synchronize_rcu();
+
+ free_percpu(pmu->pmu_disable_count);
+- idr_remove(&pmu_idr, pmu->type);
+ if (pmu_bus_running && pmu->dev && pmu->dev != PMU_NULL_DEV) {
+ if (pmu->nr_addr_filters)
+ device_remove_file(pmu->dev, &dev_attr_nr_addr_filters);
+@@ -11863,7 +11864,6 @@ void perf_pmu_unregister(struct pmu *pmu)
+ put_device(pmu->dev);
+ }
+ free_pmu_context(pmu);
+- mutex_unlock(&pmus_lock);
+ }
+ EXPORT_SYMBOL_GPL(perf_pmu_unregister);
+
+--
+2.39.5
+
--- /dev/null
+From fd6775d55895e9f9f5eae97f7a0f77a6ec74360d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 28 Feb 2025 22:14:08 +0800
+Subject: ppp: Fix KMSAN uninit-value warning with bpf
+
+From: Jiayuan Chen <jiayuan.chen@linux.dev>
+
+[ Upstream commit 4c2d14c40a68678d885eab4008a0129646805bae ]
+
+Syzbot caught an "KMSAN: uninit-value" warning [1], which is caused by the
+ppp driver not initializing a 2-byte header when using socket filter.
+
+The following code can generate a PPP filter BPF program:
+'''
+struct bpf_program fp;
+pcap_t *handle;
+handle = pcap_open_dead(DLT_PPP_PPPD, 65535);
+pcap_compile(handle, &fp, "ip and outbound", 0, 0);
+bpf_dump(&fp, 1);
+'''
+Its output is:
+'''
+(000) ldh [2]
+(001) jeq #0x21 jt 2 jf 5
+(002) ldb [0]
+(003) jeq #0x1 jt 4 jf 5
+(004) ret #65535
+(005) ret #0
+'''
+Wen can find similar code at the following link:
+https://github.com/ppp-project/ppp/blob/master/pppd/options.c#L1680
+The maintainer of this code repository is also the original maintainer
+of the ppp driver.
+
+As you can see the BPF program skips 2 bytes of data and then reads the
+'Protocol' field to determine if it's an IP packet. Then it read the first
+byte of the first 2 bytes to determine the direction.
+
+The issue is that only the first byte indicating direction is initialized
+in current ppp driver code while the second byte is not initialized.
+
+For normal BPF programs generated by libpcap, uninitialized data won't be
+used, so it's not a problem. However, for carefully crafted BPF programs,
+such as those generated by syzkaller [2], which start reading from offset
+0, the uninitialized data will be used and caught by KMSAN.
+
+[1] https://syzkaller.appspot.com/bug?extid=853242d9c9917165d791
+[2] https://syzkaller.appspot.com/text?tag=ReproC&x=11994913980000
+
+Cc: Paul Mackerras <paulus@samba.org>
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Reported-by: syzbot+853242d9c9917165d791@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/bpf/000000000000dea025060d6bc3bc@google.com/
+Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20250228141408.393864-1-jiayuan.chen@linux.dev
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ppp/ppp_generic.c | 28 +++++++++++++++++++---------
+ 1 file changed, 19 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
+index 4583e15ad03a0..1420c4efa48e6 100644
+--- a/drivers/net/ppp/ppp_generic.c
++++ b/drivers/net/ppp/ppp_generic.c
+@@ -72,6 +72,17 @@
+ #define PPP_PROTO_LEN 2
+ #define PPP_LCP_HDRLEN 4
+
++/* The filter instructions generated by libpcap are constructed
++ * assuming a four-byte PPP header on each packet, where the last
++ * 2 bytes are the protocol field defined in the RFC and the first
++ * byte of the first 2 bytes indicates the direction.
++ * The second byte is currently unused, but we still need to initialize
++ * it to prevent crafted BPF programs from reading them which would
++ * cause reading of uninitialized data.
++ */
++#define PPP_FILTER_OUTBOUND_TAG 0x0100
++#define PPP_FILTER_INBOUND_TAG 0x0000
++
+ /*
+ * An instance of /dev/ppp can be associated with either a ppp
+ * interface unit or a ppp channel. In both cases, file->private_data
+@@ -1762,10 +1773,10 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
+
+ if (proto < 0x8000) {
+ #ifdef CONFIG_PPP_FILTER
+- /* check if we should pass this packet */
+- /* the filter instructions are constructed assuming
+- a four-byte PPP header on each packet */
+- *(u8 *)skb_push(skb, 2) = 1;
++ /* check if the packet passes the pass and active filters.
++ * See comment for PPP_FILTER_OUTBOUND_TAG above.
++ */
++ *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_OUTBOUND_TAG);
+ if (ppp->pass_filter &&
+ bpf_prog_run(ppp->pass_filter, skb) == 0) {
+ if (ppp->debug & 1)
+@@ -2482,14 +2493,13 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
+ /* network protocol frame - give it to the kernel */
+
+ #ifdef CONFIG_PPP_FILTER
+- /* check if the packet passes the pass and active filters */
+- /* the filter instructions are constructed assuming
+- a four-byte PPP header on each packet */
+ if (ppp->pass_filter || ppp->active_filter) {
+ if (skb_unclone(skb, GFP_ATOMIC))
+ goto err;
+-
+- *(u8 *)skb_push(skb, 2) = 0;
++ /* Check if the packet passes the pass and active filters.
++ * See comment for PPP_FILTER_INBOUND_TAG above.
++ */
++ *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_INBOUND_TAG);
+ if (ppp->pass_filter &&
+ bpf_prog_run(ppp->pass_filter, skb) == 0) {
+ if (ppp->debug & 1)
+--
+2.39.5
+
--- /dev/null
+From cd7f205bfee27aa2a51f213265a6b2999d82a82b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 4 Mar 2025 21:40:31 +0000
+Subject: sched/fair: Fix potential memory corruption in child_cfs_rq_on_list
+
+From: Zecheng Li <zecheng@google.com>
+
+[ Upstream commit 3b4035ddbfc8e4521f85569998a7569668cccf51 ]
+
+child_cfs_rq_on_list attempts to convert a 'prev' pointer to a cfs_rq.
+This 'prev' pointer can originate from struct rq's leaf_cfs_rq_list,
+making the conversion invalid and potentially leading to memory
+corruption. Depending on the relative positions of leaf_cfs_rq_list and
+the task group (tg) pointer within the struct, this can cause a memory
+fault or access garbage data.
+
+The issue arises in list_add_leaf_cfs_rq, where both
+cfs_rq->leaf_cfs_rq_list and rq->leaf_cfs_rq_list are added to the same
+leaf list. Also, rq->tmp_alone_branch can be set to rq->leaf_cfs_rq_list.
+
+This adds a check `if (prev == &rq->leaf_cfs_rq_list)` after the main
+conditional in child_cfs_rq_on_list. This ensures that the container_of
+operation will convert a correct cfs_rq struct.
+
+This check is sufficient because only cfs_rqs on the same CPU are added
+to the list, so verifying the 'prev' pointer against the current rq's list
+head is enough.
+
+Fixes a potential memory corruption issue that due to current struct
+layout might not be manifesting as a crash but could lead to unpredictable
+behavior when the layout changes.
+
+Fixes: fdaba61ef8a2 ("sched/fair: Ensure that the CFS parent is added after unthrottling")
+Signed-off-by: Zecheng Li <zecheng@google.com>
+Reviewed-and-tested-by: K Prateek Nayak <kprateek.nayak@amd.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Reviewed-by: Vincent Guittot <vincent.guittot@linaro.org>
+Link: https://lore.kernel.org/r/20250304214031.2882646-1-zecheng@google.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/sched/fair.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
+index ddc096d6b0c20..58ba14ed8fbcb 100644
+--- a/kernel/sched/fair.c
++++ b/kernel/sched/fair.c
+@@ -4155,15 +4155,17 @@ static inline bool child_cfs_rq_on_list(struct cfs_rq *cfs_rq)
+ {
+ struct cfs_rq *prev_cfs_rq;
+ struct list_head *prev;
++ struct rq *rq = rq_of(cfs_rq);
+
+ if (cfs_rq->on_list) {
+ prev = cfs_rq->leaf_cfs_rq_list.prev;
+ } else {
+- struct rq *rq = rq_of(cfs_rq);
+-
+ prev = rq->tmp_alone_branch;
+ }
+
++ if (prev == &rq->leaf_cfs_rq_list)
++ return false;
++
+ prev_cfs_rq = container_of(prev, struct cfs_rq, leaf_cfs_rq_list);
+
+ return (prev_cfs_rq->tg->parent == cfs_rq->tg);
+--
+2.39.5
+
mm-fix-finish_fault-handling-for-large-folios.patch
hwpoison-memory_hotplug-lock-folio-before-unmap-hwpoisoned-folio.patch
mm-memory-hotplug-check-folio-ref-count-first-in-do_migrate_range.patch
+wifi-iwlwifi-mvm-clean-up-roc-on-failure.patch
+wifi-iwlwifi-mvm-don-t-try-to-talk-to-a-dead-firmwar.patch
+wifi-iwlwifi-limit-printed-string-from-fw-file.patch
+wifi-iwlwifi-free-pages-allocated-when-failing-to-bu.patch
+wifi-iwlwifi-fix-a-msdu-tso-preparation.patch
+hid-google-fix-unused-variable-warning-under-config_.patch
+hid-intel-ish-hid-fix-use-after-free-issue-in-hid_is.patch
+hid-intel-ish-hid-fix-use-after-free-issue-in-ishtp_.patch
+coredump-only-sort-vmas-when-core_sort_vma-sysctl-is.patch
+nvme-pci-add-support-for-sgl-metadata.patch
+nvme-pci-use-sgls-for-all-user-requests-if-possible.patch
+nvme-ioctl-fix-leaked-requests-on-mapping-error.patch
+wifi-mac80211-support-parsing-epcs-ml-element.patch
+wifi-mac80211-fix-mle-non-inheritance-parsing.patch
+wifi-mac80211-fix-vendor-specific-inheritance.patch
+drm-fbdev-helper-move-color-mode-lookup-into-4cc-for.patch
+drm-fbdev-add-memory-agnostic-fbdev-client.patch
+drm-add-client-agnostic-setup-helper.patch
+drm-fbdev-ttm-support-struct-drm_driver.fbdev_probe.patch
+drm-nouveau-run-drm-default-client-setup.patch
+drm-nouveau-select-fw-caching.patch
+bluetooth-btusb-initialize-.owner-field-of-force_pol.patch
+nvme-tcp-add-basic-support-for-the-c2htermreq-pdu.patch
+nvme-tcp-fix-potential-memory-corruption-in-nvme_tcp.patch
+nvmet-tcp-fix-a-possible-sporadic-response-drops-in-.patch
+alsa-hda-realtek-remove-revert-duplicate-ally-x-conf.patch
+net-gso-fix-ownership-in-__udp_gso_segment.patch
+caif_virtio-fix-wrong-pointer-check-in-cfv_probe.patch
+perf-core-fix-pmus_lock-vs.-pmus_srcu-ordering.patch
+hwmon-pmbus-initialise-page-count-in-pmbus_identify.patch
+hwmon-ntc_thermistor-fix-the-ncpxxxh103-sensor-table.patch
+hwmon-ad7314-validate-leading-zero-bits-and-return-e.patch
+tracing-probe-events-remove-unused-max_arg_buf_len-m.patch
+drm-imagination-fix-timestamps-in-firmware-traces.patch
+alsa-usx2y-validate-nrpacks-module-parameter-on-prob.patch
+llc-do-not-use-skb_get-before-dev_queue_xmit.patch
+hwmon-fix-a-null-vs-is_err_or_null-check-in-xgene_hw.patch
+drm-sched-fix-preprocessor-guard.patch
+be2net-fix-sleeping-while-atomic-bugs-in-be_ndo_brid.patch
+net-hns3-make-sure-ptp-clock-is-unregister-and-freed.patch
+fs-pipe-read-pipe-head-tail-atomically-outside-pipe-.patch
+drm-i915-color-extract-intel_color_modeset.patch
+drm-i915-plumb-dsb-all-way-to-the-plane-hooks.patch
+drm-xe-remove-double-pageflip.patch
+hid-hid-steam-fix-use-after-free-when-detaching-devi.patch
+net-ipa-fix-v4.7-resource-group-names.patch
+net-ipa-fix-qsb-data-for-v4.7.patch
+net-ipa-enable-checksum-for-ipa_endpoint_ap_modem_-r.patch
+ppp-fix-kmsan-uninit-value-warning-with-bpf.patch
+ethtool-linkstate-migrate-linkstate-functions-to-sup.patch
+net-ethtool-plumb-phy-stats-to-phy-drivers.patch
+net-ethtool-netlink-allow-null-nlattrs-when-getting-.patch
+vlan-enforce-underlying-device-type.patch
+x86-sgx-fix-size-overflows-in-sgx_encl_create.patch
+exfat-fix-just-enough-dentries-but-allocate-a-new-cl.patch
+exfat-fix-soft-lockup-in-exfat_clear_bitmap.patch
+exfat-short-circuit-zero-byte-writes-in-exfat_file_w.patch
+net-timestamp-support-tcp-gso-case-for-a-few-missing.patch
+ublk-set_params-properly-check-if-parameters-can-be-.patch
+sched-fair-fix-potential-memory-corruption-in-child_.patch
+fs-pipe-fix-pipe_occupancy-with-16-bit-indexes.patch
+nvme-tcp-fix-signedness-bug-in-nvme_tcp_init_connect.patch
+net-dsa-mt7530-fix-traffic-flooding-for-mmio-devices.patch
+mctp-i3c-handle-null-header-address.patch
+net-ipv6-fix-dst-ref-loop-in-ila-lwtunnel.patch
+net-ipv6-fix-missing-dst-ref-drop-in-ila-lwtunnel.patch
+gpio-rcar-fix-missing-of_node_put-call.patch
+fs-pipe-do-not-open-code-pipe-head-tail-logic-in-fio.patch
+fs-pipe-fix-pipe-buffer-index-use-in-fuse.patch
--- /dev/null
+From 5c64913ca9ad17e281410f591ca1c9dd9fe3758c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 26 Feb 2025 15:19:18 +0900
+Subject: tracing: probe-events: Remove unused MAX_ARG_BUF_LEN macro
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ Upstream commit fd5ba38390c59e1c147480ae49b6133c4ac24001 ]
+
+Commit 18b1e870a496 ("tracing/probes: Add $arg* meta argument for all
+function args") introduced MAX_ARG_BUF_LEN but it is not used.
+Remove it.
+
+Link: https://lore.kernel.org/all/174055075876.4079315.8805416872155957588.stgit@mhiramat.tok.corp.google.com/
+
+Fixes: 18b1e870a496 ("tracing/probes: Add $arg* meta argument for all function args")
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/trace/trace_probe.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
+index fba3ede870541..8a6797c2278d9 100644
+--- a/kernel/trace/trace_probe.h
++++ b/kernel/trace/trace_probe.h
+@@ -36,7 +36,6 @@
+ #define MAX_BTF_ARGS_LEN 128
+ #define MAX_DENTRY_ARGS_LEN 256
+ #define MAX_STRING_SIZE PATH_MAX
+-#define MAX_ARG_BUF_LEN (MAX_TRACE_ARGS * MAX_ARG_NAME_LEN)
+
+ /* Reserved field names */
+ #define FIELD_STRING_IP "__probe_ip"
+--
+2.39.5
+
--- /dev/null
+From 38a8a209d2459fb92a4fb101a8d7486418388f79 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 4 Mar 2025 14:34:26 -0700
+Subject: ublk: set_params: properly check if parameters can be applied
+
+From: Uday Shankar <ushankar@purestorage.com>
+
+[ Upstream commit 5ac60242b0173be83709603ebaf27a473f16c4e4 ]
+
+The parameters set by the set_params call are only applied to the block
+device in the start_dev call. So if a device has already been started, a
+subsequently issued set_params on that device will not have the desired
+effect, and should return an error. There is an existing check for this
+- set_params fails on devices in the LIVE state. But this check is not
+sufficient to cover the recovery case. In this case, the device will be
+in the QUIESCED or FAIL_IO states, so set_params will succeed. But this
+success is misleading, because the parameters will not be applied, since
+the device has already been started (by a previous ublk server). The bit
+UB_STATE_USED is set on completion of the start_dev; use it to detect
+and fail set_params commands which arrive too late to be applied (after
+start_dev).
+
+Signed-off-by: Uday Shankar <ushankar@purestorage.com>
+Fixes: 0aa73170eba5 ("ublk_drv: add SET_PARAMS/GET_PARAMS control command")
+Reviewed-by: Ming Lei <ming.lei@redhat.com>
+Link: https://lore.kernel.org/r/20250304-set_params-v1-1-17b5e0887606@purestorage.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/block/ublk_drv.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
+index 458ac54e7b201..c7d728d686e5a 100644
+--- a/drivers/block/ublk_drv.c
++++ b/drivers/block/ublk_drv.c
+@@ -2665,9 +2665,12 @@ static int ublk_ctrl_set_params(struct ublk_device *ub,
+ if (ph.len > sizeof(struct ublk_params))
+ ph.len = sizeof(struct ublk_params);
+
+- /* parameters can only be changed when device isn't live */
+ mutex_lock(&ub->mutex);
+- if (ub->dev_info.state == UBLK_S_DEV_LIVE) {
++ if (test_bit(UB_STATE_USED, &ub->state)) {
++ /*
++ * Parameters can only be changed when device hasn't
++ * been started yet
++ */
+ ret = -EACCES;
+ } else if (copy_from_user(&ub->params, argp, ph.len)) {
+ ret = -EFAULT;
+--
+2.39.5
+
--- /dev/null
+From e4dd2d35892bb062e4b350c5ea08d551843741c6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 3 Mar 2025 16:56:19 +0100
+Subject: vlan: enforce underlying device type
+
+From: Oscar Maes <oscmaes92@gmail.com>
+
+[ Upstream commit b33a534610067ade2bdaf2052900aaad99701353 ]
+
+Currently, VLAN devices can be created on top of non-ethernet devices.
+
+Besides the fact that it doesn't make much sense, this also causes a
+bug which leaks the address of a kernel function to usermode.
+
+When creating a VLAN device, we initialize GARP (garp_init_applicant)
+and MRP (mrp_init_applicant) for the underlying device.
+
+As part of the initialization process, we add the multicast address of
+each applicant to the underlying device, by calling dev_mc_add.
+
+__dev_mc_add uses dev->addr_len to determine the length of the new
+multicast address.
+
+This causes an out-of-bounds read if dev->addr_len is greater than 6,
+since the multicast addresses provided by GARP and MRP are only 6
+bytes long.
+
+This behaviour can be reproduced using the following commands:
+
+ip tunnel add gretest mode ip6gre local ::1 remote ::2 dev lo
+ip l set up dev gretest
+ip link add link gretest name vlantest type vlan id 100
+
+Then, the following command will display the address of garp_pdu_rcv:
+
+ip maddr show | grep 01:80:c2:00:00:21
+
+Fix the bug by enforcing the type of the underlying device during VLAN
+device initialization.
+
+Fixes: 22bedad3ce11 ("net: convert multicast list to list_head")
+Reported-by: syzbot+91161fe81857b396c8a0@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/netdev/000000000000ca9a81061a01ec20@google.com/
+Signed-off-by: Oscar Maes <oscmaes92@gmail.com>
+Reviewed-by: Jiri Pirko <jiri@nvidia.com>
+Link: https://patch.msgid.link/20250303155619.8918-1-oscmaes92@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/8021q/vlan.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
+index e45187b882206..41be38264493d 100644
+--- a/net/8021q/vlan.c
++++ b/net/8021q/vlan.c
+@@ -131,7 +131,8 @@ int vlan_check_real_dev(struct net_device *real_dev,
+ {
+ const char *name = real_dev->name;
+
+- if (real_dev->features & NETIF_F_VLAN_CHALLENGED) {
++ if (real_dev->features & NETIF_F_VLAN_CHALLENGED ||
++ real_dev->type != ARPHRD_ETHER) {
+ pr_info("VLANs not supported on %s\n", name);
+ NL_SET_ERR_MSG_MOD(extack, "VLANs not supported on device");
+ return -EOPNOTSUPP;
+--
+2.39.5
+
--- /dev/null
+From 6bc9e26879756ba620c88511c367dd027afad611 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 9 Feb 2025 14:34:53 +0200
+Subject: wifi: iwlwifi: Fix A-MSDU TSO preparation
+
+From: Ilan Peer <ilan.peer@intel.com>
+
+[ Upstream commit 3640dbc1f75ce15d128ea4af44226960d894f3fd ]
+
+The TSO preparation assumed that the skb head contained the headers
+while the rest of the data was in the fragments. Since this is not
+always true, e.g., it is possible that the data was linearised, modify
+the TSO preparation to start the data processing after the network
+headers.
+
+Fixes: 7f5e3038f029 ("wifi: iwlwifi: map entire SKB when sending AMSDUs")
+Signed-off-by: Ilan Peer <ilan.peer@intel.com>
+Reviewed-by: Benjamin Berg <benjamin.berg@intel.com>
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Link: https://patch.msgid.link/20250209143303.75769a4769bf.Iaf79e8538093cdf8c446c292cc96164ad6498f61@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../wireless/intel/iwlwifi/pcie/internal.h | 5 +++--
+ .../net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 5 +++--
+ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 20 +++++++++++--------
+ 3 files changed, 18 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
+index 27a7e0b5b3d51..ebe9b25cc53a9 100644
+--- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
++++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
+@@ -1,6 +1,6 @@
+ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+ /*
+- * Copyright (C) 2003-2015, 2018-2024 Intel Corporation
++ * Copyright (C) 2003-2015, 2018-2025 Intel Corporation
+ * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2016-2017 Intel Deutschland GmbH
+ */
+@@ -643,7 +643,8 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset,
+ unsigned int len);
+ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_cmd_meta *cmd_meta,
+- u8 **hdr, unsigned int hdr_room);
++ u8 **hdr, unsigned int hdr_room,
++ unsigned int offset);
+
+ void iwl_pcie_free_tso_pages(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_cmd_meta *cmd_meta);
+diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
+index 7bb74a480d7f1..477a05cd1288b 100644
+--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
++++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
+@@ -1,7 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+ /*
+ * Copyright (C) 2017 Intel Deutschland GmbH
+- * Copyright (C) 2018-2020, 2023-2024 Intel Corporation
++ * Copyright (C) 2018-2020, 2023-2025 Intel Corporation
+ */
+ #include <net/tso.h>
+ #include <linux/tcp.h>
+@@ -188,7 +188,8 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans,
+ (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr));
+
+ /* Our device supports 9 segments at most, it will fit in 1 page */
+- sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room);
++ sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room,
++ snap_ip_tcp_hdrlen + hdr_len);
+ if (!sgt)
+ return -ENOMEM;
+
+diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
+index 9fe050f0ddc16..a74ce5ccf59bd 100644
+--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
++++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+ /*
+- * Copyright (C) 2003-2014, 2018-2021, 2023-2024 Intel Corporation
++ * Copyright (C) 2003-2014, 2018-2021, 2023-2025 Intel Corporation
+ * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2016-2017 Intel Deutschland GmbH
+ */
+@@ -1853,6 +1853,7 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset,
+ * @cmd_meta: command meta to store the scatter list information for unmapping
+ * @hdr: output argument for TSO headers
+ * @hdr_room: requested length for TSO headers
++ * @offset: offset into the data from which mapping should start
+ *
+ * Allocate space for a scatter gather list and TSO headers and map the SKB
+ * using the scatter gather list. The SKB is unmapped again when the page is
+@@ -1862,18 +1863,20 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset,
+ */
+ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_cmd_meta *cmd_meta,
+- u8 **hdr, unsigned int hdr_room)
++ u8 **hdr, unsigned int hdr_room,
++ unsigned int offset)
+ {
+ struct sg_table *sgt;
++ unsigned int n_segments;
+
+ if (WARN_ON_ONCE(skb_has_frag_list(skb)))
+ return NULL;
+
++ n_segments = DIV_ROUND_UP(skb->len - offset, skb_shinfo(skb)->gso_size);
+ *hdr = iwl_pcie_get_page_hdr(trans,
+ hdr_room + __alignof__(struct sg_table) +
+ sizeof(struct sg_table) +
+- (skb_shinfo(skb)->nr_frags + 1) *
+- sizeof(struct scatterlist),
++ n_segments * sizeof(struct scatterlist),
+ skb);
+ if (!*hdr)
+ return NULL;
+@@ -1881,11 +1884,11 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
+ sgt = (void *)PTR_ALIGN(*hdr + hdr_room, __alignof__(struct sg_table));
+ sgt->sgl = (void *)(sgt + 1);
+
+- sg_init_table(sgt->sgl, skb_shinfo(skb)->nr_frags + 1);
++ sg_init_table(sgt->sgl, n_segments);
+
+ /* Only map the data, not the header (it is copied to the TSO page) */
+- sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, skb_headlen(skb),
+- skb->data_len);
++ sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, offset,
++ skb->len - offset);
+ if (WARN_ON_ONCE(sgt->orig_nents <= 0))
+ return NULL;
+
+@@ -1937,7 +1940,8 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
+ (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len;
+
+ /* Our device supports 9 segments at most, it will fit in 1 page */
+- sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room);
++ sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room,
++ snap_ip_tcp_hdrlen + hdr_len + iv_len);
+ if (!sgt)
+ return -ENOMEM;
+
+--
+2.39.5
+
--- /dev/null
+From f0b7a3c0cf9b7f72632ba72ae58e189bf97e2752 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 9 Feb 2025 14:34:52 +0200
+Subject: wifi: iwlwifi: Free pages allocated when failing to build A-MSDU
+
+From: Ilan Peer <ilan.peer@intel.com>
+
+[ Upstream commit 3b08e608d50c44ca1135beed179f266aa0461da7 ]
+
+When failing to prepare the data needed for A-MSDU transmission, the memory
+allocated for the TSO management was not freed. Fix it.
+
+Fixes: 7f5e3038f029 ("wifi: iwlwifi: map entire SKB when sending AMSDUs")
+Signed-off-by: Ilan Peer <ilan.peer@intel.com>
+Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Link: https://patch.msgid.link/20250209143303.bc27fad9b3d5.Ibf43dd18fb652b1a59061204e081f11c9fa34a3f@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
+index b1846abb99b78..7bb74a480d7f1 100644
+--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
++++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
+@@ -347,6 +347,7 @@ iwl_tfh_tfd *iwl_txq_gen2_build_tx_amsdu(struct iwl_trans *trans,
+ return tfd;
+
+ out_err:
++ iwl_pcie_free_tso_pages(trans, skb, out_meta);
+ iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd);
+ return NULL;
+ }
+--
+2.39.5
+
--- /dev/null
+From 71215540ff44955679a75342dd5fbebf85ed65c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 9 Feb 2025 14:34:51 +0200
+Subject: wifi: iwlwifi: limit printed string from FW file
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+[ Upstream commit e0dc2c1bef722cbf16ae557690861e5f91208129 ]
+
+There's no guarantee here that the file is always with a
+NUL-termination, so reading the string may read beyond the
+end of the TLV. If that's the last TLV in the file, it can
+perhaps even read beyond the end of the file buffer.
+
+Fix that by limiting the print format to the size of the
+buffer we have.
+
+Fixes: aee1b6385e29 ("iwlwifi: support fseq tlv and print fseq version")
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Link: https://patch.msgid.link/20250209143303.cb5f9d0c2f5d.Idec695d53c6c2234aade306f7647b576c7e3d928@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+index c620911a11933..754e01688900d 100644
+--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
++++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+@@ -1197,7 +1197,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;
+--
+2.39.5
+
--- /dev/null
+From ad06a465aafb1f438038480aa82475647e776051 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 9 Feb 2025 14:34:47 +0200
+Subject: wifi: iwlwifi: mvm: clean up ROC on failure
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+[ Upstream commit f9751163bffd3fe60794929829f810968c6de73d ]
+
+If the firmware fails to start the session protection, then we
+do call iwl_mvm_roc_finished() here, but that won't do anything
+at all because IWL_MVM_STATUS_ROC_P2P_RUNNING was never set.
+Set IWL_MVM_STATUS_ROC_P2P_RUNNING in the failure/stop path.
+If it started successfully before, it's already set, so that
+doesn't matter, and if it didn't start it needs to be set to
+clean up.
+
+Not doing so will lead to a WARN_ON() later on a fresh remain-
+on-channel, since the link is already active when activated as
+it was never deactivated.
+
+Fixes: 35c1bbd93c4e ("wifi: iwlwifi: mvm: remove IWL_MVM_STATUS_NEED_FLUSH_P2P")
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Link: https://patch.msgid.link/20250209143303.0fe36c291068.I67f5dac742170dd937f11e4d4f937f45f71b7cb4@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
+index 72fa7ac86516c..17b8ccc275693 100644
+--- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
++++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
+@@ -1030,6 +1030,8 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm,
+ /* End TE, notify mac80211 */
+ mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID;
+ mvmvif->time_event_data.link_id = -1;
++ /* set the bit so the ROC cleanup will actually clean up */
++ set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status);
+ iwl_mvm_roc_finished(mvm);
+ ieee80211_remain_on_channel_expired(mvm->hw);
+ } else if (le32_to_cpu(notif->start)) {
+--
+2.39.5
+
--- /dev/null
+From de3aac79bf04ecc6c00fb5d8bd45e00b6b9a35e6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 9 Feb 2025 14:34:49 +0200
+Subject: wifi: iwlwifi: mvm: don't try to talk to a dead firmware
+
+From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
+
+[ Upstream commit d73d2c6e3313f0ba60711ab4f4b9044eddca9ca5 ]
+
+This fixes:
+
+ bad state = 0
+ WARNING: CPU: 10 PID: 702 at drivers/net/wireless/inel/iwlwifi/iwl-trans.c:178 iwl_trans_send_cmd+0xba/0xe0 [iwlwifi]
+ Call Trace:
+ <TASK>
+ ? __warn+0xca/0x1c0
+ ? iwl_trans_send_cmd+0xba/0xe0 [iwlwifi 64fa9ad799a0e0d2ba53d4af93a53ad9a531f8d4]
+ iwl_fw_dbg_clear_monitor_buf+0xd7/0x110 [iwlwifi 64fa9ad799a0e0d2ba53d4af93a53ad9a531f8d4]
+ _iwl_dbgfs_fw_dbg_clear_write+0xe2/0x120 [iwlmvm 0e8adb18cea92d2c341766bcc10b18699290068a]
+
+Ask whether the firmware is alive before sending a command.
+
+Fixes: 268712dc3b34 ("wifi: iwlwifi: mvm: add a debugfs hook to clear the monitor data")
+Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Link: https://patch.msgid.link/20250209143303.8e1597b62c70.I12ea71dd9b805b095c9fc12a10c9f34a4e801b61@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
+index 91ca830a7b603..f4276fdee6bea 100644
+--- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
++++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
+@@ -1518,6 +1518,13 @@ static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mvm *mvm,
+ if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)
+ return -EOPNOTSUPP;
+
++ /*
++ * If the firmware is not running, silently succeed since there is
++ * no data to clear.
++ */
++ if (!iwl_mvm_firmware_running(mvm))
++ return count;
++
+ mutex_lock(&mvm->mutex);
+ iwl_fw_dbg_clear_monitor_buf(&mvm->fwrt);
+ mutex_unlock(&mvm->mutex);
+--
+2.39.5
+
--- /dev/null
+From 75904a55d4cff57a37152bd9847963d1bdffceb7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 21 Feb 2025 11:24:50 +0100
+Subject: wifi: mac80211: fix MLE non-inheritance parsing
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+[ Upstream commit 99ca2c28e6b68084a0fb65585df09b9e28c3ec16 ]
+
+The code is erroneously applying the non-inheritance element
+to the inner elements rather than the outer, which is clearly
+completely wrong. Fix it by finding the MLE basic element at
+the beginning, and then applying the non-inheritance for the
+outer parsing.
+
+While at it, do some general cleanups such as not allowing
+callers to try looking for a specific non-transmitted BSS
+and link at the same time.
+
+Fixes: 45ebac4f059b ("wifi: mac80211: Parse station profile from association response")
+Reviewed-by: Ilan Peer <ilan.peer@intel.com>
+Reviewed-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
+Link: https://patch.msgid.link/20250221112451.b46d42f45b66.If5b95dc3c80208e0c62d8895fb6152aa54b6620b@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/mlme.c | 1 +
+ net/mac80211/parse.c | 127 ++++++++++++++++++++++++++++---------------
+ 2 files changed, 83 insertions(+), 45 deletions(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 111066928b963..88751b0eb317a 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -4733,6 +4733,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
+ parse_params.start = bss_ies->data;
+ parse_params.len = bss_ies->len;
+ parse_params.bss = cbss;
++ parse_params.link_id = -1;
+ bss_elems = ieee802_11_parse_elems_full(&parse_params);
+ if (!bss_elems) {
+ ret = false;
+diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
+index cd318c1c67bec..3d5d6658fe8d5 100644
+--- a/net/mac80211/parse.c
++++ b/net/mac80211/parse.c
+@@ -47,6 +47,8 @@ struct ieee80211_elems_parse {
+ /* The EPCS Multi-Link element in the original elements */
+ const struct element *ml_epcs_elem;
+
++ bool multi_link_inner;
++
+ /*
+ * scratch buffer that can be used for various element parsing related
+ * tasks, e.g., element de-fragmentation etc.
+@@ -152,12 +154,11 @@ ieee80211_parse_extension_element(u32 *crc,
+ switch (le16_get_bits(mle->control,
+ IEEE80211_ML_CONTROL_TYPE)) {
+ case IEEE80211_ML_CONTROL_TYPE_BASIC:
+- if (elems_parse->ml_basic_elem) {
++ if (elems_parse->multi_link_inner) {
+ elems->parse_error |=
+ IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC;
+ break;
+ }
+- elems_parse->ml_basic_elem = elem;
+ break;
+ case IEEE80211_ML_CONTROL_TYPE_RECONF:
+ elems_parse->ml_reconf_elem = elem;
+@@ -866,21 +867,36 @@ ieee80211_mle_get_sta_prof(struct ieee80211_elems_parse *elems_parse,
+ }
+ }
+
+-static void ieee80211_mle_parse_link(struct ieee80211_elems_parse *elems_parse,
+- struct ieee80211_elems_parse_params *params)
++static const struct element *
++ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse,
++ struct ieee80211_elems_parse_params *params,
++ struct ieee80211_elems_parse_params *sub)
+ {
+ struct ieee802_11_elems *elems = &elems_parse->elems;
+ struct ieee80211_mle_per_sta_profile *prof;
+- struct ieee80211_elems_parse_params sub = {
+- .mode = params->mode,
+- .action = params->action,
+- .from_ap = params->from_ap,
+- .link_id = -1,
+- };
+- ssize_t ml_len = elems->ml_basic_len;
+- const struct element *non_inherit = NULL;
++ const struct element *tmp;
++ ssize_t ml_len;
+ const u8 *end;
+
++ if (params->mode < IEEE80211_CONN_MODE_EHT)
++ return NULL;
++
++ for_each_element_extid(tmp, WLAN_EID_EXT_EHT_MULTI_LINK,
++ elems->ie_start, elems->total_len) {
++ const struct ieee80211_multi_link_elem *mle =
++ (void *)tmp->data + 1;
++
++ if (!ieee80211_mle_size_ok(tmp->data + 1, tmp->datalen - 1))
++ continue;
++
++ if (le16_get_bits(mle->control, IEEE80211_ML_CONTROL_TYPE) !=
++ IEEE80211_ML_CONTROL_TYPE_BASIC)
++ continue;
++
++ elems_parse->ml_basic_elem = tmp;
++ break;
++ }
++
+ ml_len = cfg80211_defragment_element(elems_parse->ml_basic_elem,
+ elems->ie_start,
+ elems->total_len,
+@@ -891,26 +907,26 @@ static void ieee80211_mle_parse_link(struct ieee80211_elems_parse *elems_parse,
+ WLAN_EID_FRAGMENT);
+
+ if (ml_len < 0)
+- return;
++ return NULL;
+
+ elems->ml_basic = (const void *)elems_parse->scratch_pos;
+ elems->ml_basic_len = ml_len;
+ elems_parse->scratch_pos += ml_len;
+
+ if (params->link_id == -1)
+- return;
++ return NULL;
+
+ ieee80211_mle_get_sta_prof(elems_parse, params->link_id);
+ prof = elems->prof;
+
+ if (!prof)
+- return;
++ return NULL;
+
+ /* check if we have the 4 bytes for the fixed part in assoc response */
+ if (elems->sta_prof_len < sizeof(*prof) + prof->sta_info_len - 1 + 4) {
+ elems->prof = NULL;
+ elems->sta_prof_len = 0;
+- return;
++ return NULL;
+ }
+
+ /*
+@@ -919,13 +935,17 @@ static void ieee80211_mle_parse_link(struct ieee80211_elems_parse *elems_parse,
+ * the -1 is because the 'sta_info_len' is accounted to as part of the
+ * per-STA profile, but not part of the 'u8 variable[]' portion.
+ */
+- sub.start = prof->variable + prof->sta_info_len - 1 + 4;
++ sub->start = prof->variable + prof->sta_info_len - 1 + 4;
+ end = (const u8 *)prof + elems->sta_prof_len;
+- sub.len = end - sub.start;
++ sub->len = end - sub->start;
+
+- non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
+- sub.start, sub.len);
+- _ieee802_11_parse_elems_full(&sub, elems_parse, non_inherit);
++ sub->mode = params->mode;
++ sub->action = params->action;
++ sub->from_ap = params->from_ap;
++ sub->link_id = -1;
++
++ return cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
++ sub->start, sub->len);
+ }
+
+ static void
+@@ -973,15 +993,19 @@ ieee80211_mle_defrag_epcs(struct ieee80211_elems_parse *elems_parse)
+ struct ieee802_11_elems *
+ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
+ {
++ struct ieee80211_elems_parse_params sub = {};
+ struct ieee80211_elems_parse *elems_parse;
+- struct ieee802_11_elems *elems;
+ const struct element *non_inherit = NULL;
+- u8 *nontransmitted_profile;
+- int nontransmitted_profile_len = 0;
++ struct ieee802_11_elems *elems;
+ size_t scratch_len = 3 * params->len;
++ bool multi_link_inner = false;
+
+ BUILD_BUG_ON(offsetof(typeof(*elems_parse), elems) != 0);
+
++ /* cannot parse for both a specific link and non-transmitted BSS */
++ if (WARN_ON(params->link_id >= 0 && params->bss))
++ return NULL;
++
+ elems_parse = kzalloc(struct_size(elems_parse, scratch, scratch_len),
+ GFP_ATOMIC);
+ if (!elems_parse)
+@@ -998,34 +1022,47 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
+ ieee80211_clear_tpe(&elems->tpe);
+ ieee80211_clear_tpe(&elems->csa_tpe);
+
+- nontransmitted_profile = elems_parse->scratch_pos;
+- nontransmitted_profile_len =
+- ieee802_11_find_bssid_profile(params->start, params->len,
+- elems, params->bss,
+- nontransmitted_profile);
+- elems_parse->scratch_pos += nontransmitted_profile_len;
+- non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
+- nontransmitted_profile,
+- nontransmitted_profile_len);
++ /*
++ * If we're looking for a non-transmitted BSS then we cannot at
++ * the same time be looking for a second link as the two can only
++ * appear in the same frame carrying info for different BSSes.
++ *
++ * In any case, we only look for one at a time, as encoded by
++ * the WARN_ON above.
++ */
++ if (params->bss) {
++ int nontx_len =
++ ieee802_11_find_bssid_profile(params->start,
++ params->len,
++ elems, params->bss,
++ elems_parse->scratch_pos);
++ sub.start = elems_parse->scratch_pos;
++ sub.mode = params->mode;
++ sub.len = nontx_len;
++ sub.action = params->action;
++ sub.link_id = params->link_id;
++
++ /* consume the space used for non-transmitted profile */
++ elems_parse->scratch_pos += nontx_len;
++
++ non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
++ sub.start, nontx_len);
++ } else {
++ /* must always parse to get elems_parse->ml_basic_elem */
++ non_inherit = ieee80211_prep_mle_link_parse(elems_parse, params,
++ &sub);
++ multi_link_inner = true;
++ }
+
+ elems->crc = _ieee802_11_parse_elems_full(params, elems_parse,
+ non_inherit);
+
+- /* Override with nontransmitted profile, if found */
+- if (nontransmitted_profile_len) {
+- struct ieee80211_elems_parse_params sub = {
+- .mode = params->mode,
+- .start = nontransmitted_profile,
+- .len = nontransmitted_profile_len,
+- .action = params->action,
+- .link_id = params->link_id,
+- };
+-
++ /* Override with nontransmitted/per-STA profile if found */
++ if (sub.len) {
++ elems_parse->multi_link_inner = multi_link_inner;
+ _ieee802_11_parse_elems_full(&sub, elems_parse, NULL);
+ }
+
+- ieee80211_mle_parse_link(elems_parse, params);
+-
+ ieee80211_mle_defrag_reconf(elems_parse);
+
+ ieee80211_mle_defrag_epcs(elems_parse);
+--
+2.39.5
+
--- /dev/null
+From 1569b67840b4a125a62592b675c6140c13c8e640 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 21 Feb 2025 11:24:51 +0100
+Subject: wifi: mac80211: fix vendor-specific inheritance
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+[ Upstream commit 130067e9c13bdc4820748ef16076a6972364745f ]
+
+If there's any vendor-specific element in the subelements
+then the outer element parsing must not parse any vendor
+element at all. This isn't implemented correctly now due
+to parsing into the pointers and then overriding them, so
+explicitly skip vendor elements if any exist in the sub-
+elements (non-transmitted profile or per-STA profile).
+
+Fixes: 671042a4fb77 ("mac80211: support non-inheritance element")
+Reviewed-by: Ilan Peer <ilan.peer@intel.com>
+Reviewed-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
+Link: https://patch.msgid.link/20250221112451.fd71e5268840.I9db3e6a3367e6ff38d052d07dc07005f0dd3bd5c@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/parse.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
+index 3d5d6658fe8d5..6da39c864f45b 100644
+--- a/net/mac80211/parse.c
++++ b/net/mac80211/parse.c
+@@ -48,6 +48,7 @@ struct ieee80211_elems_parse {
+ const struct element *ml_epcs_elem;
+
+ bool multi_link_inner;
++ bool skip_vendor;
+
+ /*
+ * scratch buffer that can be used for various element parsing related
+@@ -400,6 +401,9 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
+ IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+ break;
+ case WLAN_EID_VENDOR_SPECIFIC:
++ if (elems_parse->skip_vendor)
++ break;
++
+ if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
+ pos[2] == 0xf2) {
+ /* Microsoft OUI (00:50:F2) */
+@@ -1054,12 +1058,16 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
+ multi_link_inner = true;
+ }
+
++ elems_parse->skip_vendor =
++ cfg80211_find_elem(WLAN_EID_VENDOR_SPECIFIC,
++ sub.start, sub.len);
+ elems->crc = _ieee802_11_parse_elems_full(params, elems_parse,
+ non_inherit);
+
+ /* Override with nontransmitted/per-STA profile if found */
+ if (sub.len) {
+ elems_parse->multi_link_inner = multi_link_inner;
++ elems_parse->skip_vendor = false;
+ _ieee802_11_parse_elems_full(&sub, elems_parse, NULL);
+ }
+
+--
+2.39.5
+
--- /dev/null
+From a6c416f2e00a4d85e92ff91bd7aa39f0e44ae127 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Jan 2025 16:20:01 +0200
+Subject: wifi: mac80211: Support parsing EPCS ML element
+
+From: Ilan Peer <ilan.peer@intel.com>
+
+[ Upstream commit 24711d60f8492a30622e419cee643d59264ea939 ]
+
+Add support for parsing an ML element of type EPCS priority
+access, which can optionally be included in EHT protected action
+frames used to configure EPCS.
+
+Signed-off-by: Ilan Peer <ilan.peer@intel.com>
+Reviewed-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Link: https://patch.msgid.link/20250102161730.5afdf65cff46.I0ffa30b40fbad47bc5b608b5fd46047a8c44e904@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Stable-dep-of: 99ca2c28e6b6 ("wifi: mac80211: fix MLE non-inheritance parsing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/ieee80211_i.h | 2 ++
+ net/mac80211/parse.c | 29 +++++++++++++++++++++++++++++
+ 2 files changed, 31 insertions(+)
+
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index 7a0242e937d36..bfe0514efca37 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1751,6 +1751,7 @@ struct ieee802_11_elems {
+ const struct ieee80211_eht_operation *eht_operation;
+ const struct ieee80211_multi_link_elem *ml_basic;
+ const struct ieee80211_multi_link_elem *ml_reconf;
++ const struct ieee80211_multi_link_elem *ml_epcs;
+ const struct ieee80211_bandwidth_indication *bandwidth_indication;
+ const struct ieee80211_ttlm_elem *ttlm[IEEE80211_TTLM_MAX_CNT];
+
+@@ -1781,6 +1782,7 @@ struct ieee802_11_elems {
+ /* mult-link element can be de-fragmented and thus u8 is not sufficient */
+ size_t ml_basic_len;
+ size_t ml_reconf_len;
++ size_t ml_epcs_len;
+
+ u8 ttlm_num;
+
+diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
+index 279c5143b3356..cd318c1c67bec 100644
+--- a/net/mac80211/parse.c
++++ b/net/mac80211/parse.c
+@@ -44,6 +44,9 @@ struct ieee80211_elems_parse {
+ /* The reconfiguration Multi-Link element in the original elements */
+ const struct element *ml_reconf_elem;
+
++ /* The EPCS Multi-Link element in the original elements */
++ const struct element *ml_epcs_elem;
++
+ /*
+ * scratch buffer that can be used for various element parsing related
+ * tasks, e.g., element de-fragmentation etc.
+@@ -159,6 +162,9 @@ ieee80211_parse_extension_element(u32 *crc,
+ case IEEE80211_ML_CONTROL_TYPE_RECONF:
+ elems_parse->ml_reconf_elem = elem;
+ break;
++ case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS:
++ elems_parse->ml_epcs_elem = elem;
++ break;
+ default:
+ break;
+ }
+@@ -943,6 +949,27 @@ ieee80211_mle_defrag_reconf(struct ieee80211_elems_parse *elems_parse)
+ elems_parse->scratch_pos += ml_len;
+ }
+
++static void
++ieee80211_mle_defrag_epcs(struct ieee80211_elems_parse *elems_parse)
++{
++ struct ieee802_11_elems *elems = &elems_parse->elems;
++ ssize_t ml_len;
++
++ ml_len = cfg80211_defragment_element(elems_parse->ml_epcs_elem,
++ elems->ie_start,
++ elems->total_len,
++ elems_parse->scratch_pos,
++ elems_parse->scratch +
++ elems_parse->scratch_len -
++ elems_parse->scratch_pos,
++ WLAN_EID_FRAGMENT);
++ if (ml_len < 0)
++ return;
++ elems->ml_epcs = (void *)elems_parse->scratch_pos;
++ elems->ml_epcs_len = ml_len;
++ elems_parse->scratch_pos += ml_len;
++}
++
+ struct ieee802_11_elems *
+ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
+ {
+@@ -1001,6 +1028,8 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
+
+ ieee80211_mle_defrag_reconf(elems_parse);
+
++ ieee80211_mle_defrag_epcs(elems_parse);
++
+ if (elems->tim && !elems->parse_error) {
+ const struct ieee80211_tim_ie *tim_ie = elems->tim;
+
+--
+2.39.5
+
--- /dev/null
+From 8ef246f9347c8708e9d6f1c2c33c786b99f3e8ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Mar 2025 07:00:05 +0200
+Subject: x86/sgx: Fix size overflows in sgx_encl_create()
+
+From: Jarkko Sakkinen <jarkko@kernel.org>
+
+[ Upstream commit 0d3e0dfd68fb9e6b0ec865be9f3377cc3ff55733 ]
+
+The total size calculated for EPC can overflow u64 given the added up page
+for SECS. Further, the total size calculated for shmem can overflow even
+when the EPC size stays within limits of u64, given that it adds the extra
+space for 128 byte PCMD structures (one for each page).
+
+Address this by pre-evaluating the micro-architectural requirement of
+SGX: the address space size must be power of two. This is eventually
+checked up by ECREATE but the pre-check has the additional benefit of
+making sure that there is some space for additional data.
+
+Fixes: 888d24911787 ("x86/sgx: Add SGX_IOC_ENCLAVE_CREATE")
+Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
+Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Ingo Molnar <mingo@kernel.org>
+Acked-by: Dave Hansen <dave.hansen@intel.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: "H. Peter Anvin" <hpa@zytor.com>
+Link: https://lore.kernel.org/r/20250305050006.43896-1-jarkko@kernel.org
+
+Closes: https://lore.kernel.org/linux-sgx/c87e01a0-e7dd-4749-a348-0980d3444f04@stanley.mountain/
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/cpu/sgx/ioctl.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c
+index b65ab214bdf57..776a20172867e 100644
+--- a/arch/x86/kernel/cpu/sgx/ioctl.c
++++ b/arch/x86/kernel/cpu/sgx/ioctl.c
+@@ -64,6 +64,13 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
+ struct file *backing;
+ long ret;
+
++ /*
++ * ECREATE would detect this too, but checking here also ensures
++ * that the 'encl_size' calculations below can never overflow.
++ */
++ if (!is_power_of_2(secs->size))
++ return -EINVAL;
++
+ va_page = sgx_encl_grow(encl, true);
+ if (IS_ERR(va_page))
+ return PTR_ERR(va_page);
+--
+2.39.5
+