From: Sasha Levin Date: Sun, 9 Mar 2025 19:40:19 +0000 (-0400) Subject: Fixes for 6.13 X-Git-Tag: v5.4.291~94 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dccc7f714cbb6d3b25aa8ac1d91cb5ecb995dbed;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 6.13 Signed-off-by: Sasha Levin --- diff --git a/queue-6.13/alsa-hda-realtek-remove-revert-duplicate-ally-x-conf.patch b/queue-6.13/alsa-hda-realtek-remove-revert-duplicate-ally-x-conf.patch new file mode 100644 index 0000000000..cf203dc90e --- /dev/null +++ b/queue-6.13/alsa-hda-realtek-remove-revert-duplicate-ally-x-conf.patch @@ -0,0 +1,64 @@ +From 51274b4c540d884d00138de0599ebda1f127116c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 18:51:06 +0100 +Subject: ALSA: hda/realtek: Remove (revert) duplicate Ally X config + +From: Antheas Kapenekakis + +[ 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 +Link: https://patch.msgid.link/20250227175107.33432-2-lkml@antheas.dev +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + 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 2cd606900b8b0..e86c4d894ea30 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -7761,7 +7761,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, +@@ -9206,12 +9205,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[]) { +@@ -10705,7 +10698,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 + diff --git a/queue-6.13/alsa-usx2y-validate-nrpacks-module-parameter-on-prob.patch b/queue-6.13/alsa-usx2y-validate-nrpacks-module-parameter-on-prob.patch new file mode 100644 index 0000000000..4dd82e1695 --- /dev/null +++ b/queue-6.13/alsa-usx2y-validate-nrpacks-module-parameter-on-prob.patch @@ -0,0 +1,135 @@ +From 58650e6a19064173af6d4cb4a0751991e7b2170a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 3 Mar 2025 13:04:13 +0300 +Subject: ALSA: usx2y: validate nrpacks module parameter on probe + +From: Murad Masimov + +[ 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 +Link: https://patch.msgid.link/20250303100413.835-1-m.masimov@mt-integration.ru +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/be2net-fix-sleeping-while-atomic-bugs-in-be_ndo_brid.patch b/queue-6.13/be2net-fix-sleeping-while-atomic-bugs-in-be_ndo_brid.patch new file mode 100644 index 0000000000..448f7fbc36 --- /dev/null +++ b/queue-6.13/be2net-fix-sleeping-while-atomic-bugs-in-be_ndo_brid.patch @@ -0,0 +1,951 @@ +From 5df7c90854bc35b612180dada693200c38621609 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 18:41:29 +0200 +Subject: be2net: fix sleeping while atomic bugs in be_ndo_bridge_getlink + +From: Nikolay Aleksandrov + +[ 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 +Fixes: b71724147e73 ("be2net: replace polling with sleeping in the FW completion path") +Signed-off-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/20250227164129.1201164-1-razor@blackwall.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/bluetooth-btusb-initialize-.owner-field-of-force_pol.patch b/queue-6.13/bluetooth-btusb-initialize-.owner-field-of-force_pol.patch new file mode 100644 index 0000000000..03c4ddc797 --- /dev/null +++ b/queue-6.13/bluetooth-btusb-initialize-.owner-field-of-force_pol.patch @@ -0,0 +1,35 @@ +From 20bea8103ada2df992147915056665341ac6b0df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 21 Feb 2025 22:32:59 +0100 +Subject: bluetooth: btusb: Initialize .owner field of force_poll_sync_fops + +From: Salah Triki + +[ 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 +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btusb.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c +index 72e85673b7095..75dbe07e07e21 100644 +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -3652,6 +3652,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 + diff --git a/queue-6.13/caif_virtio-fix-wrong-pointer-check-in-cfv_probe.patch b/queue-6.13/caif_virtio-fix-wrong-pointer-check-in-cfv_probe.patch new file mode 100644 index 0000000000..67c72c23cc --- /dev/null +++ b/queue-6.13/caif_virtio-fix-wrong-pointer-check-in-cfv_probe.patch @@ -0,0 +1,43 @@ +From 5e288daf3bc446c7488aafb8f21f7e662272553c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 23:46:27 +0500 +Subject: caif_virtio: fix wrong pointer check in cfv_probe() + +From: Vitaliy Shevtsov + +[ 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 +Reviewed-by: Gerhard Engleder +Link: https://patch.msgid.link/20250227184716.4715-1-v.shevtsov@mt-integration.ru +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/coredump-only-sort-vmas-when-core_sort_vma-sysctl-is.patch b/queue-6.13/coredump-only-sort-vmas-when-core_sort_vma-sysctl-is.patch new file mode 100644 index 0000000000..b170dc7e5e --- /dev/null +++ b/queue-6.13/coredump-only-sort-vmas-when-core_sort_vma-sysctl-is.patch @@ -0,0 +1,89 @@ +From 8712544b26ee025805cd83d2fc950db3f1a9f10a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 19 Feb 2025 11:53:16 -0800 +Subject: coredump: Only sort VMAs when core_sort_vma sysctl is set + +From: Kees Cook + +[ 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 +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 +Signed-off-by: Sasha Levin +--- + 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 b2b36d0c3094d..7a85b6eb884ea 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 d48edb37bc35c..c6f4c745e1bd8 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; +@@ -1026,6 +1027,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) +@@ -1256,8 +1266,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 + diff --git a/queue-6.13/cred-fix-rcu-warnings-in-override-revert_creds.patch b/queue-6.13/cred-fix-rcu-warnings-in-override-revert_creds.patch new file mode 100644 index 0000000000..e31a928447 --- /dev/null +++ b/queue-6.13/cred-fix-rcu-warnings-in-override-revert_creds.patch @@ -0,0 +1,53 @@ +From 4ffaad7bd171912068e8b0c85cd327aa6c0501c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 2 Mar 2025 15:18:24 +0800 +Subject: cred: Fix RCU warnings in override/revert_creds + +From: Herbert Xu + +[ Upstream commit e04918dc594669068f5d59d567d08db531167188 ] + +Fix RCU warnings in override_creds and revert_creds by turning +the RCU pointer into a normal pointer using rcu_replace_pointer. + +These warnings were previously private to the cred code, but due +to the move into the header file they are now polluting unrelated +subsystems. + +Fixes: 49dffdfde462 ("cred: Add a light version of override/revert_creds()") +Signed-off-by: Herbert Xu +Link: https://lore.kernel.org/r/Z8QGQGW0IaSklKG7@gondor.apana.org.au +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + include/linux/cred.h | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +diff --git a/include/linux/cred.h b/include/linux/cred.h +index 382768a9707b5..1e1ec8834e454 100644 +--- a/include/linux/cred.h ++++ b/include/linux/cred.h +@@ -179,18 +179,12 @@ static inline bool cap_ambient_invariant_ok(const struct cred *cred) + */ + static inline const struct cred *override_creds_light(const struct cred *override_cred) + { +- const struct cred *old = current->cred; +- +- rcu_assign_pointer(current->cred, override_cred); +- return old; ++ return rcu_replace_pointer(current->cred, override_cred, 1); + } + + static inline const struct cred *revert_creds_light(const struct cred *revert_cred) + { +- const struct cred *override_cred = current->cred; +- +- rcu_assign_pointer(current->cred, revert_cred); +- return override_cred; ++ return rcu_replace_pointer(current->cred, revert_cred, 1); + } + + /** +-- +2.39.5 + diff --git a/queue-6.13/cred-return-old-creds-from-revert_creds_light.patch b/queue-6.13/cred-return-old-creds-from-revert_creds_light.patch new file mode 100644 index 0000000000..02d43f53f5 --- /dev/null +++ b/queue-6.13/cred-return-old-creds-from-revert_creds_light.patch @@ -0,0 +1,43 @@ +From 17eef0c3ff364e08e48bee730ca7e66a9dccc3d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 Nov 2024 15:09:58 +0100 +Subject: cred: return old creds from revert_creds_light() + +From: Christian Brauner + +[ Upstream commit 95c54bc81791c210b131f2b1013942487e74896f ] + +So we can easily convert revert_creds() callers over to drop the +reference count explicitly. + +Link: https://lore.kernel.org/r/20241125-work-cred-v2-2-68b9d38bb5b2@kernel.org +Reviewed-by: Jeff Layton +Reviewed-by: Jens Axboe +Signed-off-by: Christian Brauner +Stable-dep-of: e04918dc5946 ("cred: Fix RCU warnings in override/revert_creds") +Signed-off-by: Sasha Levin +--- + include/linux/cred.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/include/linux/cred.h b/include/linux/cred.h +index e4a3155fe409d..382768a9707b5 100644 +--- a/include/linux/cred.h ++++ b/include/linux/cred.h +@@ -185,9 +185,12 @@ static inline const struct cred *override_creds_light(const struct cred *overrid + return old; + } + +-static inline void revert_creds_light(const struct cred *revert_cred) ++static inline const struct cred *revert_creds_light(const struct cred *revert_cred) + { ++ const struct cred *override_cred = current->cred; ++ + rcu_assign_pointer(current->cred, revert_cred); ++ return override_cred; + } + + /** +-- +2.39.5 + diff --git a/queue-6.13/drm-bochs-fix-dpms-regression.patch b/queue-6.13/drm-bochs-fix-dpms-regression.patch new file mode 100644 index 0000000000..230bf1efda --- /dev/null +++ b/queue-6.13/drm-bochs-fix-dpms-regression.patch @@ -0,0 +1,50 @@ +From 8450eab7171bdac790f40def130960e1cd019bf2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Mar 2025 14:41:57 +0100 +Subject: drm/bochs: Fix DPMS regression + +From: Takashi Iwai + +[ Upstream commit 80da96d735094ea22985ced98bc57fe3a4422921 ] + +The recent rewrite with the use of regular atomic helpers broke the +DPMS unblanking on X11. Fix it by moving the call of +bochs_hw_blank(false) from CRTC mode_set_nofb() to atomic_enable(). + +Fixes: 2037174993c8 ("drm/bochs: Use regular atomic helpers") +Link: https://bugzilla.suse.com/show_bug.cgi?id=1238209 +Signed-off-by: Takashi Iwai +Reviewed-by: Thomas Zimmermann +Signed-off-by: Thomas Zimmermann +Link: https://patchwork.freedesktop.org/patch/msgid/20250304134203.20534-1-tiwai@suse.de +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/tiny/bochs.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c +index 6f91ff1dbf7e2..4e7d49fb9e6e6 100644 +--- a/drivers/gpu/drm/tiny/bochs.c ++++ b/drivers/gpu/drm/tiny/bochs.c +@@ -335,8 +335,6 @@ static void bochs_hw_setmode(struct bochs_device *bochs, struct drm_display_mode + bochs->xres, bochs->yres, bochs->bpp, + bochs->yres_virtual); + +- bochs_hw_blank(bochs, false); +- + bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 0); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, bochs->bpp); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, bochs->xres); +@@ -506,6 +504,9 @@ static int bochs_crtc_helper_atomic_check(struct drm_crtc *crtc, + static void bochs_crtc_helper_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) + { ++ struct bochs_device *bochs = to_bochs_device(crtc->dev); ++ ++ bochs_hw_blank(bochs, false); + } + + static void bochs_crtc_helper_atomic_disable(struct drm_crtc *crtc, +-- +2.39.5 + diff --git a/queue-6.13/drm-imagination-fix-timestamps-in-firmware-traces.patch b/queue-6.13/drm-imagination-fix-timestamps-in-firmware-traces.patch new file mode 100644 index 0000000000..04f1f0f952 --- /dev/null +++ b/queue-6.13/drm-imagination-fix-timestamps-in-firmware-traces.patch @@ -0,0 +1,59 @@ +From a47213ff79f6dc76216f4d69cd192cf8bdd8eb2e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 21 Feb 2025 10:49:35 +0000 +Subject: drm/imagination: Fix timestamps in firmware traces + +From: Alessio Belle + +[ 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 +Reviewed-by: Matt Coster +Link: https://patchwork.freedesktop.org/patch/msgid/20250221-fix-fw-trace-timestamps-v1-1-dba4aeb030ca@imgtec.com +Signed-off-by: Matt Coster +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/drm-nouveau-select-fw-caching.patch b/queue-6.13/drm-nouveau-select-fw-caching.patch new file mode 100644 index 0000000000..1b87cc1ab7 --- /dev/null +++ b/queue-6.13/drm-nouveau-select-fw-caching.patch @@ -0,0 +1,37 @@ +From c54f61254dfcbb1b436482e42f16e5e2a8b160fc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 7 Feb 2025 11:25:31 +1000 +Subject: drm/nouveau: select FW caching + +From: Dave Airlie + +[ 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 +Signed-off-by: Danilo Krummrich +Link: https://patchwork.freedesktop.org/patch/msgid/20250207012531.621369-1-airlied@gmail.com +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/drm-sched-fix-preprocessor-guard.patch b/queue-6.13/drm-sched-fix-preprocessor-guard.patch new file mode 100644 index 0000000000..ec0c4247c9 --- /dev/null +++ b/queue-6.13/drm-sched-fix-preprocessor-guard.patch @@ -0,0 +1,48 @@ +From bd73827fec0e65cb3e6da9b0c84c2c9ea77ba719 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 18 Feb 2025 13:41:50 +0100 +Subject: drm/sched: Fix preprocessor guard + +From: Philipp Stanner + +[ 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 +Signed-off-by: Philipp Stanner +Link: https://patchwork.freedesktop.org/patch/msgid/20250218124149.118002-2-phasta@kernel.org +Signed-off-by: Sasha Levin +--- + 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 +@@ -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 + diff --git a/queue-6.13/drm-xe-remove-double-pageflip.patch b/queue-6.13/drm-xe-remove-double-pageflip.patch new file mode 100644 index 0000000000..c2768de431 --- /dev/null +++ b/queue-6.13/drm-xe-remove-double-pageflip.patch @@ -0,0 +1,58 @@ +From aa47c3a1a4a227d86e2e4e9b16c5b0d3b8606d4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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ä +Reviewed-by: Vinod Govindapillai +Reviewed-by: Lucas De Marchi +Link: https://patchwork.freedesktop.org/patch/msgid/20241210083111.230484-3-dev@lankhorst.se +Signed-off-by: Maarten Lankhorst +(cherry picked from commit 2218704997979fbf11765281ef752f07c5cf25bb) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + 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 8c113463a3d55..668f544a7ac80 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 + diff --git a/queue-6.13/ethtool-linkstate-migrate-linkstate-functions-to-sup.patch b/queue-6.13/ethtool-linkstate-migrate-linkstate-functions-to-sup.patch new file mode 100644 index 0000000000..a5909f82c0 --- /dev/null +++ b/queue-6.13/ethtool-linkstate-migrate-linkstate-functions-to-sup.patch @@ -0,0 +1,100 @@ +From 0aa788e31f8f0ac2a82c7aa7fe01906afed461de Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Jan 2025 07:05:11 +0100 +Subject: ethtool: linkstate: migrate linkstate functions to support multi-PHY + setups + +From: Oleksij Rempel + +[ 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 +Signed-off-by: Paolo Abeni +Stable-dep-of: 637399bf7e77 ("net: ethtool: netlink: Allow NULL nlattrs when getting a phy_device") +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/exfat-fix-just-enough-dentries-but-allocate-a-new-cl.patch b/queue-6.13/exfat-fix-just-enough-dentries-but-allocate-a-new-cl.patch new file mode 100644 index 0000000000..5e972829bd --- /dev/null +++ b/queue-6.13/exfat-fix-just-enough-dentries-but-allocate-a-new-cl.patch @@ -0,0 +1,38 @@ +From 4cca8401e41774d3e64a945e1fa16bcc19d915d4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Namjae Jeon +Signed-off-by: Sasha Levin +--- + 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 099f806450721..9996ca61c85b7 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 + diff --git a/queue-6.13/exfat-fix-soft-lockup-in-exfat_clear_bitmap.patch b/queue-6.13/exfat-fix-soft-lockup-in-exfat_clear_bitmap.patch new file mode 100644 index 0000000000..5885b25045 --- /dev/null +++ b/queue-6.13/exfat-fix-soft-lockup-in-exfat_clear_bitmap.patch @@ -0,0 +1,129 @@ +From 9ce1e14704a01ce4edca97e41768f9b058ff562f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 31 Jan 2025 12:55:55 +0900 +Subject: exfat: fix soft lockup in exfat_clear_bitmap + +From: Namjae Jeon + +[ 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 , Jiaji Qin +Reviewed-by: Sungjong Seo +Signed-off-by: Namjae Jeon +Signed-off-by: Sasha Levin +--- + 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 78be6964a8a08..d30ce18a88b7a 100644 +--- a/fs/exfat/exfat_fs.h ++++ b/fs/exfat/exfat_fs.h +@@ -456,7 +456,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 + diff --git a/queue-6.13/exfat-short-circuit-zero-byte-writes-in-exfat_file_w.patch b/queue-6.13/exfat-short-circuit-zero-byte-writes-in-exfat_file_w.patch new file mode 100644 index 0000000000..310cca1801 --- /dev/null +++ b/queue-6.13/exfat-short-circuit-zero-byte-writes-in-exfat_file_w.patch @@ -0,0 +1,44 @@ +From 21846bfe892c62859c1a9f79456ecb23301dc31d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Feb 2025 14:14:21 -0600 +Subject: exfat: short-circuit zero-byte writes in exfat_file_write_iter + +From: Eric Sandeen + +[ 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 +Signed-off-by: Eric Sandeen +Reviewed-by: Yuezhang Mo +Signed-off-by: Namjae Jeon +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/fgraph-replace-fgraph_ret_regs-with-ftrace_regs.patch b/queue-6.13/fgraph-replace-fgraph_ret_regs-with-ftrace_regs.patch new file mode 100644 index 0000000000..6682a8c09e --- /dev/null +++ b/queue-6.13/fgraph-replace-fgraph_ret_regs-with-ftrace_regs.patch @@ -0,0 +1,766 @@ +From 8d08d4ef90d31596e14da03d051eaa3dc100344a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Dec 2024 14:11:55 +0900 +Subject: fgraph: Replace fgraph_ret_regs with ftrace_regs + +From: Masami Hiramatsu (Google) + +[ Upstream commit a3ed4157b7d89800a0008de0c9e46a438a5c3745 ] + +Use ftrace_regs instead of fgraph_ret_regs for tracing return value +on function_graph tracer because of simplifying the callback interface. + +The CONFIG_HAVE_FUNCTION_GRAPH_RETVAL is also replaced by +CONFIG_HAVE_FUNCTION_GRAPH_FREGS. + +Signed-off-by: Masami Hiramatsu (Google) +Acked-by: Heiko Carstens +Acked-by: Will Deacon +Cc: Catalin Marinas +Cc: Alexei Starovoitov +Cc: Florent Revest +Cc: Martin KaFai Lau +Cc: bpf +Cc: Alexei Starovoitov +Cc: Jiri Olsa +Cc: Alan Maguire +Cc: Mark Rutland +Cc: Huacai Chen +Cc: WANG Xuerui +Cc: Paul Walmsley +Cc: Palmer Dabbelt +Cc: Albert Ou +Cc: Vasily Gorbik +Cc: Alexander Gordeev +Cc: Heiko Carstens +Cc: Christian Borntraeger +Cc: Sven Schnelle +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: Borislav Petkov +Cc: Dave Hansen +Cc: x86@kernel.org +Cc: "H. Peter Anvin" +Cc: Mathieu Desnoyers +Link: https://lore.kernel.org/173518991508.391279.16635322774382197642.stgit@devnote2 +Signed-off-by: Steven Rostedt (Google) +Stable-dep-of: db5e228611b1 ("tracing: fprobe-events: Log error for exceeding the number of entry args") +Signed-off-by: Sasha Levin +--- + arch/arm64/Kconfig | 1 + + arch/arm64/include/asm/ftrace.h | 23 ++++++--------------- + arch/arm64/kernel/asm-offsets.c | 12 ----------- + arch/arm64/kernel/entry-ftrace.S | 32 ++++++++++++++++------------- + arch/loongarch/Kconfig | 2 +- + arch/loongarch/include/asm/ftrace.h | 26 ++++------------------- + arch/loongarch/kernel/asm-offsets.c | 12 ----------- + arch/loongarch/kernel/mcount.S | 17 ++++++++------- + arch/loongarch/kernel/mcount_dyn.S | 14 ++++++------- + arch/riscv/Kconfig | 2 +- + arch/riscv/include/asm/ftrace.h | 26 +++++------------------ + arch/riscv/kernel/mcount.S | 24 ++++++++++++---------- + arch/s390/Kconfig | 2 +- + arch/s390/include/asm/ftrace.h | 24 +++++++--------------- + arch/s390/kernel/asm-offsets.c | 6 ------ + arch/s390/kernel/mcount.S | 12 +++++------ + arch/x86/Kconfig | 2 +- + arch/x86/include/asm/ftrace.h | 20 ------------------ + arch/x86/kernel/ftrace_32.S | 13 ++++++------ + arch/x86/kernel/ftrace_64.S | 17 +++++++-------- + include/linux/ftrace.h | 12 ++++++++--- + include/linux/ftrace_regs.h | 2 ++ + kernel/trace/Kconfig | 4 ++-- + kernel/trace/fgraph.c | 21 ++++++++----------- + 24 files changed, 119 insertions(+), 207 deletions(-) + +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 100570a048c5e..5f086777dad9b 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -219,6 +219,7 @@ config ARM64 + select HAVE_FTRACE_MCOUNT_RECORD + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_ERROR_INJECTION ++ select HAVE_FUNCTION_GRAPH_FREGS + select HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_RETVAL + select HAVE_GCC_PLUGINS +diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h +index 5ccff4de7f091..b5fa57b61378e 100644 +--- a/arch/arm64/include/asm/ftrace.h ++++ b/arch/arm64/include/asm/ftrace.h +@@ -129,6 +129,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs) + arch_ftrace_regs(fregs)->pc = arch_ftrace_regs(fregs)->lr; + } + ++static __always_inline unsigned long ++ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs) ++{ ++ return arch_ftrace_regs(fregs)->fp; ++} ++ + int ftrace_regs_query_register_offset(const char *name); + + int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec); +@@ -186,23 +192,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym, + + #ifndef __ASSEMBLY__ + #ifdef CONFIG_FUNCTION_GRAPH_TRACER +-struct fgraph_ret_regs { +- /* x0 - x7 */ +- unsigned long regs[8]; +- +- unsigned long fp; +- unsigned long __unused; +-}; +- +-static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->regs[0]; +-} +- +-static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->fp; +-} + + void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, + unsigned long frame_pointer); +diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c +index 29bf85dacffe1..eb1a840e4110f 100644 +--- a/arch/arm64/kernel/asm-offsets.c ++++ b/arch/arm64/kernel/asm-offsets.c +@@ -179,18 +179,6 @@ int main(void) + DEFINE(FTRACE_OPS_FUNC, offsetof(struct ftrace_ops, func)); + #endif + BLANK(); +-#ifdef CONFIG_FUNCTION_GRAPH_TRACER +- DEFINE(FGRET_REGS_X0, offsetof(struct fgraph_ret_regs, regs[0])); +- DEFINE(FGRET_REGS_X1, offsetof(struct fgraph_ret_regs, regs[1])); +- DEFINE(FGRET_REGS_X2, offsetof(struct fgraph_ret_regs, regs[2])); +- DEFINE(FGRET_REGS_X3, offsetof(struct fgraph_ret_regs, regs[3])); +- DEFINE(FGRET_REGS_X4, offsetof(struct fgraph_ret_regs, regs[4])); +- DEFINE(FGRET_REGS_X5, offsetof(struct fgraph_ret_regs, regs[5])); +- DEFINE(FGRET_REGS_X6, offsetof(struct fgraph_ret_regs, regs[6])); +- DEFINE(FGRET_REGS_X7, offsetof(struct fgraph_ret_regs, regs[7])); +- DEFINE(FGRET_REGS_FP, offsetof(struct fgraph_ret_regs, fp)); +- DEFINE(FGRET_REGS_SIZE, sizeof(struct fgraph_ret_regs)); +-#endif + #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + DEFINE(FTRACE_OPS_DIRECT_CALL, offsetof(struct ftrace_ops, direct_call)); + #endif +diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S +index f0c16640ef215..169ccf600066b 100644 +--- a/arch/arm64/kernel/entry-ftrace.S ++++ b/arch/arm64/kernel/entry-ftrace.S +@@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph) + * @fp is checked against the value passed by ftrace_graph_caller(). + */ + SYM_CODE_START(return_to_handler) +- /* save return value regs */ +- sub sp, sp, #FGRET_REGS_SIZE +- stp x0, x1, [sp, #FGRET_REGS_X0] +- stp x2, x3, [sp, #FGRET_REGS_X2] +- stp x4, x5, [sp, #FGRET_REGS_X4] +- stp x6, x7, [sp, #FGRET_REGS_X6] +- str x29, [sp, #FGRET_REGS_FP] // parent's fp ++ /* Make room for ftrace_regs */ ++ sub sp, sp, #FREGS_SIZE ++ ++ /* Save return value regs */ ++ stp x0, x1, [sp, #FREGS_X0] ++ stp x2, x3, [sp, #FREGS_X2] ++ stp x4, x5, [sp, #FREGS_X4] ++ stp x6, x7, [sp, #FREGS_X6] ++ ++ /* Save the callsite's FP */ ++ str x29, [sp, #FREGS_FP] + + mov x0, sp +- bl ftrace_return_to_handler // addr = ftrace_return_to_hander(regs); ++ bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs); + mov x30, x0 // restore the original return address + +- /* restore return value regs */ +- ldp x0, x1, [sp, #FGRET_REGS_X0] +- ldp x2, x3, [sp, #FGRET_REGS_X2] +- ldp x4, x5, [sp, #FGRET_REGS_X4] +- ldp x6, x7, [sp, #FGRET_REGS_X6] +- add sp, sp, #FGRET_REGS_SIZE ++ /* Restore return value regs */ ++ ldp x0, x1, [sp, #FREGS_X0] ++ ldp x2, x3, [sp, #FREGS_X2] ++ ldp x4, x5, [sp, #FREGS_X4] ++ ldp x6, x7, [sp, #FREGS_X6] ++ add sp, sp, #FREGS_SIZE + + ret + SYM_CODE_END(return_to_handler) +diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig +index dae3a9104ca65..49f5bfc00e5a1 100644 +--- a/arch/loongarch/Kconfig ++++ b/arch/loongarch/Kconfig +@@ -137,7 +137,7 @@ config LOONGARCH + select HAVE_FTRACE_MCOUNT_RECORD + select HAVE_FUNCTION_ARG_ACCESS_API + select HAVE_FUNCTION_ERROR_INJECTION +- select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER ++ select HAVE_FUNCTION_GRAPH_FREGS + select HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_TRACER + select HAVE_GCC_PLUGINS +diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h +index 8f13eaeaa3251..ceb3e3d9c0d3d 100644 +--- a/arch/loongarch/include/asm/ftrace.h ++++ b/arch/loongarch/include/asm/ftrace.h +@@ -57,6 +57,10 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) + instruction_pointer_set(&arch_ftrace_regs(fregs)->regs, ip); + } + ++#undef ftrace_regs_get_frame_pointer ++#define ftrace_regs_get_frame_pointer(fregs) \ ++ (arch_ftrace_regs(fregs)->regs.regs[22]) ++ + #define ftrace_graph_func ftrace_graph_func + void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct ftrace_regs *fregs); +@@ -78,26 +82,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr) + + #endif /* CONFIG_FUNCTION_TRACER */ + +-#ifndef __ASSEMBLY__ +-#ifdef CONFIG_FUNCTION_GRAPH_TRACER +-struct fgraph_ret_regs { +- /* a0 - a1 */ +- unsigned long regs[2]; +- +- unsigned long fp; +- unsigned long __unused; +-}; +- +-static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->regs[0]; +-} +- +-static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->fp; +-} +-#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ +-#endif +- + #endif /* _ASM_LOONGARCH_FTRACE_H */ +diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c +index 049c5c3e370cb..8be1c38ad8eb2 100644 +--- a/arch/loongarch/kernel/asm-offsets.c ++++ b/arch/loongarch/kernel/asm-offsets.c +@@ -280,18 +280,6 @@ static void __used output_pbe_defines(void) + } + #endif + +-#ifdef CONFIG_FUNCTION_GRAPH_TRACER +-static void __used output_fgraph_ret_regs_defines(void) +-{ +- COMMENT("LoongArch fgraph_ret_regs offsets."); +- OFFSET(FGRET_REGS_A0, fgraph_ret_regs, regs[0]); +- OFFSET(FGRET_REGS_A1, fgraph_ret_regs, regs[1]); +- OFFSET(FGRET_REGS_FP, fgraph_ret_regs, fp); +- DEFINE(FGRET_REGS_SIZE, sizeof(struct fgraph_ret_regs)); +- BLANK(); +-} +-#endif +- + static void __used output_kvm_defines(void) + { + COMMENT("KVM/LoongArch Specific offsets."); +diff --git a/arch/loongarch/kernel/mcount.S b/arch/loongarch/kernel/mcount.S +index 3015896016a0b..b6850503e061b 100644 +--- a/arch/loongarch/kernel/mcount.S ++++ b/arch/loongarch/kernel/mcount.S +@@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller) + SYM_FUNC_END(ftrace_graph_caller) + + SYM_FUNC_START(return_to_handler) +- PTR_ADDI sp, sp, -FGRET_REGS_SIZE +- PTR_S a0, sp, FGRET_REGS_A0 +- PTR_S a1, sp, FGRET_REGS_A1 +- PTR_S zero, sp, FGRET_REGS_FP ++ /* Save return value regs */ ++ PTR_ADDI sp, sp, -PT_SIZE ++ PTR_S a0, sp, PT_R4 ++ PTR_S a1, sp, PT_R5 ++ PTR_S zero, sp, PT_R22 + + move a0, sp + bl ftrace_return_to_handler +@@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler) + /* Restore the real parent address: a0 -> ra */ + move ra, a0 + +- PTR_L a0, sp, FGRET_REGS_A0 +- PTR_L a1, sp, FGRET_REGS_A1 +- PTR_ADDI sp, sp, FGRET_REGS_SIZE ++ /* Restore return value regs */ ++ PTR_L a0, sp, PT_R4 ++ PTR_L a1, sp, PT_R5 ++ PTR_ADDI sp, sp, PT_SIZE ++ + jr ra + SYM_FUNC_END(return_to_handler) + #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S +index 0c65cf09110cd..d6b474ad1d5e5 100644 +--- a/arch/loongarch/kernel/mcount_dyn.S ++++ b/arch/loongarch/kernel/mcount_dyn.S +@@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller) + SYM_CODE_START(return_to_handler) + UNWIND_HINT_UNDEFINED + /* Save return value regs */ +- PTR_ADDI sp, sp, -FGRET_REGS_SIZE +- PTR_S a0, sp, FGRET_REGS_A0 +- PTR_S a1, sp, FGRET_REGS_A1 +- PTR_S zero, sp, FGRET_REGS_FP ++ PTR_ADDI sp, sp, -PT_SIZE ++ PTR_S a0, sp, PT_R4 ++ PTR_S a1, sp, PT_R5 ++ PTR_S zero, sp, PT_R22 + + move a0, sp + bl ftrace_return_to_handler + move ra, a0 + + /* Restore return value regs */ +- PTR_L a0, sp, FGRET_REGS_A0 +- PTR_L a1, sp, FGRET_REGS_A1 +- PTR_ADDI sp, sp, FGRET_REGS_SIZE ++ PTR_L a0, sp, PT_R4 ++ PTR_L a1, sp, PT_R5 ++ PTR_ADDI sp, sp, PT_SIZE + + jr ra + SYM_CODE_END(return_to_handler) +diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig +index d4a7ca0388c07..1e807c61258fb 100644 +--- a/arch/riscv/Kconfig ++++ b/arch/riscv/Kconfig +@@ -148,7 +148,7 @@ config RISCV + select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE + select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL + select HAVE_FUNCTION_GRAPH_TRACER +- select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER ++ select HAVE_FUNCTION_GRAPH_FREGS + select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION + select HAVE_EBPF_JIT if MMU + select HAVE_GUP_FAST if MMU +diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h +index 3d66437a10297..9372f8d7036f8 100644 +--- a/arch/riscv/include/asm/ftrace.h ++++ b/arch/riscv/include/asm/ftrace.h +@@ -168,6 +168,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const struct + return arch_ftrace_regs(fregs)->sp; + } + ++static __always_inline unsigned long ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs) ++{ ++ return arch_ftrace_regs(fregs)->s0; ++} ++ + static __always_inline unsigned long ftrace_regs_get_argument(struct ftrace_regs *fregs, + unsigned int n) + { +@@ -208,25 +213,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi + + #endif /* CONFIG_DYNAMIC_FTRACE */ + +-#ifndef __ASSEMBLY__ +-#ifdef CONFIG_FUNCTION_GRAPH_TRACER +-struct fgraph_ret_regs { +- unsigned long a1; +- unsigned long a0; +- unsigned long s0; +- unsigned long ra; +-}; +- +-static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->a0; +-} +- +-static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->s0; +-} +-#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ +-#endif +- + #endif /* _ASM_RISCV_FTRACE_H */ +diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S +index 3a42f6287909d..068168046e0ef 100644 +--- a/arch/riscv/kernel/mcount.S ++++ b/arch/riscv/kernel/mcount.S +@@ -12,6 +12,8 @@ + #include + #include + ++#define ABI_SIZE_ON_STACK 80 ++ + .text + + .macro SAVE_ABI_STATE +@@ -26,12 +28,12 @@ + * register if a0 was not saved. + */ + .macro SAVE_RET_ABI_STATE +- addi sp, sp, -4*SZREG +- REG_S s0, 2*SZREG(sp) +- REG_S ra, 3*SZREG(sp) +- REG_S a0, 1*SZREG(sp) +- REG_S a1, 0*SZREG(sp) +- addi s0, sp, 4*SZREG ++ addi sp, sp, -ABI_SIZE_ON_STACK ++ REG_S ra, 1*SZREG(sp) ++ REG_S s0, 8*SZREG(sp) ++ REG_S a0, 10*SZREG(sp) ++ REG_S a1, 11*SZREG(sp) ++ addi s0, sp, ABI_SIZE_ON_STACK + .endm + + .macro RESTORE_ABI_STATE +@@ -41,11 +43,11 @@ + .endm + + .macro RESTORE_RET_ABI_STATE +- REG_L ra, 3*SZREG(sp) +- REG_L s0, 2*SZREG(sp) +- REG_L a0, 1*SZREG(sp) +- REG_L a1, 0*SZREG(sp) +- addi sp, sp, 4*SZREG ++ REG_L ra, 1*SZREG(sp) ++ REG_L s0, 8*SZREG(sp) ++ REG_L a0, 10*SZREG(sp) ++ REG_L a1, 11*SZREG(sp) ++ addi sp, sp, ABI_SIZE_ON_STACK + .endm + + SYM_TYPED_FUNC_START(ftrace_stub) +diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig +index 83b1d7bbd8880..a8cecae74fe81 100644 +--- a/arch/s390/Kconfig ++++ b/arch/s390/Kconfig +@@ -193,7 +193,7 @@ config S390 + select HAVE_FTRACE_MCOUNT_RECORD + select HAVE_FUNCTION_ARG_ACCESS_API + select HAVE_FUNCTION_ERROR_INJECTION +- select HAVE_FUNCTION_GRAPH_RETVAL ++ select HAVE_FUNCTION_GRAPH_FREGS + select HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_TRACER + select HAVE_GCC_PLUGINS +diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h +index fc97d75dc752c..5c94c1fc1bc1c 100644 +--- a/arch/s390/include/asm/ftrace.h ++++ b/arch/s390/include/asm/ftrace.h +@@ -62,23 +62,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs * + return NULL; + } + +-#ifdef CONFIG_FUNCTION_GRAPH_TRACER +-struct fgraph_ret_regs { +- unsigned long gpr2; +- unsigned long fp; +-}; +- +-static __always_inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->gpr2; +-} +- +-static __always_inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->fp; +-} +-#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +- + static __always_inline void + ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, + unsigned long ip) +@@ -86,6 +69,13 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, + arch_ftrace_regs(fregs)->regs.psw.addr = ip; + } + ++#undef ftrace_regs_get_frame_pointer ++static __always_inline unsigned long ++ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs) ++{ ++ return ftrace_regs_get_stack_pointer(fregs); ++} ++ + #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + /* + * When an ftrace registered caller is tracing a function that is +diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c +index 862a9140528e9..36709112ae7a2 100644 +--- a/arch/s390/kernel/asm-offsets.c ++++ b/arch/s390/kernel/asm-offsets.c +@@ -175,12 +175,6 @@ int main(void) + DEFINE(OLDMEM_SIZE, PARMAREA + offsetof(struct parmarea, oldmem_size)); + DEFINE(COMMAND_LINE, PARMAREA + offsetof(struct parmarea, command_line)); + DEFINE(MAX_COMMAND_LINE_SIZE, PARMAREA + offsetof(struct parmarea, max_command_line_size)); +-#ifdef CONFIG_FUNCTION_GRAPH_TRACER +- /* function graph return value tracing */ +- OFFSET(__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2); +- OFFSET(__FGRAPH_RET_FP, fgraph_ret_regs, fp); +- DEFINE(__FGRAPH_RET_SIZE, sizeof(struct fgraph_ret_regs)); +-#endif + OFFSET(__FTRACE_REGS_PT_REGS, __arch_ftrace_regs, regs); + DEFINE(__FTRACE_REGS_SIZE, sizeof(struct __arch_ftrace_regs)); + +diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S +index 7e267ef63a7fe..2b628aa3d8095 100644 +--- a/arch/s390/kernel/mcount.S ++++ b/arch/s390/kernel/mcount.S +@@ -134,14 +134,14 @@ SYM_CODE_END(ftrace_common) + SYM_FUNC_START(return_to_handler) + stmg %r2,%r5,32(%r15) + lgr %r1,%r15 +- aghi %r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE) ++ # allocate ftrace_regs and stack frame for ftrace_return_to_handler ++ aghi %r15,-STACK_FRAME_SIZE_FREGS + stg %r1,__SF_BACKCHAIN(%r15) +- la %r3,STACK_FRAME_OVERHEAD(%r15) +- stg %r1,__FGRAPH_RET_FP(%r3) +- stg %r2,__FGRAPH_RET_GPR2(%r3) +- lgr %r2,%r3 ++ stg %r2,(STACK_FREGS_PTREGS_GPRS+2*8)(%r15) ++ stg %r1,(STACK_FREGS_PTREGS_GPRS+15*8)(%r15) ++ la %r2,STACK_FRAME_OVERHEAD(%r15) + brasl %r14,ftrace_return_to_handler +- aghi %r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE ++ aghi %r15,STACK_FRAME_SIZE_FREGS + lgr %r14,%r2 + lmg %r2,%r5,32(%r15) + BR_EX %r14 +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index 757333fe82c76..aa317f6064b89 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -234,7 +234,7 @@ config X86 + select HAVE_GUP_FAST + select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE + select HAVE_FTRACE_MCOUNT_RECORD +- select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER ++ select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE) + select HAVE_FUNCTION_TRACER + select HAVE_GCC_PLUGINS +diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h +index 6e8cf0fa48fc6..d61407c680c28 100644 +--- a/arch/x86/include/asm/ftrace.h ++++ b/arch/x86/include/asm/ftrace.h +@@ -134,24 +134,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) + #endif /* !COMPILE_OFFSETS */ + #endif /* !__ASSEMBLY__ */ + +-#ifndef __ASSEMBLY__ +-#ifdef CONFIG_FUNCTION_GRAPH_TRACER +-struct fgraph_ret_regs { +- unsigned long ax; +- unsigned long dx; +- unsigned long bp; +-}; +- +-static inline unsigned long fgraph_ret_regs_return_value(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->ax; +-} +- +-static inline unsigned long fgraph_ret_regs_frame_pointer(struct fgraph_ret_regs *ret_regs) +-{ +- return ret_regs->bp; +-} +-#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ +-#endif +- + #endif /* _ASM_X86_FTRACE_H */ +diff --git a/arch/x86/kernel/ftrace_32.S b/arch/x86/kernel/ftrace_32.S +index 58d9ed50fe617..f4e0c33612342 100644 +--- a/arch/x86/kernel/ftrace_32.S ++++ b/arch/x86/kernel/ftrace_32.S +@@ -187,14 +187,15 @@ SYM_CODE_END(ftrace_graph_caller) + + .globl return_to_handler + return_to_handler: +- pushl $0 +- pushl %edx +- pushl %eax ++ subl $(PTREGS_SIZE), %esp ++ movl $0, PT_EBP(%esp) ++ movl %edx, PT_EDX(%esp) ++ movl %eax, PT_EAX(%esp) + movl %esp, %eax + call ftrace_return_to_handler + movl %eax, %ecx +- popl %eax +- popl %edx +- addl $4, %esp # skip ebp ++ movl PT_EAX(%esp), %eax ++ movl PT_EDX(%esp), %edx ++ addl $(PTREGS_SIZE), %esp + JMP_NOSPEC ecx + #endif +diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S +index 214f30e9f0c01..d516472285967 100644 +--- a/arch/x86/kernel/ftrace_64.S ++++ b/arch/x86/kernel/ftrace_64.S +@@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__) + SYM_CODE_START(return_to_handler) + UNWIND_HINT_UNDEFINED + ANNOTATE_NOENDBR +- subq $24, %rsp + +- /* Save the return values */ +- movq %rax, (%rsp) +- movq %rdx, 8(%rsp) +- movq %rbp, 16(%rsp) ++ /* Save ftrace_regs for function exit context */ ++ subq $(FRAME_SIZE), %rsp ++ ++ movq %rax, RAX(%rsp) ++ movq %rdx, RDX(%rsp) ++ movq %rbp, RBP(%rsp) + movq %rsp, %rdi + + call ftrace_return_to_handler + + movq %rax, %rdi +- movq 8(%rsp), %rdx +- movq (%rsp), %rax ++ movq RDX(%rsp), %rdx ++ movq RAX(%rsp), %rax + +- addq $24, %rsp ++ addq $(FRAME_SIZE), %rsp + /* + * Jump back to the old return address. This cannot be JMP_NOSPEC rdi + * since IBT would demand that contain ENDBR, which simply isn't so for +diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h +index aa9ddd1e4bb6a..b7407004c799e 100644 +--- a/include/linux/ftrace.h ++++ b/include/linux/ftrace.h +@@ -43,9 +43,8 @@ struct dyn_ftrace; + + char *arch_ftrace_match_adjust(char *str, const char *search); + +-#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL +-struct fgraph_ret_regs; +-unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs); ++#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS ++unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs); + #else + unsigned long ftrace_return_to_handler(unsigned long frame_pointer); + #endif +@@ -134,6 +133,13 @@ extern int ftrace_enabled; + * Also, architecture dependent fields can be used for internal process. + * (e.g. orig_ax on x86_64) + * ++ * Basically, ftrace_regs stores the registers related to the context. ++ * On function entry, registers for function parameters and hooking the ++ * function call are stored, and on function exit, registers for function ++ * return value and frame pointers are stored. ++ * ++ * And also, it dpends on the context that which registers are restored ++ * from the ftrace_regs. + * On the function entry, those registers will be restored except for + * the stack pointer, so that user can change the function parameters + * and instruction pointer (e.g. live patching.) +diff --git a/include/linux/ftrace_regs.h b/include/linux/ftrace_regs.h +index be1ed0c891d07..bbc1873ca6b8e 100644 +--- a/include/linux/ftrace_regs.h ++++ b/include/linux/ftrace_regs.h +@@ -30,6 +30,8 @@ struct ftrace_regs; + override_function_with_return(&arch_ftrace_regs(fregs)->regs) + #define ftrace_regs_query_register_offset(name) \ + regs_query_register_offset(name) ++#define ftrace_regs_get_frame_pointer(fregs) \ ++ frame_pointer(&arch_ftrace_regs(fregs)->regs) + + #endif /* HAVE_ARCH_FTRACE_REGS */ + +diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig +index 74c2b1d43bb98..c5ab2a561272d 100644 +--- a/kernel/trace/Kconfig ++++ b/kernel/trace/Kconfig +@@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER + help + See Documentation/trace/ftrace-design.rst + +-config HAVE_FUNCTION_GRAPH_RETVAL ++config HAVE_FUNCTION_GRAPH_FREGS + bool + + config HAVE_DYNAMIC_FTRACE +@@ -232,7 +232,7 @@ config FUNCTION_GRAPH_TRACER + + config FUNCTION_GRAPH_RETVAL + bool "Kernel Function Graph Return Value" +- depends on HAVE_FUNCTION_GRAPH_RETVAL ++ depends on HAVE_FUNCTION_GRAPH_FREGS + depends on FUNCTION_GRAPH_TRACER + default n + help +diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c +index 30e3ddc8a8a84..6e55759b45776 100644 +--- a/kernel/trace/fgraph.c ++++ b/kernel/trace/fgraph.c +@@ -792,15 +792,12 @@ static struct notifier_block ftrace_suspend_notifier = { + .notifier_call = ftrace_suspend_notifier_call, + }; + +-/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ +-struct fgraph_ret_regs; +- + /* + * Send the trace to the ring-buffer. + * @return the original return address. + */ +-static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs, +- unsigned long frame_pointer) ++static inline unsigned long ++__ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointer) + { + struct ftrace_ret_stack *ret_stack; + struct ftrace_graph_ret trace; +@@ -820,7 +817,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs + + trace.rettime = trace_clock_local(); + #ifdef CONFIG_FUNCTION_GRAPH_RETVAL +- trace.retval = fgraph_ret_regs_return_value(ret_regs); ++ trace.retval = ftrace_regs_get_return_value(fregs); + #endif + + bitmap = get_bitmap_bits(current, offset); +@@ -855,14 +852,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs + } + + /* +- * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can +- * leave only ftrace_return_to_handler(ret_regs). ++ * After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can ++ * leave only ftrace_return_to_handler(fregs). + */ +-#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL +-unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs) ++#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS ++unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs) + { +- return __ftrace_return_to_handler(ret_regs, +- fgraph_ret_regs_frame_pointer(ret_regs)); ++ return __ftrace_return_to_handler(fregs, ++ ftrace_regs_get_frame_pointer(fregs)); + } + #else + unsigned long ftrace_return_to_handler(unsigned long frame_pointer) +-- +2.39.5 + diff --git a/queue-6.13/fprobe-rewrite-fprobe-on-function-graph-tracer.patch b/queue-6.13/fprobe-rewrite-fprobe-on-function-graph-tracer.patch new file mode 100644 index 0000000000..04302feece --- /dev/null +++ b/queue-6.13/fprobe-rewrite-fprobe-on-function-graph-tracer.patch @@ -0,0 +1,1168 @@ +From 98b2c6a182a85ab61910f92b12b78f066e34fd38 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Dec 2024 14:13:59 +0900 +Subject: fprobe: Rewrite fprobe on function-graph tracer + +From: Masami Hiramatsu (Google) + +[ Upstream commit 4346ba1604093305a287e08eb465a9c15ba05b80 ] + +Rewrite fprobe implementation on function-graph tracer. +Major API changes are: + - 'nr_maxactive' field is deprecated. + - This depends on CONFIG_DYNAMIC_FTRACE_WITH_ARGS or + !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS, and + CONFIG_HAVE_FUNCTION_GRAPH_FREGS. So currently works only + on x86_64. + - Currently the entry size is limited in 15 * sizeof(long). + - If there is too many fprobe exit handler set on the same + function, it will fail to probe. + +Signed-off-by: Masami Hiramatsu (Google) +Acked-by: Heiko Carstens # s390 +Cc: Alexei Starovoitov +Cc: Florent Revest +Cc: Martin KaFai Lau +Cc: bpf +Cc: Alexei Starovoitov +Cc: Jiri Olsa +Cc: Alan Maguire +Cc: Heiko Carstens +Cc: Mark Rutland +Cc: Catalin Marinas +Cc: Will Deacon +Cc: Huacai Chen +Cc: WANG Xuerui +Cc: Michael Ellerman +Cc: Nicholas Piggin +Cc: Christophe Leroy +Cc: Naveen N Rao +Cc: Madhavan Srinivasan +Cc: Paul Walmsley +Cc: Palmer Dabbelt +Cc: Albert Ou +Cc: Vasily Gorbik +Cc: Alexander Gordeev +Cc: Christian Borntraeger +Cc: Sven Schnelle +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: Borislav Petkov +Cc: Dave Hansen +Cc: x86@kernel.org +Cc: "H. Peter Anvin" +Cc: Mathieu Desnoyers +Cc: Andrew Morton +Link: https://lore.kernel.org/173519003970.391279.14406792285453830996.stgit@devnote2 +Signed-off-by: Steven Rostedt (Google) +Stable-dep-of: db5e228611b1 ("tracing: fprobe-events: Log error for exceeding the number of entry args") +Signed-off-by: Sasha Levin +--- + arch/arm64/include/asm/ftrace.h | 6 + + arch/loongarch/include/asm/ftrace.h | 6 + + arch/powerpc/include/asm/ftrace.h | 6 + + arch/riscv/include/asm/ftrace.h | 5 + + arch/s390/include/asm/ftrace.h | 6 + + arch/x86/include/asm/ftrace.h | 6 + + include/linux/fprobe.h | 58 ++- + kernel/trace/Kconfig | 8 +- + kernel/trace/fprobe.c | 637 ++++++++++++++++++++-------- + lib/test_fprobe.c | 45 -- + 10 files changed, 538 insertions(+), 245 deletions(-) + +diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h +index 10e56522122aa..876e88ad4119f 100644 +--- a/arch/arm64/include/asm/ftrace.h ++++ b/arch/arm64/include/asm/ftrace.h +@@ -135,6 +135,12 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs) + return arch_ftrace_regs(fregs)->fp; + } + ++static __always_inline unsigned long ++ftrace_regs_get_return_address(const struct ftrace_regs *fregs) ++{ ++ return arch_ftrace_regs(fregs)->lr; ++} ++ + static __always_inline struct pt_regs * + ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs) + { +diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h +index ceb3e3d9c0d3d..6e0a99763a9a7 100644 +--- a/arch/loongarch/include/asm/ftrace.h ++++ b/arch/loongarch/include/asm/ftrace.h +@@ -61,6 +61,12 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) + #define ftrace_regs_get_frame_pointer(fregs) \ + (arch_ftrace_regs(fregs)->regs.regs[22]) + ++static __always_inline unsigned long ++ftrace_regs_get_return_address(struct ftrace_regs *fregs) ++{ ++ return *(unsigned long *)(arch_ftrace_regs(fregs)->regs.regs[1]); ++} ++ + #define ftrace_graph_func ftrace_graph_func + void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct ftrace_regs *fregs); +diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h +index fe181bafdca4f..82da7c7a1d125 100644 +--- a/arch/powerpc/include/asm/ftrace.h ++++ b/arch/powerpc/include/asm/ftrace.h +@@ -57,6 +57,12 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, + regs_set_return_ip(&arch_ftrace_regs(fregs)->regs, ip); + } + ++static __always_inline unsigned long ++ftrace_regs_get_return_address(struct ftrace_regs *fregs) ++{ ++ return arch_ftrace_regs(fregs)->regs.link; ++} ++ + struct ftrace_ops; + + #define ftrace_graph_func ftrace_graph_func +diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h +index 7064a530794b6..c4721ce44ca47 100644 +--- a/arch/riscv/include/asm/ftrace.h ++++ b/arch/riscv/include/asm/ftrace.h +@@ -186,6 +186,11 @@ static __always_inline unsigned long ftrace_regs_get_return_value(const struct f + return arch_ftrace_regs(fregs)->a0; + } + ++static __always_inline unsigned long ftrace_regs_get_return_address(const struct ftrace_regs *fregs) ++{ ++ return arch_ftrace_regs(fregs)->ra; ++} ++ + static __always_inline void ftrace_regs_set_return_value(struct ftrace_regs *fregs, + unsigned long ret) + { +diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h +index 5b7cb49c41ee0..8c94a330c70c9 100644 +--- a/arch/s390/include/asm/ftrace.h ++++ b/arch/s390/include/asm/ftrace.h +@@ -76,6 +76,12 @@ ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs) + return ftrace_regs_get_stack_pointer(fregs); + } + ++static __always_inline unsigned long ++ftrace_regs_get_return_address(const struct ftrace_regs *fregs) ++{ ++ return arch_ftrace_regs(fregs)->regs.gprs[14]; ++} ++ + #define arch_ftrace_fill_perf_regs(fregs, _regs) do { \ + (_regs)->psw.mask = 0; \ + (_regs)->psw.addr = arch_ftrace_regs(fregs)->regs.psw.addr; \ +diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h +index 7e06f8c7937aa..cc92c99ef2760 100644 +--- a/arch/x86/include/asm/ftrace.h ++++ b/arch/x86/include/asm/ftrace.h +@@ -58,6 +58,12 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs) + do { arch_ftrace_regs(fregs)->regs.ip = (_ip); } while (0) + + ++static __always_inline unsigned long ++ftrace_regs_get_return_address(struct ftrace_regs *fregs) ++{ ++ return *(unsigned long *)ftrace_regs_get_stack_pointer(fregs); ++} ++ + struct ftrace_ops; + #define ftrace_graph_func ftrace_graph_func + void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, +diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h +index ef609bcca0f92..91337bcb452ff 100644 +--- a/include/linux/fprobe.h ++++ b/include/linux/fprobe.h +@@ -5,10 +5,11 @@ + + #include + #include +-#include ++#include ++#include ++#include + + struct fprobe; +- + typedef int (*fprobe_entry_cb)(struct fprobe *fp, unsigned long entry_ip, + unsigned long ret_ip, struct ftrace_regs *regs, + void *entry_data); +@@ -17,35 +18,57 @@ typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip, + unsigned long ret_ip, struct ftrace_regs *regs, + void *entry_data); + ++/** ++ * struct fprobe_hlist_node - address based hash list node for fprobe. ++ * ++ * @hlist: The hlist node for address search hash table. ++ * @addr: One of the probing address of @fp. ++ * @fp: The fprobe which owns this. ++ */ ++struct fprobe_hlist_node { ++ struct hlist_node hlist; ++ unsigned long addr; ++ struct fprobe *fp; ++}; ++ ++/** ++ * struct fprobe_hlist - hash list nodes for fprobe. ++ * ++ * @hlist: The hlist node for existence checking hash table. ++ * @rcu: rcu_head for RCU deferred release. ++ * @fp: The fprobe which owns this fprobe_hlist. ++ * @size: The size of @array. ++ * @array: The fprobe_hlist_node for each address to probe. ++ */ ++struct fprobe_hlist { ++ struct hlist_node hlist; ++ struct rcu_head rcu; ++ struct fprobe *fp; ++ int size; ++ struct fprobe_hlist_node array[] __counted_by(size); ++}; ++ + /** + * struct fprobe - ftrace based probe. +- * @ops: The ftrace_ops. ++ * + * @nmissed: The counter for missing events. + * @flags: The status flag. +- * @rethook: The rethook data structure. (internal data) + * @entry_data_size: The private data storage size. +- * @nr_maxactive: The max number of active functions. ++ * @nr_maxactive: The max number of active functions. (*deprecated) + * @entry_handler: The callback function for function entry. + * @exit_handler: The callback function for function exit. ++ * @hlist_array: The fprobe_hlist for fprobe search from IP hash table. + */ + struct fprobe { +-#ifdef CONFIG_FUNCTION_TRACER +- /* +- * If CONFIG_FUNCTION_TRACER is not set, CONFIG_FPROBE is disabled too. +- * But user of fprobe may keep embedding the struct fprobe on their own +- * code. To avoid build error, this will keep the fprobe data structure +- * defined here, but remove ftrace_ops data structure. +- */ +- struct ftrace_ops ops; +-#endif + unsigned long nmissed; + unsigned int flags; +- struct rethook *rethook; + size_t entry_data_size; + int nr_maxactive; + + fprobe_entry_cb entry_handler; + fprobe_exit_cb exit_handler; ++ ++ struct fprobe_hlist *hlist_array; + }; + + /* This fprobe is soft-disabled. */ +@@ -121,4 +144,9 @@ static inline void enable_fprobe(struct fprobe *fp) + fp->flags &= ~FPROBE_FL_DISABLED; + } + ++/* The entry data size is 4 bits (=16) * sizeof(long) in maximum */ ++#define FPROBE_DATA_SIZE_BITS 4 ++#define MAX_FPROBE_DATA_SIZE_WORD ((1L << FPROBE_DATA_SIZE_BITS) - 1) ++#define MAX_FPROBE_DATA_SIZE (MAX_FPROBE_DATA_SIZE_WORD * sizeof(long)) ++ + #endif +diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig +index 7f8165f2049a5..69954212c77d1 100644 +--- a/kernel/trace/Kconfig ++++ b/kernel/trace/Kconfig +@@ -302,11 +302,9 @@ config DYNAMIC_FTRACE_WITH_ARGS + + config FPROBE + bool "Kernel Function Probe (fprobe)" +- depends on FUNCTION_TRACER +- depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS +- depends on HAVE_FTRACE_REGS_HAVING_PT_REGS || !HAVE_DYNAMIC_FTRACE_WITH_ARGS +- depends on HAVE_RETHOOK +- select RETHOOK ++ depends on HAVE_FUNCTION_GRAPH_FREGS && HAVE_FTRACE_GRAPH_FUNC ++ depends on DYNAMIC_FTRACE_WITH_ARGS ++ select FUNCTION_GRAPH_TRACER + default n + help + This option enables kernel function probe (fprobe) based on ftrace. +diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c +index 90a3c8e2bbdf1..ed9c1d79426a2 100644 +--- a/kernel/trace/fprobe.c ++++ b/kernel/trace/fprobe.c +@@ -8,98 +8,195 @@ + #include + #include + #include +-#include ++#include ++#include + #include + #include + + #include "trace.h" + +-struct fprobe_rethook_node { +- struct rethook_node node; +- unsigned long entry_ip; +- unsigned long entry_parent_ip; +- char data[]; +-}; ++#define FPROBE_IP_HASH_BITS 8 ++#define FPROBE_IP_TABLE_SIZE (1 << FPROBE_IP_HASH_BITS) + +-static inline void __fprobe_handler(unsigned long ip, unsigned long parent_ip, +- struct ftrace_ops *ops, struct ftrace_regs *fregs) +-{ +- struct fprobe_rethook_node *fpr; +- struct rethook_node *rh = NULL; +- struct fprobe *fp; +- void *entry_data = NULL; +- int ret = 0; ++#define FPROBE_HASH_BITS 6 ++#define FPROBE_TABLE_SIZE (1 << FPROBE_HASH_BITS) + +- fp = container_of(ops, struct fprobe, ops); ++#define SIZE_IN_LONG(x) ((x + sizeof(long) - 1) >> (sizeof(long) == 8 ? 3 : 2)) + +- if (fp->exit_handler) { +- rh = rethook_try_get(fp->rethook); +- if (!rh) { +- fp->nmissed++; +- return; +- } +- fpr = container_of(rh, struct fprobe_rethook_node, node); +- fpr->entry_ip = ip; +- fpr->entry_parent_ip = parent_ip; +- if (fp->entry_data_size) +- entry_data = fpr->data; ++/* ++ * fprobe_table: hold 'fprobe_hlist::hlist' for checking the fprobe still ++ * exists. The key is the address of fprobe instance. ++ * fprobe_ip_table: hold 'fprobe_hlist::array[*]' for searching the fprobe ++ * instance related to the funciton address. The key is the ftrace IP ++ * address. ++ * ++ * When unregistering the fprobe, fprobe_hlist::fp and fprobe_hlist::array[*].fp ++ * are set NULL and delete those from both hash tables (by hlist_del_rcu). ++ * After an RCU grace period, the fprobe_hlist itself will be released. ++ * ++ * fprobe_table and fprobe_ip_table can be accessed from either ++ * - Normal hlist traversal and RCU add/del under 'fprobe_mutex' is held. ++ * - RCU hlist traversal under disabling preempt ++ */ ++static struct hlist_head fprobe_table[FPROBE_TABLE_SIZE]; ++static struct hlist_head fprobe_ip_table[FPROBE_IP_TABLE_SIZE]; ++static DEFINE_MUTEX(fprobe_mutex); ++ ++/* ++ * Find first fprobe in the hlist. It will be iterated twice in the entry ++ * probe, once for correcting the total required size, the second time is ++ * calling back the user handlers. ++ * Thus the hlist in the fprobe_table must be sorted and new probe needs to ++ * be added *before* the first fprobe. ++ */ ++static struct fprobe_hlist_node *find_first_fprobe_node(unsigned long ip) ++{ ++ struct fprobe_hlist_node *node; ++ struct hlist_head *head; ++ ++ head = &fprobe_ip_table[hash_ptr((void *)ip, FPROBE_IP_HASH_BITS)]; ++ hlist_for_each_entry_rcu(node, head, hlist, ++ lockdep_is_held(&fprobe_mutex)) { ++ if (node->addr == ip) ++ return node; + } ++ return NULL; ++} ++NOKPROBE_SYMBOL(find_first_fprobe_node); + +- if (fp->entry_handler) +- ret = fp->entry_handler(fp, ip, parent_ip, fregs, entry_data); ++/* Node insertion and deletion requires the fprobe_mutex */ ++static void insert_fprobe_node(struct fprobe_hlist_node *node) ++{ ++ unsigned long ip = node->addr; ++ struct fprobe_hlist_node *next; ++ struct hlist_head *head; + +- /* If entry_handler returns !0, nmissed is not counted. */ +- if (rh) { +- if (ret) +- rethook_recycle(rh); +- else +- rethook_hook(rh, ftrace_get_regs(fregs), true); ++ lockdep_assert_held(&fprobe_mutex); ++ ++ next = find_first_fprobe_node(ip); ++ if (next) { ++ hlist_add_before_rcu(&node->hlist, &next->hlist); ++ return; + } ++ head = &fprobe_ip_table[hash_ptr((void *)ip, FPROBE_IP_HASH_BITS)]; ++ hlist_add_head_rcu(&node->hlist, head); + } + +-static void fprobe_handler(unsigned long ip, unsigned long parent_ip, +- struct ftrace_ops *ops, struct ftrace_regs *fregs) ++/* Return true if there are synonims */ ++static bool delete_fprobe_node(struct fprobe_hlist_node *node) + { +- struct fprobe *fp; +- int bit; ++ lockdep_assert_held(&fprobe_mutex); + +- fp = container_of(ops, struct fprobe, ops); +- if (fprobe_disabled(fp)) +- return; ++ WRITE_ONCE(node->fp, NULL); ++ hlist_del_rcu(&node->hlist); ++ return !!find_first_fprobe_node(node->addr); ++} + +- /* recursion detection has to go before any traceable function and +- * all functions before this point should be marked as notrace +- */ +- bit = ftrace_test_recursion_trylock(ip, parent_ip); +- if (bit < 0) { +- fp->nmissed++; +- return; ++/* Check existence of the fprobe */ ++static bool is_fprobe_still_exist(struct fprobe *fp) ++{ ++ struct hlist_head *head; ++ struct fprobe_hlist *fph; ++ ++ head = &fprobe_table[hash_ptr(fp, FPROBE_HASH_BITS)]; ++ hlist_for_each_entry_rcu(fph, head, hlist, ++ lockdep_is_held(&fprobe_mutex)) { ++ if (fph->fp == fp) ++ return true; + } +- __fprobe_handler(ip, parent_ip, ops, fregs); +- ftrace_test_recursion_unlock(bit); ++ return false; ++} ++NOKPROBE_SYMBOL(is_fprobe_still_exist); ++ ++static int add_fprobe_hash(struct fprobe *fp) ++{ ++ struct fprobe_hlist *fph = fp->hlist_array; ++ struct hlist_head *head; ++ ++ lockdep_assert_held(&fprobe_mutex); ++ ++ if (WARN_ON_ONCE(!fph)) ++ return -EINVAL; ++ ++ if (is_fprobe_still_exist(fp)) ++ return -EEXIST; + ++ head = &fprobe_table[hash_ptr(fp, FPROBE_HASH_BITS)]; ++ hlist_add_head_rcu(&fp->hlist_array->hlist, head); ++ return 0; + } +-NOKPROBE_SYMBOL(fprobe_handler); + +-static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip, +- struct ftrace_ops *ops, struct ftrace_regs *fregs) ++static int del_fprobe_hash(struct fprobe *fp) + { ++ struct fprobe_hlist *fph = fp->hlist_array; ++ ++ lockdep_assert_held(&fprobe_mutex); ++ ++ if (WARN_ON_ONCE(!fph)) ++ return -EINVAL; ++ ++ if (!is_fprobe_still_exist(fp)) ++ return -ENOENT; ++ ++ fph->fp = NULL; ++ hlist_del_rcu(&fph->hlist); ++ return 0; ++} ++ ++/* Generic fprobe_header */ ++struct __fprobe_header { + struct fprobe *fp; +- int bit; ++ unsigned long size_words; ++} __packed; + +- fp = container_of(ops, struct fprobe, ops); +- if (fprobe_disabled(fp)) +- return; ++#define FPROBE_HEADER_SIZE_IN_LONG SIZE_IN_LONG(sizeof(struct __fprobe_header)) + +- /* recursion detection has to go before any traceable function and +- * all functions called before this point should be marked as notrace +- */ +- bit = ftrace_test_recursion_trylock(ip, parent_ip); +- if (bit < 0) { +- fp->nmissed++; +- return; +- } ++static inline bool write_fprobe_header(unsigned long *stack, ++ struct fprobe *fp, unsigned int size_words) ++{ ++ struct __fprobe_header *fph = (struct __fprobe_header *)stack; + ++ if (WARN_ON_ONCE(size_words > MAX_FPROBE_DATA_SIZE_WORD)) ++ return false; ++ ++ fph->fp = fp; ++ fph->size_words = size_words; ++ return true; ++} ++ ++static inline void read_fprobe_header(unsigned long *stack, ++ struct fprobe **fp, unsigned int *size_words) ++{ ++ struct __fprobe_header *fph = (struct __fprobe_header *)stack; ++ ++ *fp = fph->fp; ++ *size_words = fph->size_words; ++} ++ ++/* ++ * fprobe shadow stack management: ++ * Since fprobe shares a single fgraph_ops, it needs to share the stack entry ++ * among the probes on the same function exit. Note that a new probe can be ++ * registered before a target function is returning, we can not use the hash ++ * table to find the corresponding probes. Thus the probe address is stored on ++ * the shadow stack with its entry data size. ++ * ++ */ ++static inline int __fprobe_handler(unsigned long ip, unsigned long parent_ip, ++ struct fprobe *fp, struct ftrace_regs *fregs, ++ void *data) ++{ ++ if (!fp->entry_handler) ++ return 0; ++ ++ return fp->entry_handler(fp, ip, parent_ip, fregs, data); ++} ++ ++static inline int __fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip, ++ struct fprobe *fp, struct ftrace_regs *fregs, ++ void *data) ++{ ++ int ret; + /* + * This user handler is shared with other kprobes and is not expected to be + * called recursively. So if any other kprobe handler is running, this will +@@ -108,45 +205,183 @@ static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip, + */ + if (unlikely(kprobe_running())) { + fp->nmissed++; +- goto recursion_unlock; ++ return 0; + } + + kprobe_busy_begin(); +- __fprobe_handler(ip, parent_ip, ops, fregs); ++ ret = __fprobe_handler(ip, parent_ip, fp, fregs, data); + kprobe_busy_end(); +- +-recursion_unlock: +- ftrace_test_recursion_unlock(bit); ++ return ret; + } + +-static void fprobe_exit_handler(struct rethook_node *rh, void *data, +- unsigned long ret_ip, struct pt_regs *regs) ++static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops, ++ struct ftrace_regs *fregs) + { +- struct fprobe *fp = (struct fprobe *)data; +- struct fprobe_rethook_node *fpr; +- struct ftrace_regs *fregs = (struct ftrace_regs *)regs; +- int bit; ++ struct fprobe_hlist_node *node, *first; ++ unsigned long *fgraph_data = NULL; ++ unsigned long func = trace->func; ++ unsigned long ret_ip; ++ int reserved_words; ++ struct fprobe *fp; ++ int used, ret; + +- if (!fp || fprobe_disabled(fp)) +- return; ++ if (WARN_ON_ONCE(!fregs)) ++ return 0; + +- fpr = container_of(rh, struct fprobe_rethook_node, node); ++ first = node = find_first_fprobe_node(func); ++ if (unlikely(!first)) ++ return 0; ++ ++ reserved_words = 0; ++ hlist_for_each_entry_from_rcu(node, hlist) { ++ if (node->addr != func) ++ break; ++ fp = READ_ONCE(node->fp); ++ if (!fp || !fp->exit_handler) ++ continue; ++ /* ++ * Since fprobe can be enabled until the next loop, we ignore the ++ * fprobe's disabled flag in this loop. ++ */ ++ reserved_words += ++ FPROBE_HEADER_SIZE_IN_LONG + SIZE_IN_LONG(fp->entry_data_size); ++ } ++ node = first; ++ if (reserved_words) { ++ fgraph_data = fgraph_reserve_data(gops->idx, reserved_words * sizeof(long)); ++ if (unlikely(!fgraph_data)) { ++ hlist_for_each_entry_from_rcu(node, hlist) { ++ if (node->addr != func) ++ break; ++ fp = READ_ONCE(node->fp); ++ if (fp && !fprobe_disabled(fp)) ++ fp->nmissed++; ++ } ++ return 0; ++ } ++ } + + /* +- * we need to assure no calls to traceable functions in-between the +- * end of fprobe_handler and the beginning of fprobe_exit_handler. ++ * TODO: recursion detection has been done in the fgraph. Thus we need ++ * to add a callback to increment missed counter. + */ +- bit = ftrace_test_recursion_trylock(fpr->entry_ip, fpr->entry_parent_ip); +- if (bit < 0) { +- fp->nmissed++; ++ ret_ip = ftrace_regs_get_return_address(fregs); ++ used = 0; ++ hlist_for_each_entry_from_rcu(node, hlist) { ++ int data_size; ++ void *data; ++ ++ if (node->addr != func) ++ break; ++ fp = READ_ONCE(node->fp); ++ if (!fp || fprobe_disabled(fp)) ++ continue; ++ ++ data_size = fp->entry_data_size; ++ if (data_size && fp->exit_handler) ++ data = fgraph_data + used + FPROBE_HEADER_SIZE_IN_LONG; ++ else ++ data = NULL; ++ ++ if (fprobe_shared_with_kprobes(fp)) ++ ret = __fprobe_kprobe_handler(func, ret_ip, fp, fregs, data); ++ else ++ ret = __fprobe_handler(func, ret_ip, fp, fregs, data); ++ ++ /* If entry_handler returns !0, nmissed is not counted but skips exit_handler. */ ++ if (!ret && fp->exit_handler) { ++ int size_words = SIZE_IN_LONG(data_size); ++ ++ if (write_fprobe_header(&fgraph_data[used], fp, size_words)) ++ used += FPROBE_HEADER_SIZE_IN_LONG + size_words; ++ } ++ } ++ if (used < reserved_words) ++ memset(fgraph_data + used, 0, reserved_words - used); ++ ++ /* If any exit_handler is set, data must be used. */ ++ return used != 0; ++} ++NOKPROBE_SYMBOL(fprobe_entry); ++ ++static void fprobe_return(struct ftrace_graph_ret *trace, ++ struct fgraph_ops *gops, ++ struct ftrace_regs *fregs) ++{ ++ unsigned long *fgraph_data = NULL; ++ unsigned long ret_ip; ++ struct fprobe *fp; ++ int size, curr; ++ int size_words; ++ ++ fgraph_data = (unsigned long *)fgraph_retrieve_data(gops->idx, &size); ++ if (WARN_ON_ONCE(!fgraph_data)) + return; ++ size_words = SIZE_IN_LONG(size); ++ ret_ip = ftrace_regs_get_instruction_pointer(fregs); ++ ++ preempt_disable(); ++ ++ curr = 0; ++ while (size_words > curr) { ++ read_fprobe_header(&fgraph_data[curr], &fp, &size); ++ if (!fp) ++ break; ++ curr += FPROBE_HEADER_SIZE_IN_LONG; ++ if (is_fprobe_still_exist(fp) && !fprobe_disabled(fp)) { ++ if (WARN_ON_ONCE(curr + size > size_words)) ++ break; ++ fp->exit_handler(fp, trace->func, ret_ip, fregs, ++ size ? fgraph_data + curr : NULL); ++ } ++ curr += size; + } ++ preempt_enable(); ++} ++NOKPROBE_SYMBOL(fprobe_return); ++ ++static struct fgraph_ops fprobe_graph_ops = { ++ .entryfunc = fprobe_entry, ++ .retfunc = fprobe_return, ++}; ++static int fprobe_graph_active; ++ ++/* Add @addrs to the ftrace filter and register fgraph if needed. */ ++static int fprobe_graph_add_ips(unsigned long *addrs, int num) ++{ ++ int ret; + +- fp->exit_handler(fp, fpr->entry_ip, ret_ip, fregs, +- fp->entry_data_size ? (void *)fpr->data : NULL); +- ftrace_test_recursion_unlock(bit); ++ lockdep_assert_held(&fprobe_mutex); ++ ++ ret = ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 0, 0); ++ if (ret) ++ return ret; ++ ++ if (!fprobe_graph_active) { ++ ret = register_ftrace_graph(&fprobe_graph_ops); ++ if (WARN_ON_ONCE(ret)) { ++ ftrace_free_filter(&fprobe_graph_ops.ops); ++ return ret; ++ } ++ } ++ fprobe_graph_active++; ++ return 0; ++} ++ ++/* Remove @addrs from the ftrace filter and unregister fgraph if possible. */ ++static void fprobe_graph_remove_ips(unsigned long *addrs, int num) ++{ ++ lockdep_assert_held(&fprobe_mutex); ++ ++ fprobe_graph_active--; ++ if (!fprobe_graph_active) { ++ /* Q: should we unregister it ? */ ++ unregister_ftrace_graph(&fprobe_graph_ops); ++ return; ++ } ++ ++ ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 1, 0); + } +-NOKPROBE_SYMBOL(fprobe_exit_handler); + + static int symbols_cmp(const void *a, const void *b) + { +@@ -176,54 +411,97 @@ static unsigned long *get_ftrace_locations(const char **syms, int num) + return ERR_PTR(-ENOENT); + } + +-static void fprobe_init(struct fprobe *fp) +-{ +- fp->nmissed = 0; +- if (fprobe_shared_with_kprobes(fp)) +- fp->ops.func = fprobe_kprobe_handler; +- else +- fp->ops.func = fprobe_handler; +- +- fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS; +-} ++struct filter_match_data { ++ const char *filter; ++ const char *notfilter; ++ size_t index; ++ size_t size; ++ unsigned long *addrs; ++}; + +-static int fprobe_init_rethook(struct fprobe *fp, int num) ++static int filter_match_callback(void *data, const char *name, unsigned long addr) + { +- int size; ++ struct filter_match_data *match = data; + +- if (!fp->exit_handler) { +- fp->rethook = NULL; ++ if (!glob_match(match->filter, name) || ++ (match->notfilter && glob_match(match->notfilter, name))) + return 0; +- } + +- /* Initialize rethook if needed */ +- if (fp->nr_maxactive) +- num = fp->nr_maxactive; +- else +- num *= num_possible_cpus() * 2; +- if (num <= 0) +- return -EINVAL; ++ if (!ftrace_location(addr)) ++ return 0; + +- size = sizeof(struct fprobe_rethook_node) + fp->entry_data_size; ++ if (match->addrs) ++ match->addrs[match->index] = addr; + +- /* Initialize rethook */ +- fp->rethook = rethook_alloc((void *)fp, fprobe_exit_handler, size, num); +- if (IS_ERR(fp->rethook)) +- return PTR_ERR(fp->rethook); ++ match->index++; ++ return match->index == match->size; ++} + +- return 0; ++/* ++ * Make IP list from the filter/no-filter glob patterns. ++ * Return the number of matched symbols, or -ENOENT. ++ */ ++static int ip_list_from_filter(const char *filter, const char *notfilter, ++ unsigned long *addrs, size_t size) ++{ ++ struct filter_match_data match = { .filter = filter, .notfilter = notfilter, ++ .index = 0, .size = size, .addrs = addrs}; ++ int ret; ++ ++ ret = kallsyms_on_each_symbol(filter_match_callback, &match); ++ if (ret < 0) ++ return ret; ++ ret = module_kallsyms_on_each_symbol(NULL, filter_match_callback, &match); ++ if (ret < 0) ++ return ret; ++ ++ return match.index ?: -ENOENT; + } + + static void fprobe_fail_cleanup(struct fprobe *fp) + { +- if (!IS_ERR_OR_NULL(fp->rethook)) { +- /* Don't need to cleanup rethook->handler because this is not used. */ +- rethook_free(fp->rethook); +- fp->rethook = NULL; ++ kfree(fp->hlist_array); ++ fp->hlist_array = NULL; ++} ++ ++/* Initialize the fprobe data structure. */ ++static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num) ++{ ++ struct fprobe_hlist *hlist_array; ++ unsigned long addr; ++ int size, i; ++ ++ if (!fp || !addrs || num <= 0) ++ return -EINVAL; ++ ++ size = ALIGN(fp->entry_data_size, sizeof(long)); ++ if (size > MAX_FPROBE_DATA_SIZE) ++ return -E2BIG; ++ fp->entry_data_size = size; ++ ++ hlist_array = kzalloc(struct_size(hlist_array, array, num), GFP_KERNEL); ++ if (!hlist_array) ++ return -ENOMEM; ++ ++ fp->nmissed = 0; ++ ++ hlist_array->size = num; ++ fp->hlist_array = hlist_array; ++ hlist_array->fp = fp; ++ for (i = 0; i < num; i++) { ++ hlist_array->array[i].fp = fp; ++ addr = ftrace_location(addrs[i]); ++ if (!addr) { ++ fprobe_fail_cleanup(fp); ++ return -ENOENT; ++ } ++ hlist_array->array[i].addr = addr; + } +- ftrace_free_filter(&fp->ops); ++ return 0; + } + ++#define FPROBE_IPS_MAX INT_MAX ++ + /** + * register_fprobe() - Register fprobe to ftrace by pattern. + * @fp: A fprobe data structure to be registered. +@@ -237,46 +515,24 @@ static void fprobe_fail_cleanup(struct fprobe *fp) + */ + int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter) + { +- struct ftrace_hash *hash; +- unsigned char *str; +- int ret, len; ++ unsigned long *addrs; ++ int ret; + + if (!fp || !filter) + return -EINVAL; + +- fprobe_init(fp); +- +- len = strlen(filter); +- str = kstrdup(filter, GFP_KERNEL); +- ret = ftrace_set_filter(&fp->ops, str, len, 0); +- kfree(str); +- if (ret) ++ ret = ip_list_from_filter(filter, notfilter, NULL, FPROBE_IPS_MAX); ++ if (ret < 0) + return ret; + +- if (notfilter) { +- len = strlen(notfilter); +- str = kstrdup(notfilter, GFP_KERNEL); +- ret = ftrace_set_notrace(&fp->ops, str, len, 0); +- kfree(str); +- if (ret) +- goto out; +- } +- +- /* TODO: +- * correctly calculate the total number of filtered symbols +- * from both filter and notfilter. +- */ +- hash = rcu_access_pointer(fp->ops.local_hash.filter_hash); +- if (WARN_ON_ONCE(!hash)) +- goto out; +- +- ret = fprobe_init_rethook(fp, (int)hash->count); +- if (!ret) +- ret = register_ftrace_function(&fp->ops); ++ addrs = kcalloc(ret, sizeof(unsigned long), GFP_KERNEL); ++ if (!addrs) ++ return -ENOMEM; ++ ret = ip_list_from_filter(filter, notfilter, addrs, ret); ++ if (ret > 0) ++ ret = register_fprobe_ips(fp, addrs, ret); + +-out: +- if (ret) +- fprobe_fail_cleanup(fp); ++ kfree(addrs); + return ret; + } + EXPORT_SYMBOL_GPL(register_fprobe); +@@ -284,7 +540,7 @@ EXPORT_SYMBOL_GPL(register_fprobe); + /** + * register_fprobe_ips() - Register fprobe to ftrace by address. + * @fp: A fprobe data structure to be registered. +- * @addrs: An array of target ftrace location addresses. ++ * @addrs: An array of target function address. + * @num: The number of entries of @addrs. + * + * Register @fp to ftrace for enabling the probe on the address given by @addrs. +@@ -296,23 +552,27 @@ EXPORT_SYMBOL_GPL(register_fprobe); + */ + int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num) + { +- int ret; +- +- if (!fp || !addrs || num <= 0) +- return -EINVAL; ++ struct fprobe_hlist *hlist_array; ++ int ret, i; + +- fprobe_init(fp); +- +- ret = ftrace_set_filter_ips(&fp->ops, addrs, num, 0, 0); ++ ret = fprobe_init(fp, addrs, num); + if (ret) + return ret; + +- ret = fprobe_init_rethook(fp, num); +- if (!ret) +- ret = register_ftrace_function(&fp->ops); ++ mutex_lock(&fprobe_mutex); ++ ++ hlist_array = fp->hlist_array; ++ ret = fprobe_graph_add_ips(addrs, num); ++ if (!ret) { ++ add_fprobe_hash(fp); ++ for (i = 0; i < hlist_array->size; i++) ++ insert_fprobe_node(&hlist_array->array[i]); ++ } ++ mutex_unlock(&fprobe_mutex); + + if (ret) + fprobe_fail_cleanup(fp); ++ + return ret; + } + EXPORT_SYMBOL_GPL(register_fprobe_ips); +@@ -350,14 +610,13 @@ EXPORT_SYMBOL_GPL(register_fprobe_syms); + + bool fprobe_is_registered(struct fprobe *fp) + { +- if (!fp || (fp->ops.saved_func != fprobe_handler && +- fp->ops.saved_func != fprobe_kprobe_handler)) ++ if (!fp || !fp->hlist_array) + return false; + return true; + } + + /** +- * unregister_fprobe() - Unregister fprobe from ftrace ++ * unregister_fprobe() - Unregister fprobe. + * @fp: A fprobe data structure to be unregistered. + * + * Unregister fprobe (and remove ftrace hooks from the function entries). +@@ -366,23 +625,41 @@ bool fprobe_is_registered(struct fprobe *fp) + */ + int unregister_fprobe(struct fprobe *fp) + { +- int ret; ++ struct fprobe_hlist *hlist_array; ++ unsigned long *addrs = NULL; ++ int ret = 0, i, count; + +- if (!fprobe_is_registered(fp)) +- return -EINVAL; ++ mutex_lock(&fprobe_mutex); ++ if (!fp || !is_fprobe_still_exist(fp)) { ++ ret = -EINVAL; ++ goto out; ++ } + +- if (!IS_ERR_OR_NULL(fp->rethook)) +- rethook_stop(fp->rethook); ++ hlist_array = fp->hlist_array; ++ addrs = kcalloc(hlist_array->size, sizeof(unsigned long), GFP_KERNEL); ++ if (!addrs) { ++ ret = -ENOMEM; /* TODO: Fallback to one-by-one loop */ ++ goto out; ++ } + +- ret = unregister_ftrace_function(&fp->ops); +- if (ret < 0) +- return ret; ++ /* Remove non-synonim ips from table and hash */ ++ count = 0; ++ for (i = 0; i < hlist_array->size; i++) { ++ if (!delete_fprobe_node(&hlist_array->array[i])) ++ addrs[count++] = hlist_array->array[i].addr; ++ } ++ del_fprobe_hash(fp); + +- if (!IS_ERR_OR_NULL(fp->rethook)) +- rethook_free(fp->rethook); ++ if (count) ++ fprobe_graph_remove_ips(addrs, count); + +- ftrace_free_filter(&fp->ops); ++ kfree_rcu(hlist_array, rcu); ++ fp->hlist_array = NULL; + ++out: ++ mutex_unlock(&fprobe_mutex); ++ ++ kfree(addrs); + return ret; + } + EXPORT_SYMBOL_GPL(unregister_fprobe); +diff --git a/lib/test_fprobe.c b/lib/test_fprobe.c +index 271ce0caeec03..cf92111b5c79d 100644 +--- a/lib/test_fprobe.c ++++ b/lib/test_fprobe.c +@@ -17,10 +17,8 @@ static u32 rand1, entry_val, exit_val; + /* Use indirect calls to avoid inlining the target functions */ + static u32 (*target)(u32 value); + static u32 (*target2)(u32 value); +-static u32 (*target_nest)(u32 value, u32 (*nest)(u32)); + static unsigned long target_ip; + static unsigned long target2_ip; +-static unsigned long target_nest_ip; + static int entry_return_value; + + static noinline u32 fprobe_selftest_target(u32 value) +@@ -33,11 +31,6 @@ static noinline u32 fprobe_selftest_target2(u32 value) + return (value / div_factor) + 1; + } + +-static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32)) +-{ +- return nest(value + 2); +-} +- + static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip, + unsigned long ret_ip, + struct ftrace_regs *fregs, void *data) +@@ -79,22 +72,6 @@ static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, + KUNIT_EXPECT_NULL(current_test, data); + } + +-static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip, +- unsigned long ret_ip, +- struct ftrace_regs *fregs, void *data) +-{ +- KUNIT_EXPECT_FALSE(current_test, preemptible()); +- return 0; +-} +- +-static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip, +- unsigned long ret_ip, +- struct ftrace_regs *fregs, void *data) +-{ +- KUNIT_EXPECT_FALSE(current_test, preemptible()); +- KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip); +-} +- + /* Test entry only (no rethook) */ + static void test_fprobe_entry(struct kunit *test) + { +@@ -191,25 +168,6 @@ static void test_fprobe_data(struct kunit *test) + KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); + } + +-/* Test nr_maxactive */ +-static void test_fprobe_nest(struct kunit *test) +-{ +- static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_nest_target"}; +- struct fprobe fp = { +- .entry_handler = nest_entry_handler, +- .exit_handler = nest_exit_handler, +- .nr_maxactive = 1, +- }; +- +- current_test = test; +- KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2)); +- +- target_nest(rand1, target); +- KUNIT_EXPECT_EQ(test, 1, fp.nmissed); +- +- KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); +-} +- + static void test_fprobe_skip(struct kunit *test) + { + struct fprobe fp = { +@@ -247,10 +205,8 @@ static int fprobe_test_init(struct kunit *test) + rand1 = get_random_u32_above(div_factor); + target = fprobe_selftest_target; + target2 = fprobe_selftest_target2; +- target_nest = fprobe_selftest_nest_target; + target_ip = get_ftrace_location(target); + target2_ip = get_ftrace_location(target2); +- target_nest_ip = get_ftrace_location(target_nest); + + return 0; + } +@@ -260,7 +216,6 @@ static struct kunit_case fprobe_testcases[] = { + KUNIT_CASE(test_fprobe), + KUNIT_CASE(test_fprobe_syms), + KUNIT_CASE(test_fprobe_data), +- KUNIT_CASE(test_fprobe_nest), + KUNIT_CASE(test_fprobe_skip), + {} + }; +-- +2.39.5 + diff --git a/queue-6.13/fprobe-use-ftrace_regs-in-fprobe-entry-handler.patch b/queue-6.13/fprobe-use-ftrace_regs-in-fprobe-entry-handler.patch new file mode 100644 index 0000000000..0c7d70ec21 --- /dev/null +++ b/queue-6.13/fprobe-use-ftrace_regs-in-fprobe-entry-handler.patch @@ -0,0 +1,207 @@ +From 96eedf4d4d07660c69478c2a7c41d8ceee750de6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Dec 2024 14:12:20 +0900 +Subject: fprobe: Use ftrace_regs in fprobe entry handler + +From: Masami Hiramatsu (Google) + +[ Upstream commit 46bc082388560a95e3649b698a4675e5ea3262e6 ] + +This allows fprobes to be available with CONFIG_DYNAMIC_FTRACE_WITH_ARGS +instead of CONFIG_DYNAMIC_FTRACE_WITH_REGS, then we can enable fprobe +on arm64. + +Cc: Alexei Starovoitov +Cc: Martin KaFai Lau +Cc: bpf +Cc: Alexei Starovoitov +Cc: Jiri Olsa +Cc: Alan Maguire +Cc: Mark Rutland +Link: https://lore.kernel.org/173518994037.391279.2786805566359674586.stgit@devnote2 +Signed-off-by: Masami Hiramatsu (Google) +Acked-by: Florent Revest +Signed-off-by: Steven Rostedt (Google) +Stable-dep-of: db5e228611b1 ("tracing: fprobe-events: Log error for exceeding the number of entry args") +Signed-off-by: Sasha Levin +--- + include/linux/fprobe.h | 2 +- + kernel/trace/Kconfig | 3 ++- + kernel/trace/bpf_trace.c | 10 +++++++--- + kernel/trace/fprobe.c | 3 ++- + kernel/trace/trace_fprobe.c | 11 ++++++++--- + lib/test_fprobe.c | 4 ++-- + samples/fprobe/fprobe_example.c | 2 +- + 7 files changed, 23 insertions(+), 12 deletions(-) + +diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h +index f398695881175..ca64ee5e45d2c 100644 +--- a/include/linux/fprobe.h ++++ b/include/linux/fprobe.h +@@ -10,7 +10,7 @@ + struct fprobe; + + typedef int (*fprobe_entry_cb)(struct fprobe *fp, unsigned long entry_ip, +- unsigned long ret_ip, struct pt_regs *regs, ++ unsigned long ret_ip, struct ftrace_regs *regs, + void *entry_data); + + typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip, +diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig +index c5ab2a561272d..f10ca86fbfad2 100644 +--- a/kernel/trace/Kconfig ++++ b/kernel/trace/Kconfig +@@ -297,7 +297,7 @@ config DYNAMIC_FTRACE_WITH_ARGS + config FPROBE + bool "Kernel Function Probe (fprobe)" + depends on FUNCTION_TRACER +- depends on DYNAMIC_FTRACE_WITH_REGS ++ depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS + depends on HAVE_RETHOOK + select RETHOOK + default n +@@ -682,6 +682,7 @@ config FPROBE_EVENTS + select TRACING + select PROBE_EVENTS + select DYNAMIC_EVENTS ++ depends on DYNAMIC_FTRACE_WITH_REGS + default y + help + This allows user to add tracing events on the function entry and +diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c +index 2c2205e91fee9..6b58e84995e46 100644 +--- a/kernel/trace/bpf_trace.c ++++ b/kernel/trace/bpf_trace.c +@@ -2562,7 +2562,7 @@ struct bpf_session_run_ctx { + void *data; + }; + +-#ifdef CONFIG_FPROBE ++#if defined(CONFIG_FPROBE) && defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS) + struct bpf_kprobe_multi_link { + struct bpf_link link; + struct fprobe fp; +@@ -2814,12 +2814,16 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link, + + static int + kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip, +- unsigned long ret_ip, struct pt_regs *regs, ++ unsigned long ret_ip, struct ftrace_regs *fregs, + void *data) + { ++ struct pt_regs *regs = ftrace_get_regs(fregs); + struct bpf_kprobe_multi_link *link; + int err; + ++ if (!regs) ++ return 0; ++ + link = container_of(fp, struct bpf_kprobe_multi_link, fp); + err = kprobe_multi_link_prog_run(link, get_entry_ip(fentry_ip), regs, false, data); + return is_kprobe_session(link->link.prog) ? err : 0; +@@ -3094,7 +3098,7 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr + kvfree(cookies); + return err; + } +-#else /* !CONFIG_FPROBE */ ++#else /* !CONFIG_FPROBE || !CONFIG_DYNAMIC_FTRACE_WITH_REGS */ + int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) + { + return -EOPNOTSUPP; +diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c +index 9ff0182458408..3d37892838739 100644 +--- a/kernel/trace/fprobe.c ++++ b/kernel/trace/fprobe.c +@@ -46,7 +46,7 @@ static inline void __fprobe_handler(unsigned long ip, unsigned long parent_ip, + } + + if (fp->entry_handler) +- ret = fp->entry_handler(fp, ip, parent_ip, ftrace_get_regs(fregs), entry_data); ++ ret = fp->entry_handler(fp, ip, parent_ip, fregs, entry_data); + + /* If entry_handler returns !0, nmissed is not counted. */ + if (rh) { +@@ -182,6 +182,7 @@ static void fprobe_init(struct fprobe *fp) + fp->ops.func = fprobe_kprobe_handler; + else + fp->ops.func = fprobe_handler; ++ + fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS; + } + +diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c +index 99048c3303822..c1eef70212b25 100644 +--- a/kernel/trace/trace_fprobe.c ++++ b/kernel/trace/trace_fprobe.c +@@ -217,12 +217,13 @@ NOKPROBE_SYMBOL(fentry_trace_func); + + /* function exit handler */ + static int trace_fprobe_entry_handler(struct fprobe *fp, unsigned long entry_ip, +- unsigned long ret_ip, struct pt_regs *regs, ++ unsigned long ret_ip, struct ftrace_regs *fregs, + void *entry_data) + { + struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp); ++ struct pt_regs *regs = ftrace_get_regs(fregs); + +- if (tf->tp.entry_arg) ++ if (regs && tf->tp.entry_arg) + store_trace_entry_data(entry_data, &tf->tp, regs); + + return 0; +@@ -339,12 +340,16 @@ NOKPROBE_SYMBOL(fexit_perf_func); + #endif /* CONFIG_PERF_EVENTS */ + + static int fentry_dispatcher(struct fprobe *fp, unsigned long entry_ip, +- unsigned long ret_ip, struct pt_regs *regs, ++ unsigned long ret_ip, struct ftrace_regs *fregs, + void *entry_data) + { + struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp); ++ struct pt_regs *regs = ftrace_get_regs(fregs); + int ret = 0; + ++ if (!regs) ++ return 0; ++ + if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE)) + fentry_trace_func(tf, entry_ip, regs); + #ifdef CONFIG_PERF_EVENTS +diff --git a/lib/test_fprobe.c b/lib/test_fprobe.c +index 24de0e5ff8599..ff607babba189 100644 +--- a/lib/test_fprobe.c ++++ b/lib/test_fprobe.c +@@ -40,7 +40,7 @@ static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32)) + + static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip, + unsigned long ret_ip, +- struct pt_regs *regs, void *data) ++ struct ftrace_regs *fregs, void *data) + { + KUNIT_EXPECT_FALSE(current_test, preemptible()); + /* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */ +@@ -81,7 +81,7 @@ static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, + + static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip, + unsigned long ret_ip, +- struct pt_regs *regs, void *data) ++ struct ftrace_regs *fregs, void *data) + { + KUNIT_EXPECT_FALSE(current_test, preemptible()); + return 0; +diff --git a/samples/fprobe/fprobe_example.c b/samples/fprobe/fprobe_example.c +index 0a50b05add969..c234afae52d6f 100644 +--- a/samples/fprobe/fprobe_example.c ++++ b/samples/fprobe/fprobe_example.c +@@ -50,7 +50,7 @@ static void show_backtrace(void) + + static int sample_entry_handler(struct fprobe *fp, unsigned long ip, + unsigned long ret_ip, +- struct pt_regs *regs, void *data) ++ struct ftrace_regs *fregs, void *data) + { + if (use_trace) + /* +-- +2.39.5 + diff --git a/queue-6.13/fprobe-use-ftrace_regs-in-fprobe-exit-handler.patch b/queue-6.13/fprobe-use-ftrace_regs-in-fprobe-exit-handler.patch new file mode 100644 index 0000000000..5e3632eacd --- /dev/null +++ b/queue-6.13/fprobe-use-ftrace_regs-in-fprobe-exit-handler.patch @@ -0,0 +1,261 @@ +From 9ec816522c378200971176807181e023885863af Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Dec 2024 14:12:31 +0900 +Subject: fprobe: Use ftrace_regs in fprobe exit handler + +From: Masami Hiramatsu (Google) + +[ Upstream commit 762abbc0d09f7ae123c82d315eb1a961c1a2cf7b ] + +Change the fprobe exit handler to use ftrace_regs structure instead of +pt_regs. This also introduce HAVE_FTRACE_REGS_HAVING_PT_REGS which +means the ftrace_regs is including the pt_regs so that ftrace_regs +can provide pt_regs without memory allocation. +Fprobe introduces a new dependency with that. + +Signed-off-by: Masami Hiramatsu (Google) +Acked-by: Heiko Carstens # s390 +Cc: Huacai Chen +Cc: Alexei Starovoitov +Cc: Florent Revest +Cc: bpf +Cc: Alan Maguire +Cc: Heiko Carstens +Cc: WANG Xuerui +Cc: Vasily Gorbik +Cc: Alexander Gordeev +Cc: Christian Borntraeger +Cc: Sven Schnelle +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: Borislav Petkov +Cc: Dave Hansen +Cc: x86@kernel.org +Cc: "H. Peter Anvin" +Cc: Mark Rutland +Cc: Mathieu Desnoyers +Cc: Song Liu +Cc: Jiri Olsa +Cc: KP Singh +Cc: Matt Bobrowski +Cc: Alexei Starovoitov +Cc: Daniel Borkmann +Cc: Andrii Nakryiko +Cc: Martin KaFai Lau +Cc: Eduard Zingerman +Cc: Yonghong Song +Cc: John Fastabend +Cc: Stanislav Fomichev +Cc: Hao Luo +Cc: Andrew Morton +Link: https://lore.kernel.org/173518995092.391279.6765116450352977627.stgit@devnote2 +Signed-off-by: Steven Rostedt (Google) +Stable-dep-of: db5e228611b1 ("tracing: fprobe-events: Log error for exceeding the number of entry args") +Signed-off-by: Sasha Levin +--- + arch/loongarch/Kconfig | 1 + + arch/s390/Kconfig | 1 + + arch/x86/Kconfig | 1 + + include/linux/fprobe.h | 2 +- + include/linux/ftrace.h | 6 ++++++ + kernel/trace/Kconfig | 7 +++++++ + kernel/trace/bpf_trace.c | 6 +++++- + kernel/trace/fprobe.c | 3 ++- + kernel/trace/trace_fprobe.c | 6 +++++- + lib/test_fprobe.c | 6 +++--- + samples/fprobe/fprobe_example.c | 2 +- + 11 files changed, 33 insertions(+), 8 deletions(-) + +diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig +index 49f5bfc00e5a1..6396615ec035e 100644 +--- a/arch/loongarch/Kconfig ++++ b/arch/loongarch/Kconfig +@@ -128,6 +128,7 @@ config LOONGARCH + select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE + select HAVE_DYNAMIC_FTRACE_WITH_ARGS ++ select HAVE_FTRACE_REGS_HAVING_PT_REGS + select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + select HAVE_DYNAMIC_FTRACE_WITH_REGS + select HAVE_EBPF_JIT +diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig +index a8cecae74fe81..c7a7f91a6ce19 100644 +--- a/arch/s390/Kconfig ++++ b/arch/s390/Kconfig +@@ -184,6 +184,7 @@ config S390 + select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE + select HAVE_DYNAMIC_FTRACE_WITH_ARGS ++ select HAVE_FTRACE_REGS_HAVING_PT_REGS + select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + select HAVE_DYNAMIC_FTRACE_WITH_REGS + select HAVE_EBPF_JIT if HAVE_MARCH_Z196_FEATURES +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index aa317f6064b89..9b20a96651671 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -224,6 +224,7 @@ config X86 + select HAVE_DYNAMIC_FTRACE + select HAVE_DYNAMIC_FTRACE_WITH_REGS + select HAVE_DYNAMIC_FTRACE_WITH_ARGS if X86_64 ++ select HAVE_FTRACE_REGS_HAVING_PT_REGS if X86_64 + select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + select HAVE_SAMPLE_FTRACE_DIRECT if X86_64 + select HAVE_SAMPLE_FTRACE_DIRECT_MULTI if X86_64 +diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h +index ca64ee5e45d2c..ef609bcca0f92 100644 +--- a/include/linux/fprobe.h ++++ b/include/linux/fprobe.h +@@ -14,7 +14,7 @@ typedef int (*fprobe_entry_cb)(struct fprobe *fp, unsigned long entry_ip, + void *entry_data); + + typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip, +- unsigned long ret_ip, struct pt_regs *regs, ++ unsigned long ret_ip, struct ftrace_regs *regs, + void *entry_data); + + /** +diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h +index b7407004c799e..46ac44366c90a 100644 +--- a/include/linux/ftrace.h ++++ b/include/linux/ftrace.h +@@ -176,6 +176,12 @@ static inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *fregs) + #define ftrace_regs_set_instruction_pointer(fregs, ip) do { } while (0) + #endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ + ++#ifdef CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS ++ ++static_assert(sizeof(struct pt_regs) == ftrace_regs_size()); ++ ++#endif /* CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS */ ++ + static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs) + { + if (!fregs) +diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig +index f10ca86fbfad2..7f8165f2049a5 100644 +--- a/kernel/trace/Kconfig ++++ b/kernel/trace/Kconfig +@@ -57,6 +57,12 @@ config HAVE_DYNAMIC_FTRACE_WITH_ARGS + This allows for use of ftrace_regs_get_argument() and + ftrace_regs_get_stack_pointer(). + ++config HAVE_FTRACE_REGS_HAVING_PT_REGS ++ bool ++ help ++ If this is set, ftrace_regs has pt_regs, thus it can convert to ++ pt_regs without allocating memory. ++ + config HAVE_DYNAMIC_FTRACE_NO_PATCHABLE + bool + help +@@ -298,6 +304,7 @@ config FPROBE + bool "Kernel Function Probe (fprobe)" + depends on FUNCTION_TRACER + depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS ++ depends on HAVE_FTRACE_REGS_HAVING_PT_REGS || !HAVE_DYNAMIC_FTRACE_WITH_ARGS + depends on HAVE_RETHOOK + select RETHOOK + default n +diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c +index 6b58e84995e46..968520b04b6d3 100644 +--- a/kernel/trace/bpf_trace.c ++++ b/kernel/trace/bpf_trace.c +@@ -2831,10 +2831,14 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip, + + static void + kprobe_multi_link_exit_handler(struct fprobe *fp, unsigned long fentry_ip, +- unsigned long ret_ip, struct pt_regs *regs, ++ unsigned long ret_ip, struct ftrace_regs *fregs, + void *data) + { + struct bpf_kprobe_multi_link *link; ++ struct pt_regs *regs = ftrace_get_regs(fregs); ++ ++ if (!regs) ++ return; + + link = container_of(fp, struct bpf_kprobe_multi_link, fp); + kprobe_multi_link_prog_run(link, get_entry_ip(fentry_ip), regs, true, data); +diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c +index 3d37892838739..90a3c8e2bbdf1 100644 +--- a/kernel/trace/fprobe.c ++++ b/kernel/trace/fprobe.c +@@ -124,6 +124,7 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data, + { + struct fprobe *fp = (struct fprobe *)data; + struct fprobe_rethook_node *fpr; ++ struct ftrace_regs *fregs = (struct ftrace_regs *)regs; + int bit; + + if (!fp || fprobe_disabled(fp)) +@@ -141,7 +142,7 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data, + return; + } + +- fp->exit_handler(fp, fpr->entry_ip, ret_ip, regs, ++ fp->exit_handler(fp, fpr->entry_ip, ret_ip, fregs, + fp->entry_data_size ? (void *)fpr->data : NULL); + ftrace_test_recursion_unlock(bit); + } +diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c +index c1eef70212b25..d906baba2d40c 100644 +--- a/kernel/trace/trace_fprobe.c ++++ b/kernel/trace/trace_fprobe.c +@@ -361,10 +361,14 @@ static int fentry_dispatcher(struct fprobe *fp, unsigned long entry_ip, + NOKPROBE_SYMBOL(fentry_dispatcher); + + static void fexit_dispatcher(struct fprobe *fp, unsigned long entry_ip, +- unsigned long ret_ip, struct pt_regs *regs, ++ unsigned long ret_ip, struct ftrace_regs *fregs, + void *entry_data) + { + struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp); ++ struct pt_regs *regs = ftrace_get_regs(fregs); ++ ++ if (!regs) ++ return; + + if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE)) + fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data); +diff --git a/lib/test_fprobe.c b/lib/test_fprobe.c +index ff607babba189..271ce0caeec03 100644 +--- a/lib/test_fprobe.c ++++ b/lib/test_fprobe.c +@@ -59,9 +59,9 @@ static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip, + + static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, + unsigned long ret_ip, +- struct pt_regs *regs, void *data) ++ struct ftrace_regs *fregs, void *data) + { +- unsigned long ret = regs_return_value(regs); ++ unsigned long ret = ftrace_regs_get_return_value(fregs); + + KUNIT_EXPECT_FALSE(current_test, preemptible()); + if (ip != target_ip) { +@@ -89,7 +89,7 @@ static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip, + + static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip, + unsigned long ret_ip, +- struct pt_regs *regs, void *data) ++ struct ftrace_regs *fregs, void *data) + { + KUNIT_EXPECT_FALSE(current_test, preemptible()); + KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip); +diff --git a/samples/fprobe/fprobe_example.c b/samples/fprobe/fprobe_example.c +index c234afae52d6f..bfe98ce826f3a 100644 +--- a/samples/fprobe/fprobe_example.c ++++ b/samples/fprobe/fprobe_example.c +@@ -67,7 +67,7 @@ static int sample_entry_handler(struct fprobe *fp, unsigned long ip, + } + + static void sample_exit_handler(struct fprobe *fp, unsigned long ip, +- unsigned long ret_ip, struct pt_regs *regs, ++ unsigned long ret_ip, struct ftrace_regs *regs, + void *data) + { + unsigned long rip = ret_ip; +-- +2.39.5 + diff --git a/queue-6.13/fs-pipe-do-not-open-code-pipe-head-tail-logic-in-fio.patch b/queue-6.13/fs-pipe-do-not-open-code-pipe-head-tail-logic-in-fio.patch new file mode 100644 index 0000000000..4440670b6c --- /dev/null +++ b/queue-6.13/fs-pipe-do-not-open-code-pipe-head-tail-logic-in-fio.patch @@ -0,0 +1,63 @@ +From cff7aa72cf4bb6ff5928f8534d705827ec42b140 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +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 +Cc: Mateusz Guzik +Cc: K Prateek Nayak +Cc: Swapnil Sapkal +Signed-off-by: Linus Torvalds +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/fs-pipe-fix-pipe-buffer-index-use-in-fuse.patch b/queue-6.13/fs-pipe-fix-pipe-buffer-index-use-in-fuse.patch new file mode 100644 index 0000000000..d67dbe3d45 --- /dev/null +++ b/queue-6.13/fs-pipe-fix-pipe-buffer-index-use-in-fuse.patch @@ -0,0 +1,78 @@ +From 097ef18912b9dcbefd6ee19bb430f8f2f3832fde Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Mar 2025 07:53:25 -1000 +Subject: fs/pipe: fix pipe buffer index use in FUSE + +From: Linus Torvalds + +[ 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 +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 +Cc: K Prateek Nayak +Cc: Swapnil Sapkal +Signed-off-by: Linus Torvalds +Signed-off-by: Sasha Levin +--- + 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 9ce6d1c6cac15..b8fb5da2565ed 100644 +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -2097,7 +2097,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; +@@ -2114,8 +2114,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) { +@@ -2125,8 +2124,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) +@@ -2137,10 +2136,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 + diff --git a/queue-6.13/fs-pipe-fix-pipe_occupancy-with-16-bit-indexes.patch b/queue-6.13/fs-pipe-fix-pipe_occupancy-with-16-bit-indexes.patch new file mode 100644 index 0000000000..a9ffd7885c --- /dev/null +++ b/queue-6.13/fs-pipe-fix-pipe_occupancy-with-16-bit-indexes.patch @@ -0,0 +1,48 @@ +From f0472863c6ef82537b45b7056dd15b42f505097e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Mar 2025 07:08:09 -1000 +Subject: fs/pipe: Fix pipe_occupancy() with 16-bit indexes + +From: Linus Torvalds + +[ 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 +Cc: Mateusz Guzik +Cc: Manfred Spraul +Cc: Christian Brauner +Cc: Swapnil Sapkal +Cc: Alexey Gladkov +Cc: K Prateek Nayak +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 +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/fs-pipe-read-pipe-head-tail-atomically-outside-pipe-.patch b/queue-6.13/fs-pipe-read-pipe-head-tail-atomically-outside-pipe-.patch new file mode 100644 index 0000000000..d5b5e9bfe3 --- /dev/null +++ b/queue-6.13/fs-pipe-read-pipe-head-tail-atomically-outside-pipe-.patch @@ -0,0 +1,220 @@ +From 2fa9bc4a929953d776783b960bbbd9b78efed1c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Mar 2025 13:51:38 +0000 +Subject: fs/pipe: Read pipe->{head,tail} atomically outside pipe->mutex + +From: Linus Torvalds + +[ 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 +Closes: https://lore.kernel.org/lkml/e813814e-7094-4673-bc69-731af065a0eb@amd.com/ +Reported-by: Alexey Gladkov +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 +Reviewed-by: Oleg Nesterov +Tested-by: Alexey Gladkov +Signed-off-by: K Prateek Nayak +Signed-off-by: Linus Torvalds +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/gpio-rcar-fix-missing-of_node_put-call.patch b/queue-6.13/gpio-rcar-fix-missing-of_node_put-call.patch new file mode 100644 index 0000000000..8aa1b30277 --- /dev/null +++ b/queue-6.13/gpio-rcar-fix-missing-of_node_put-call.patch @@ -0,0 +1,47 @@ +From d5a4d8738022b2e41b210e95dee105b5140a9af3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Mar 2025 16:37:50 +0000 +Subject: gpio: rcar: Fix missing of_node_put() call + +From: Fabrizio Castro + +[ 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 +Reviewed-by: Lad Prabhakar +Reviewed-by: Geert Uytterhoeven +Link: https://lore.kernel.org/r/20250305163753.34913-2-fabrizio.castro.jz@renesas.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + 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 8e0544e924886..a7a1cdf7ac66d 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 + diff --git a/queue-6.13/hid-google-fix-unused-variable-warning-under-config_.patch b/queue-6.13/hid-google-fix-unused-variable-warning-under-config_.patch new file mode 100644 index 0000000000..d3c96ceecf --- /dev/null +++ b/queue-6.13/hid-google-fix-unused-variable-warning-under-config_.patch @@ -0,0 +1,50 @@ +From c94020093fa2fa11fe4e556ead67f7d46ad9a3ae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 18 Feb 2025 00:50:13 +0800 +Subject: HID: google: fix unused variable warning under !CONFIG_ACPI + +From: Yu-Chun Lin + +[ 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 +Closes: https://lore.kernel.org/oe-kbuild-all/202501201141.jctFH5eB-lkp@intel.com/ +Signed-off-by: Yu-Chun Lin +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + 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 0f292b5d3e26d..eb6fd2dc75d0a 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 + diff --git a/queue-6.13/hid-hid-steam-fix-use-after-free-when-detaching-devi.patch b/queue-6.13/hid-hid-steam-fix-use-after-free-when-detaching-devi.patch new file mode 100644 index 0000000000..f0d67faf28 --- /dev/null +++ b/queue-6.13/hid-hid-steam-fix-use-after-free-when-detaching-devi.patch @@ -0,0 +1,45 @@ +From 3a4b825dce120c159d018af99a503f6303ce13b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 15:41:33 -0800 +Subject: HID: hid-steam: Fix use-after-free when detaching device + +From: Vicki Pfau + +[ 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 +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + 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 5f8518f6f5ac7..03e57d8acdadf 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 + diff --git a/queue-6.13/hid-intel-ish-hid-fix-use-after-free-issue-in-hid_is.patch b/queue-6.13/hid-intel-ish-hid-fix-use-after-free-issue-in-hid_is.patch new file mode 100644 index 0000000000..59767cf695 --- /dev/null +++ b/queue-6.13/hid-intel-ish-hid-fix-use-after-free-issue-in-hid_is.patch @@ -0,0 +1,66 @@ +From 0c0668479bc1870ce39e777163eb90f52a8ca8c4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Acked-by: Srinivas Pandruvada +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + 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 cb04cd1d980bd..6550ad5bfbb53 100644 +--- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c ++++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c +@@ -832,9 +832,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 + diff --git a/queue-6.13/hid-intel-ish-hid-fix-use-after-free-issue-in-ishtp_.patch b/queue-6.13/hid-intel-ish-hid-fix-use-after-free-issue-in-ishtp_.patch new file mode 100644 index 0000000000..99b08bcdf7 --- /dev/null +++ b/queue-6.13/hid-intel-ish-hid-fix-use-after-free-issue-in-ishtp_.patch @@ -0,0 +1,55 @@ +From 22966fef72cec94309dad509a4fac9d17500a8a7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Acked-by: Srinivas Pandruvada +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/hwmon-ad7314-validate-leading-zero-bits-and-return-e.patch b/queue-6.13/hwmon-ad7314-validate-leading-zero-bits-and-return-e.patch new file mode 100644 index 0000000000..86633d1075 --- /dev/null +++ b/queue-6.13/hwmon-ad7314-validate-leading-zero-bits-and-return-e.patch @@ -0,0 +1,65 @@ +From 1cff4f8e21c80b98933f8eda1f8576cb43100569 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 24 Feb 2025 09:19:04 +0000 +Subject: hwmon: (ad7314) Validate leading zero bits and return error + +From: Erik Schumacher + +[ 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 +Fixes: 4f3a659581cab ("hwmon: AD7314 driver (ported from IIO)") +Link: https://lore.kernel.org/r/24a50c2981a318580aca8f50d23be7987b69ea00.camel@iris-sensing.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/hwmon-fix-a-null-vs-is_err_or_null-check-in-xgene_hw.patch b/queue-6.13/hwmon-fix-a-null-vs-is_err_or_null-check-in-xgene_hw.patch new file mode 100644 index 0000000000..d522c82f00 --- /dev/null +++ b/queue-6.13/hwmon-fix-a-null-vs-is_err_or_null-check-in-xgene_hw.patch @@ -0,0 +1,37 @@ +From a3533819bc031a0bebd0d00fcf21e35dad4c7b8b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://lore.kernel.org/r/tencent_9AD8E7683EC29CAC97496B44F3F865BA070A@qq.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + 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 1e3bd129a922d..7087197383c96 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 + diff --git a/queue-6.13/hwmon-ntc_thermistor-fix-the-ncpxxxh103-sensor-table.patch b/queue-6.13/hwmon-ntc_thermistor-fix-the-ncpxxxh103-sensor-table.patch new file mode 100644 index 0000000000..9c070a1273 --- /dev/null +++ b/queue-6.13/hwmon-ntc_thermistor-fix-the-ncpxxxh103-sensor-table.patch @@ -0,0 +1,108 @@ +From 4940cdde6a7cf5da398706e0483f8be626543546 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 13:57:53 +0100 +Subject: hwmon: (ntc_thermistor) Fix the ncpXXxh103 sensor table + +From: Maud Spierings + +[ 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 +Link: https://lore.kernel.org/r/20250227-ntc_thermistor_fixes-v1-3-70fa73200b52@gocontroll.com +Reviewed-by: Linus Walleij +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/hwmon-pmbus-initialise-page-count-in-pmbus_identify.patch b/queue-6.13/hwmon-pmbus-initialise-page-count-in-pmbus_identify.patch new file mode 100644 index 0000000000..f1cbf90511 --- /dev/null +++ b/queue-6.13/hwmon-pmbus-initialise-page-count-in-pmbus_identify.patch @@ -0,0 +1,49 @@ +From 7ce8170a5ec0a60e03752fe2d4b892e0c382f52e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 22:24:55 +0000 +Subject: hwmon: (pmbus) Initialise page count in pmbus_identify() + +From: Titus Rwantare + +[ 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 +Fixes: 442aba78728e7 ("hwmon: PMBus device driver") +Link: https://lore.kernel.org/r/20250227222455.2583468-1-titusr@google.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + 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 77cf268e7d2d6..920cd5408141a 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 + diff --git a/queue-6.13/llc-do-not-use-skb_get-before-dev_queue_xmit.patch b/queue-6.13/llc-do-not-use-skb_get-before-dev_queue_xmit.patch new file mode 100644 index 0000000000..6fc2d562cd --- /dev/null +++ b/queue-6.13/llc-do-not-use-skb_get-before-dev_queue_xmit.patch @@ -0,0 +1,166 @@ +From bb1e1913a4f576d58a56b0b09f2b26ed0359e38a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 08:26:42 +0000 +Subject: llc: do not use skb_get() before dev_queue_xmit() + +From: Eric Dumazet + +[ 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: + + __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 +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 + #include + #include +- ++#include + + /** + * 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 + diff --git a/queue-6.13/mctp-i3c-handle-null-header-address.patch b/queue-6.13/mctp-i3c-handle-null-header-address.patch new file mode 100644 index 0000000000..7f86703e23 --- /dev/null +++ b/queue-6.13/mctp-i3c-handle-null-header-address.patch @@ -0,0 +1,41 @@ +From 8bebee00886bbd5f75f50a5b8c171fbb090285f8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Mar 2025 13:59:51 +0800 +Subject: mctp i3c: handle NULL header address + +From: Matt Johnston + +[ 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 +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 +Signed-off-by: Sasha Levin +--- + 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 d247fe483c588..c1e72253063b5 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 + diff --git a/queue-6.13/net-dsa-mt7530-fix-traffic-flooding-for-mmio-devices.patch b/queue-6.13/net-dsa-mt7530-fix-traffic-flooding-for-mmio-devices.patch new file mode 100644 index 0000000000..f7e2b9c07f --- /dev/null +++ b/queue-6.13/net-dsa-mt7530-fix-traffic-flooding-for-mmio-devices.patch @@ -0,0 +1,54 @@ +From ae00ca3bc84fbb027443abe4f42ea2ff5bd550a6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Mar 2025 09:50:23 +0100 +Subject: net: dsa: mt7530: Fix traffic flooding for MMIO devices + +From: Lorenzo Bianconi + +[ 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 +Reviewed-by: Chester A. Unal +Link: https://patch.msgid.link/20250304-mt7988-flooding-fix-v1-1-905523ae83e9@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 086b8b3d5b40f..b05e38d19b212 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2591,7 +2591,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 +@@ -2687,11 +2688,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 + diff --git a/queue-6.13/net-ethtool-netlink-allow-null-nlattrs-when-getting-.patch b/queue-6.13/net-ethtool-netlink-allow-null-nlattrs-when-getting-.patch new file mode 100644 index 0000000000..9a72585382 --- /dev/null +++ b/queue-6.13/net-ethtool-netlink-allow-null-nlattrs-when-getting-.patch @@ -0,0 +1,239 @@ +From f4a6bdc6258e2d7647f29d67c24544dd75d06862 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 1 Mar 2025 15:11:13 +0100 +Subject: net: ethtool: netlink: Allow NULL nlattrs when getting a phy_device + +From: Maxime Chevallier + +[ 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 +Reviewed-by: Kory Maincent +Reported-by: Parthiban Veerasooran +Link: https://patch.msgid.link/20250301141114.97204-1-maxime.chevallier@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/net-ethtool-plumb-phy-stats-to-phy-drivers.patch b/queue-6.13/net-ethtool-plumb-phy-stats-to-phy-drivers.patch new file mode 100644 index 0000000000..7f3bc72f9b --- /dev/null +++ b/queue-6.13/net-ethtool-plumb-phy-stats-to-phy-drivers.patch @@ -0,0 +1,343 @@ +From 166e1f64e88edd782fdce531e619f4aebdca354f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Jan 2025 07:05:12 +0100 +Subject: net: ethtool: plumb PHY stats to PHY drivers + +From: Jakub Kicinski + +[ 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 +Signed-off-by: Oleksij Rempel +Signed-off-by: Paolo Abeni +Stable-dep-of: 637399bf7e77 ("net: ethtool: netlink: Allow NULL nlattrs when getting a phy_device") +Signed-off-by: Sasha Levin +--- + 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 0d20b534122b2..ffd1da04d3ad1 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 b26bb33cd1d48..5f1ad0c7fb534 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -3773,6 +3773,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 563c462056857..bed2b81f8fb3f 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 */ +@@ -2065,6 +2094,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 + ++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 ++#include + + 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 ++#include ++ + #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 + diff --git a/queue-6.13/net-gso-fix-ownership-in-__udp_gso_segment.patch b/queue-6.13/net-gso-fix-ownership-in-__udp_gso_segment.patch new file mode 100644 index 0000000000..9d0161a9d5 --- /dev/null +++ b/queue-6.13/net-gso-fix-ownership-in-__udp_gso_segment.patch @@ -0,0 +1,73 @@ +From c0e2c29cabd13663a2362314e4a1946bba87c4f6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 26 Feb 2025 18:13:42 +0100 +Subject: net: gso: fix ownership in __udp_gso_segment + +From: Antoine Tenart + +[ 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 +Link: https://patch.msgid.link/20250226171352.258045-1-atenart@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/net-hns3-make-sure-ptp-clock-is-unregister-and-freed.patch b/queue-6.13/net-hns3-make-sure-ptp-clock-is-unregister-and-freed.patch new file mode 100644 index 0000000000..fbe95fcfa2 --- /dev/null +++ b/queue-6.13/net-hns3-make-sure-ptp-clock-is-unregister-and-freed.patch @@ -0,0 +1,42 @@ +From 58a6b1734fd69417408b60bf345206563a357309 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Jijie Shao +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250228105258.1243461-1-shaojijie@huawei.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/net-ipa-enable-checksum-for-ipa_endpoint_ap_modem_-r.patch b/queue-6.13/net-ipa-enable-checksum-for-ipa_endpoint_ap_modem_-r.patch new file mode 100644 index 0000000000..41466083ef --- /dev/null +++ b/queue-6.13/net-ipa-enable-checksum-for-ipa_endpoint_ap_modem_-r.patch @@ -0,0 +1,46 @@ +From 6c7b68871205e53034f0aba85aafee3fda86d05c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Alex Elder +Link: https://patch.msgid.link/20250227-ipa-v4-7-fixes-v1-3-a88dd8249d8a@fairphone.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/net-ipa-fix-qsb-data-for-v4.7.patch b/queue-6.13/net-ipa-fix-qsb-data-for-v4.7.patch new file mode 100644 index 0000000000..19fe49cd17 --- /dev/null +++ b/queue-6.13/net-ipa-fix-qsb-data-for-v4.7.patch @@ -0,0 +1,40 @@ +From 05b97fb2353433d34c143aa97ba0247fbd46f971 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 11:33:41 +0100 +Subject: net: ipa: Fix QSB data for v4.7 + +From: Luca Weiss + +[ 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 +Reviewed-by: Alex Elder +Link: https://patch.msgid.link/20250227-ipa-v4-7-fixes-v1-2-a88dd8249d8a@fairphone.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/net-ipa-fix-v4.7-resource-group-names.patch b/queue-6.13/net-ipa-fix-v4.7-resource-group-names.patch new file mode 100644 index 0000000000..af3224e1cf --- /dev/null +++ b/queue-6.13/net-ipa-fix-v4.7-resource-group-names.patch @@ -0,0 +1,76 @@ +From 8447f8402e8a2fa3b97a9390b742b970ddd1809b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 11:33:40 +0100 +Subject: net: ipa: Fix v4.7 resource group names + +From: Luca Weiss + +[ 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 +Reviewed-by: Alex Elder +Link: https://patch.msgid.link/20250227-ipa-v4-7-fixes-v1-1-a88dd8249d8a@fairphone.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/net-ipv6-fix-dst-ref-loop-in-ila-lwtunnel.patch b/queue-6.13/net-ipv6-fix-dst-ref-loop-in-ila-lwtunnel.patch new file mode 100644 index 0000000000..fdec224570 --- /dev/null +++ b/queue-6.13/net-ipv6-fix-dst-ref-loop-in-ila-lwtunnel.patch @@ -0,0 +1,41 @@ +From 6067ab0f22c3025eed4d2374fbf04378385720df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Mar 2025 19:10:39 +0100 +Subject: net: ipv6: fix dst ref loop in ila lwtunnel + +From: Justin Iurman + +[ 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 +Signed-off-by: Justin Iurman +Link: https://patch.msgid.link/20250304181039.35951-1-justin.iurman@uliege.be +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/net-ipv6-fix-missing-dst-ref-drop-in-ila-lwtunnel.patch b/queue-6.13/net-ipv6-fix-missing-dst-ref-drop-in-ila-lwtunnel.patch new file mode 100644 index 0000000000..08f06dafe0 --- /dev/null +++ b/queue-6.13/net-ipv6-fix-missing-dst-ref-drop-in-ila-lwtunnel.patch @@ -0,0 +1,37 @@ +From 9cbc296718bc79af937ad53545dbf61182bf00ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Mar 2025 09:16:55 +0100 +Subject: net: ipv6: fix missing dst ref drop in ila lwtunnel + +From: Justin Iurman + +[ 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 +Signed-off-by: Justin Iurman +Link: https://patch.msgid.link/20250305081655.19032-1-justin.iurman@uliege.be +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/net-timestamp-support-tcp-gso-case-for-a-few-missing.patch b/queue-6.13/net-timestamp-support-tcp-gso-case-for-a-few-missing.patch new file mode 100644 index 0000000000..dbec058f17 --- /dev/null +++ b/queue-6.13/net-timestamp-support-tcp-gso-case-for-a-few-missing.patch @@ -0,0 +1,73 @@ +From 69681afbdb409e0684ec3c2a38a5a0e787a9025f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Mar 2025 08:44:29 +0800 +Subject: net-timestamp: support TCP GSO case for a few missing flags + +From: Jason Xing + +[ 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 +Reviewed-by: Willem de Bruijn +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 + #include + +-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 + diff --git a/queue-6.13/nvme-ioctl-fix-leaked-requests-on-mapping-error.patch b/queue-6.13/nvme-ioctl-fix-leaked-requests-on-mapping-error.patch new file mode 100644 index 0000000000..f8190c4d39 --- /dev/null +++ b/queue-6.13/nvme-ioctl-fix-leaked-requests-on-mapping-error.patch @@ -0,0 +1,57 @@ +From dadd055d5a61fc1f15e93db63af88201ea49af8c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 24 Feb 2025 17:13:30 -0800 +Subject: nvme-ioctl: fix leaked requests on mapping error + +From: Keith Busch + +[ 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 +Reviewed-by: Kanchan Joshi +Reviewed-by: Christoph Hellwig +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + 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 b1b46c2713e1c..24e2c702da7a2 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 + diff --git a/queue-6.13/nvme-tcp-add-basic-support-for-the-c2htermreq-pdu.patch b/queue-6.13/nvme-tcp-add-basic-support-for-the-c2htermreq-pdu.patch new file mode 100644 index 0000000000..308c1efa76 --- /dev/null +++ b/queue-6.13/nvme-tcp-add-basic-support-for-the-c2htermreq-pdu.patch @@ -0,0 +1,104 @@ +From e3316b9433ce0459e640d6127e16b3c7f89c919b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 17 Feb 2025 17:08:27 +0100 +Subject: nvme-tcp: add basic support for the C2HTermReq PDU + +From: Maurizio Lombardi + +[ 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 +Reviewed-by: Sagi Grimberg +Signed-off-by: Keith Busch +Stable-dep-of: ad95bab0cd28 ("nvme-tcp: fix potential memory corruption in nvme_tcp_recv_pdu()") +Signed-off-by: Sasha Levin +--- + 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 d7c193028e7c3..8a9131c95a3da 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 + diff --git a/queue-6.13/nvme-tcp-fix-potential-memory-corruption-in-nvme_tcp.patch b/queue-6.13/nvme-tcp-fix-potential-memory-corruption-in-nvme_tcp.patch new file mode 100644 index 0000000000..588548ab98 --- /dev/null +++ b/queue-6.13/nvme-tcp-fix-potential-memory-corruption-in-nvme_tcp.patch @@ -0,0 +1,87 @@ +From f1777e91068605db1ab0d3878ece353ab3f68d95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 26 Feb 2025 14:42:18 +0100 +Subject: nvme-tcp: fix potential memory corruption in nvme_tcp_recv_pdu() + +From: Maurizio Lombardi + +[ 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 +Reviewed-by: Sagi Grimberg +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + 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 8a9131c95a3da..ade7f1af33d80 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 + diff --git a/queue-6.13/nvme-tcp-fix-signedness-bug-in-nvme_tcp_init_connect.patch b/queue-6.13/nvme-tcp-fix-signedness-bug-in-nvme_tcp_init_connect.patch new file mode 100644 index 0000000000..4da951ab7b --- /dev/null +++ b/queue-6.13/nvme-tcp-fix-signedness-bug-in-nvme_tcp_init_connect.patch @@ -0,0 +1,53 @@ +From b17d52d57b2895fd1feddd56ef46b19490b91551 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Mar 2025 18:52:59 +0300 +Subject: nvme-tcp: fix signedness bug in nvme_tcp_init_connection() + +From: Dan Carpenter + +[ 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 +Reviewed-by: Caleb Sander Mateos +Reviewed-by: Sagi Grimberg +Reviewed-by: Chaitanya Kulkarni +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + 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 ade7f1af33d80..5c50f1a5af957 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 + diff --git a/queue-6.13/nvmet-remove-old-function-prototype.patch b/queue-6.13/nvmet-remove-old-function-prototype.patch new file mode 100644 index 0000000000..d7a3461a77 --- /dev/null +++ b/queue-6.13/nvmet-remove-old-function-prototype.patch @@ -0,0 +1,34 @@ +From 42c3b8b87c03f12c5cbf1178787a74be1a957d8e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 27 Feb 2025 20:00:05 +0100 +Subject: nvmet: remove old function prototype + +From: Maurizio Lombardi + +[ Upstream commit 0979ff3676b1b4e6a20970bc265491d23c2da42b ] + +nvmet_subsys_nsid_exists() doesn't exist anymore + +Fixes: 74d16965d7ac ("nvmet-loop: avoid using mutex in IO hotpath") +Signed-off-by: Maurizio Lombardi +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + drivers/nvme/target/nvmet.h | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h +index 7233549f7c8a0..016a5c2505464 100644 +--- a/drivers/nvme/target/nvmet.h ++++ b/drivers/nvme/target/nvmet.h +@@ -607,7 +607,6 @@ void nvmet_subsys_disc_changed(struct nvmet_subsys *subsys, + struct nvmet_host *host); + void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type, + u8 event_info, u8 log_page); +-bool nvmet_subsys_nsid_exists(struct nvmet_subsys *subsys, u32 nsid); + + #define NVMET_MIN_QUEUE_SIZE 16 + #define NVMET_MAX_QUEUE_SIZE 1024 +-- +2.39.5 + diff --git a/queue-6.13/nvmet-tcp-fix-a-possible-sporadic-response-drops-in-.patch b/queue-6.13/nvmet-tcp-fix-a-possible-sporadic-response-drops-in-.patch new file mode 100644 index 0000000000..9516f96fe8 --- /dev/null +++ b/queue-6.13/nvmet-tcp-fix-a-possible-sporadic-response-drops-in-.patch @@ -0,0 +1,71 @@ +From b1d1aa466b08ca05f944e80325e967458a60d81a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Sagi Grimberg +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/perf-core-fix-pmus_lock-vs.-pmus_srcu-ordering.patch b/queue-6.13/perf-core-fix-pmus_lock-vs.-pmus_srcu-ordering.patch new file mode 100644 index 0000000000..c651fbe2bb --- /dev/null +++ b/queue-6.13/perf-core-fix-pmus_lock-vs.-pmus_srcu-ordering.patch @@ -0,0 +1,58 @@ +From 6784f5ed325dae0a086fada909b581c83dc496c7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 4 Nov 2024 14:39:11 +0100 +Subject: perf/core: Fix pmus_lock vs. pmus_srcu ordering + +From: Peter Zijlstra + +[ 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) +Signed-off-by: Ingo Molnar +Link: https://lore.kernel.org/r/20241104135517.679556858@infradead.org +Signed-off-by: Sasha Levin +--- + 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 0e6e16eb2d106..43a44a6e243b1 100644 +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -11896,6 +11896,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 +@@ -11905,7 +11907,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); +@@ -11913,7 +11914,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 + diff --git a/queue-6.13/ppp-fix-kmsan-uninit-value-warning-with-bpf.patch b/queue-6.13/ppp-fix-kmsan-uninit-value-warning-with-bpf.patch new file mode 100644 index 0000000000..0bd6db057e --- /dev/null +++ b/queue-6.13/ppp-fix-kmsan-uninit-value-warning-with-bpf.patch @@ -0,0 +1,121 @@ +From f076a35d44e56cac10b4bb8c6911c438e89cb789 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 28 Feb 2025 22:14:08 +0800 +Subject: ppp: Fix KMSAN uninit-value warning with bpf + +From: Jiayuan Chen + +[ 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 +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 +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250228141408.393864-1-jiayuan.chen@linux.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/sched-fair-fix-potential-memory-corruption-in-child_.patch b/queue-6.13/sched-fair-fix-potential-memory-corruption-in-child_.patch new file mode 100644 index 0000000000..51a2962205 --- /dev/null +++ b/queue-6.13/sched-fair-fix-potential-memory-corruption-in-child_.patch @@ -0,0 +1,70 @@ +From c9a453357b738f240dfe63a77f40e0f9dab8af71 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-and-tested-by: K Prateek Nayak +Signed-off-by: Peter Zijlstra (Intel) +Reviewed-by: Vincent Guittot +Link: https://lore.kernel.org/r/20250304214031.2882646-1-zecheng@google.com +Signed-off-by: Sasha Levin +--- + 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 7d0a05660e5ef..4f850edf16401 100644 +--- a/kernel/sched/fair.c ++++ b/kernel/sched/fair.c +@@ -4046,15 +4046,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 + diff --git a/queue-6.13/series b/queue-6.13/series index 378d754d08..96c3da9448 100644 --- a/queue-6.13/series +++ b/queue-6.13/series @@ -78,3 +78,77 @@ mm-don-t-skip-arch_sync_kernel_mappings-in-error-paths.patch 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-fw-avoid-using-an-uninitialized-variabl.patch +wifi-iwlwifi-mvm-clean-up-roc-on-failure.patch +wifi-iwlwifi-mvm-log-error-for-failures-after-d3.patch +wifi-iwlwifi-mvm-don-t-dump-the-firmware-state-upon-.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-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 +fgraph-replace-fgraph_ret_regs-with-ftrace_regs.patch +fprobe-use-ftrace_regs-in-fprobe-entry-handler.patch +fprobe-use-ftrace_regs-in-fprobe-exit-handler.patch +tracing-add-ftrace_partial_regs-for-converting-ftrac.patch +tracing-add-ftrace_fill_perf_regs-for-perf-event.patch +fprobe-rewrite-fprobe-on-function-graph-tracer.patch +tracing-fprobe-events-log-error-for-exceeding-the-nu.patch +drm-nouveau-select-fw-caching.patch +bluetooth-btusb-initialize-.owner-field-of-force_pol.patch +nvmet-remove-old-function-prototype.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 +cred-return-old-creds-from-revert_creds_light.patch +cred-fix-rcu-warnings-in-override-revert_creds.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-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 +drm-bochs-fix-dpms-regression.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 diff --git a/queue-6.13/tracing-add-ftrace_fill_perf_regs-for-perf-event.patch b/queue-6.13/tracing-add-ftrace_fill_perf_regs-for-perf-event.patch new file mode 100644 index 0000000000..1921c3190b --- /dev/null +++ b/queue-6.13/tracing-add-ftrace_fill_perf_regs-for-perf-event.patch @@ -0,0 +1,171 @@ +From 2dc177cc7af51eb92a7f8ee3b81edfcc1601d361 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Dec 2024 14:12:59 +0900 +Subject: tracing: Add ftrace_fill_perf_regs() for perf event + +From: Masami Hiramatsu (Google) + +[ Upstream commit d5d01b71996ec03af51b3c0736c92d0fc89703b5 ] + +Add ftrace_fill_perf_regs() which should be compatible with the +perf_fetch_caller_regs(). In other words, the pt_regs returned from the +ftrace_fill_perf_regs() must satisfy 'user_mode(regs) == false' and can be +used for stack tracing. + +Signed-off-by: Masami Hiramatsu (Google) +Acked-by: Will Deacon +Acked-by: Heiko Carstens # s390 +Cc: Alexei Starovoitov +Cc: Florent Revest +Cc: Martin KaFai Lau +Cc: bpf +Cc: Alexei Starovoitov +Cc: Jiri Olsa +Cc: Alan Maguire +Cc: Heiko Carstens +Cc: Mark Rutland +Cc: Catalin Marinas +Cc: Will Deacon +Cc: Michael Ellerman +Cc: Nicholas Piggin +Cc: Christophe Leroy +Cc: Naveen N Rao +Cc: Madhavan Srinivasan +Cc: Vasily Gorbik +Cc: Alexander Gordeev +Cc: Christian Borntraeger +Cc: Sven Schnelle +Cc: Thomas Gleixner +Cc: Ingo Molnar +Cc: Borislav Petkov +Cc: Dave Hansen +Cc: x86@kernel.org +Cc: "H. Peter Anvin" +Link: https://lore.kernel.org/173518997908.391279.15910334347345106424.stgit@devnote2 +Signed-off-by: Steven Rostedt (Google) +Stable-dep-of: db5e228611b1 ("tracing: fprobe-events: Log error for exceeding the number of entry args") +Signed-off-by: Sasha Levin +--- + arch/arm64/include/asm/ftrace.h | 7 +++++++ + arch/powerpc/include/asm/ftrace.h | 7 +++++++ + arch/s390/include/asm/ftrace.h | 6 ++++++ + arch/x86/include/asm/ftrace.h | 7 +++++++ + include/linux/ftrace.h | 31 +++++++++++++++++++++++++++++++ + 5 files changed, 58 insertions(+) + +diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h +index 09210f853f12d..10e56522122aa 100644 +--- a/arch/arm64/include/asm/ftrace.h ++++ b/arch/arm64/include/asm/ftrace.h +@@ -148,6 +148,13 @@ ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs) + return regs; + } + ++#define arch_ftrace_fill_perf_regs(fregs, _regs) do { \ ++ (_regs)->pc = arch_ftrace_regs(fregs)->pc; \ ++ (_regs)->regs[29] = arch_ftrace_regs(fregs)->fp; \ ++ (_regs)->sp = arch_ftrace_regs(fregs)->sp; \ ++ (_regs)->pstate = PSR_MODE_EL1h; \ ++ } while (0) ++ + int ftrace_regs_query_register_offset(const char *name); + + int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec); +diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h +index db481b336bca4..fe181bafdca4f 100644 +--- a/arch/powerpc/include/asm/ftrace.h ++++ b/arch/powerpc/include/asm/ftrace.h +@@ -43,6 +43,13 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs * + return arch_ftrace_regs(fregs)->regs.msr ? &arch_ftrace_regs(fregs)->regs : NULL; + } + ++#define arch_ftrace_fill_perf_regs(fregs, _regs) do { \ ++ (_regs)->result = 0; \ ++ (_regs)->nip = arch_ftrace_regs(fregs)->regs.nip; \ ++ (_regs)->gpr[1] = arch_ftrace_regs(fregs)->regs.gpr[1]; \ ++ asm volatile("mfmsr %0" : "=r" ((_regs)->msr)); \ ++ } while (0) ++ + static __always_inline void + ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, + unsigned long ip) +diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h +index 5c94c1fc1bc1c..5b7cb49c41ee0 100644 +--- a/arch/s390/include/asm/ftrace.h ++++ b/arch/s390/include/asm/ftrace.h +@@ -76,6 +76,12 @@ ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs) + return ftrace_regs_get_stack_pointer(fregs); + } + ++#define arch_ftrace_fill_perf_regs(fregs, _regs) do { \ ++ (_regs)->psw.mask = 0; \ ++ (_regs)->psw.addr = arch_ftrace_regs(fregs)->regs.psw.addr; \ ++ (_regs)->gprs[15] = arch_ftrace_regs(fregs)->regs.gprs[15]; \ ++ } while (0) ++ + #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS + /* + * When an ftrace registered caller is tracing a function that is +diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h +index d61407c680c28..7e06f8c7937aa 100644 +--- a/arch/x86/include/asm/ftrace.h ++++ b/arch/x86/include/asm/ftrace.h +@@ -47,6 +47,13 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs) + return &arch_ftrace_regs(fregs)->regs; + } + ++#define arch_ftrace_fill_perf_regs(fregs, _regs) do { \ ++ (_regs)->ip = arch_ftrace_regs(fregs)->regs.ip; \ ++ (_regs)->sp = arch_ftrace_regs(fregs)->regs.sp; \ ++ (_regs)->cs = __KERNEL_CS; \ ++ (_regs)->flags = 0; \ ++ } while (0) ++ + #define ftrace_regs_set_instruction_pointer(fregs, _ip) \ + do { arch_ftrace_regs(fregs)->regs.ip = (_ip); } while (0) + +diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h +index 863c014dff683..56cb3d243c6c4 100644 +--- a/include/linux/ftrace.h ++++ b/include/linux/ftrace.h +@@ -207,6 +207,37 @@ ftrace_partial_regs(struct ftrace_regs *fregs, struct pt_regs *regs) + + #endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS */ + ++#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS ++ ++/* ++ * Please define arch dependent pt_regs which compatible to the ++ * perf_arch_fetch_caller_regs() but based on ftrace_regs. ++ * This requires ++ * - user_mode(_regs) returns false (always kernel mode). ++ * - able to use the _regs for stack trace. ++ */ ++#ifndef arch_ftrace_fill_perf_regs ++/* As same as perf_arch_fetch_caller_regs(), do nothing by default */ ++#define arch_ftrace_fill_perf_regs(fregs, _regs) do {} while (0) ++#endif ++ ++static __always_inline struct pt_regs * ++ftrace_fill_perf_regs(struct ftrace_regs *fregs, struct pt_regs *regs) ++{ ++ arch_ftrace_fill_perf_regs(fregs, regs); ++ return regs; ++} ++ ++#else /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ ++ ++static __always_inline struct pt_regs * ++ftrace_fill_perf_regs(struct ftrace_regs *fregs, struct pt_regs *regs) ++{ ++ return &arch_ftrace_regs(fregs)->regs; ++} ++ ++#endif ++ + /* + * When true, the ftrace_regs_{get,set}_*() functions may be used on fregs. + * Note: this can be true even when ftrace_get_regs() cannot provide a pt_regs. +-- +2.39.5 + diff --git a/queue-6.13/tracing-add-ftrace_partial_regs-for-converting-ftrac.patch b/queue-6.13/tracing-add-ftrace_partial_regs-for-converting-ftrac.patch new file mode 100644 index 0000000000..418f517689 --- /dev/null +++ b/queue-6.13/tracing-add-ftrace_partial_regs-for-converting-ftrac.patch @@ -0,0 +1,124 @@ +From d610ad390f261355db36806d148174e492d32f0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Dec 2024 14:12:47 +0900 +Subject: tracing: Add ftrace_partial_regs() for converting ftrace_regs to + pt_regs + +From: Masami Hiramatsu (Google) + +[ Upstream commit b9b55c8912ce1e5555715d126486bdd63ddfeaec ] + +Add ftrace_partial_regs() which converts the ftrace_regs to pt_regs. +This is for the eBPF which needs this to keep the same pt_regs interface +to access registers. +Thus when replacing the pt_regs with ftrace_regs in fprobes (which is +used by kprobe_multi eBPF event), this will be used. + +If the architecture defines its own ftrace_regs, this copies partial +registers to pt_regs and returns it. If not, ftrace_regs is the same as +pt_regs and ftrace_partial_regs() will return ftrace_regs::regs. + +Signed-off-by: Masami Hiramatsu (Google) +Acked-by: Florent Revest +Cc: Alexei Starovoitov +Cc: Martin KaFai Lau +Cc: bpf +Cc: Alexei Starovoitov +Cc: Jiri Olsa +Cc: Alan Maguire +Cc: Mark Rutland +Cc: Catalin Marinas +Cc: Will Deacon +Cc: Paul Walmsley +Cc: Palmer Dabbelt +Cc: Albert Ou +Link: https://lore.kernel.org/173518996761.391279.4987911298206448122.stgit@devnote2 +Signed-off-by: Steven Rostedt (Google) +Stable-dep-of: db5e228611b1 ("tracing: fprobe-events: Log error for exceeding the number of entry args") +Signed-off-by: Sasha Levin +--- + arch/arm64/include/asm/ftrace.h | 13 +++++++++++++ + arch/riscv/include/asm/ftrace.h | 14 ++++++++++++++ + include/linux/ftrace.h | 17 +++++++++++++++++ + 3 files changed, 44 insertions(+) + +diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h +index b5fa57b61378e..09210f853f12d 100644 +--- a/arch/arm64/include/asm/ftrace.h ++++ b/arch/arm64/include/asm/ftrace.h +@@ -135,6 +135,19 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs) + return arch_ftrace_regs(fregs)->fp; + } + ++static __always_inline struct pt_regs * ++ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs) ++{ ++ struct __arch_ftrace_regs *afregs = arch_ftrace_regs(fregs); ++ ++ memcpy(regs->regs, afregs->regs, sizeof(afregs->regs)); ++ regs->sp = afregs->sp; ++ regs->pc = afregs->pc; ++ regs->regs[29] = afregs->fp; ++ regs->regs[30] = afregs->lr; ++ return regs; ++} ++ + int ftrace_regs_query_register_offset(const char *name); + + int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec); +diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h +index 9372f8d7036f8..7064a530794b6 100644 +--- a/arch/riscv/include/asm/ftrace.h ++++ b/arch/riscv/include/asm/ftrace.h +@@ -197,6 +197,20 @@ static __always_inline void ftrace_override_function_with_return(struct ftrace_r + arch_ftrace_regs(fregs)->epc = arch_ftrace_regs(fregs)->ra; + } + ++static __always_inline struct pt_regs * ++ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs) ++{ ++ struct __arch_ftrace_regs *afregs = arch_ftrace_regs(fregs); ++ ++ memcpy(®s->a0, afregs->args, sizeof(afregs->args)); ++ regs->epc = afregs->epc; ++ regs->ra = afregs->ra; ++ regs->sp = afregs->sp; ++ regs->s0 = afregs->s0; ++ regs->t1 = afregs->t1; ++ return regs; ++} ++ + int ftrace_regs_query_register_offset(const char *name); + + void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, +diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h +index 46ac44366c90a..863c014dff683 100644 +--- a/include/linux/ftrace.h ++++ b/include/linux/ftrace.h +@@ -190,6 +190,23 @@ static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs + return arch_ftrace_get_regs(fregs); + } + ++#if !defined(CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS) || \ ++ defined(CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS) ++ ++static __always_inline struct pt_regs * ++ftrace_partial_regs(struct ftrace_regs *fregs, struct pt_regs *regs) ++{ ++ /* ++ * If CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS=y, ftrace_regs memory ++ * layout is including pt_regs. So always returns that address. ++ * Since arch_ftrace_get_regs() will check some members and may return ++ * NULL, we can not use it. ++ */ ++ return &arch_ftrace_regs(fregs)->regs; ++} ++ ++#endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS */ ++ + /* + * When true, the ftrace_regs_{get,set}_*() functions may be used on fregs. + * Note: this can be true even when ftrace_get_regs() cannot provide a pt_regs. +-- +2.39.5 + diff --git a/queue-6.13/tracing-fprobe-events-log-error-for-exceeding-the-nu.patch b/queue-6.13/tracing-fprobe-events-log-error-for-exceeding-the-nu.patch new file mode 100644 index 0000000000..5886cd0f44 --- /dev/null +++ b/queue-6.13/tracing-fprobe-events-log-error-for-exceeding-the-nu.patch @@ -0,0 +1,59 @@ +From b9d5ba4f995b836db2e9dfec82979f43671868b0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 26 Feb 2025 15:19:02 +0900 +Subject: tracing: fprobe-events: Log error for exceeding the number of entry + args + +From: Masami Hiramatsu (Google) + +[ Upstream commit db5e228611b118cf7b1f8084063feda5c037f4a7 ] + +Add error message when the number of entry argument exceeds the +maximum size of entry data. +This is currently checked when registering fprobe, but in this case +no error message is shown in the error_log file. + +Link: https://lore.kernel.org/all/174055074269.4079315.17809232650360988538.stgit@mhiramat.tok.corp.google.com/ + +Fixes: 25f00e40ce79 ("tracing/probes: Support $argN in return probe (kprobe and fprobe)") +Signed-off-by: Masami Hiramatsu (Google) +Reviewed-by: Steven Rostedt (Google) +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_fprobe.c | 5 +++++ + kernel/trace/trace_probe.h | 3 ++- + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c +index d906baba2d40c..94fe261218771 100644 +--- a/kernel/trace/trace_fprobe.c ++++ b/kernel/trace/trace_fprobe.c +@@ -1242,6 +1242,11 @@ static int __trace_fprobe_create(int argc, const char *argv[]) + if (is_return && tf->tp.entry_arg) { + tf->fp.entry_handler = trace_fprobe_entry_handler; + tf->fp.entry_data_size = traceprobe_get_entry_data_size(&tf->tp); ++ if (ALIGN(tf->fp.entry_data_size, sizeof(long)) > MAX_FPROBE_DATA_SIZE) { ++ trace_probe_log_set_index(2); ++ trace_probe_log_err(0, TOO_MANY_EARGS); ++ return -E2BIG; ++ } + } + + ret = traceprobe_set_print_fmt(&tf->tp, +diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h +index fba3ede870541..c47ca002347a7 100644 +--- a/kernel/trace/trace_probe.h ++++ b/kernel/trace/trace_probe.h +@@ -545,7 +545,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, + C(NO_BTF_FIELD, "This field is not found."), \ + C(BAD_BTF_TID, "Failed to get BTF type info."),\ + C(BAD_TYPE4STR, "This type does not fit for string."),\ +- C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"), ++ C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"),\ ++ C(TOO_MANY_EARGS, "Too many entry arguments specified"), + + #undef C + #define C(a, b) TP_ERR_##a +-- +2.39.5 + diff --git a/queue-6.13/tracing-probe-events-remove-unused-max_arg_buf_len-m.patch b/queue-6.13/tracing-probe-events-remove-unused-max_arg_buf_len-m.patch new file mode 100644 index 0000000000..002ac615e4 --- /dev/null +++ b/queue-6.13/tracing-probe-events-remove-unused-max_arg_buf_len-m.patch @@ -0,0 +1,38 @@ +From 92c8502e7c5098d794a87fbfc144268d810f0959 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 26 Feb 2025 15:19:18 +0900 +Subject: tracing: probe-events: Remove unused MAX_ARG_BUF_LEN macro + +From: Masami Hiramatsu (Google) + +[ 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) +Reviewed-by: Steven Rostedt (Google) +Signed-off-by: Sasha Levin +--- + 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 c47ca002347a7..96792bc4b0924 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 + diff --git a/queue-6.13/ublk-set_params-properly-check-if-parameters-can-be-.patch b/queue-6.13/ublk-set_params-properly-check-if-parameters-can-be-.patch new file mode 100644 index 0000000000..ff801b6655 --- /dev/null +++ b/queue-6.13/ublk-set_params-properly-check-if-parameters-can-be-.patch @@ -0,0 +1,54 @@ +From c6515b32626cf47865e135ed278aa2a91b42de39 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Mar 2025 14:34:26 -0700 +Subject: ublk: set_params: properly check if parameters can be applied + +From: Uday Shankar + +[ 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 +Fixes: 0aa73170eba5 ("ublk_drv: add SET_PARAMS/GET_PARAMS control command") +Reviewed-by: Ming Lei +Link: https://lore.kernel.org/r/20250304-set_params-v1-1-17b5e0887606@purestorage.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + 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 934ab9332c80a..d9e8bf9f5e5a8 100644 +--- a/drivers/block/ublk_drv.c ++++ b/drivers/block/ublk_drv.c +@@ -2716,9 +2716,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 + diff --git a/queue-6.13/vlan-enforce-underlying-device-type.patch b/queue-6.13/vlan-enforce-underlying-device-type.patch new file mode 100644 index 0000000000..fa89075746 --- /dev/null +++ b/queue-6.13/vlan-enforce-underlying-device-type.patch @@ -0,0 +1,69 @@ +From 1bd7b77cd2bd91f88c5461afb48bbf2e6f462bc4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 3 Mar 2025 16:56:19 +0100 +Subject: vlan: enforce underlying device type + +From: Oscar Maes + +[ 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 +Reviewed-by: Jiri Pirko +Link: https://patch.msgid.link/20250303155619.8918-1-oscmaes92@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/wifi-iwlwifi-fix-a-msdu-tso-preparation.patch b/queue-6.13/wifi-iwlwifi-fix-a-msdu-tso-preparation.patch new file mode 100644 index 0000000000..e98c54d382 --- /dev/null +++ b/queue-6.13/wifi-iwlwifi-fix-a-msdu-tso-preparation.patch @@ -0,0 +1,145 @@ +From 15b9eb6e9b77ef803943832538e1cb4cd74856f2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 9 Feb 2025 14:34:53 +0200 +Subject: wifi: iwlwifi: Fix A-MSDU TSO preparation + +From: Ilan Peer + +[ 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 +Reviewed-by: Benjamin Berg +Signed-off-by: Miri Korenblit +Link: https://patch.msgid.link/20250209143303.75769a4769bf.Iaf79e8538093cdf8c446c292cc96164ad6498f61@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + .../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 + #include +@@ -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 1ef14340953c3..f46bdc0a5fec9 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 + diff --git a/queue-6.13/wifi-iwlwifi-free-pages-allocated-when-failing-to-bu.patch b/queue-6.13/wifi-iwlwifi-free-pages-allocated-when-failing-to-bu.patch new file mode 100644 index 0000000000..1f29cf5266 --- /dev/null +++ b/queue-6.13/wifi-iwlwifi-free-pages-allocated-when-failing-to-bu.patch @@ -0,0 +1,38 @@ +From 62379e5f7f7056fe984ff74f17706127ffdf24b8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 9 Feb 2025 14:34:52 +0200 +Subject: wifi: iwlwifi: Free pages allocated when failing to build A-MSDU + +From: Ilan Peer + +[ 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 +Reviewed-by: Emmanuel Grumbach +Signed-off-by: Miri Korenblit +Link: https://patch.msgid.link/20250209143303.bc27fad9b3d5.Ibf43dd18fb652b1a59061204e081f11c9fa34a3f@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/wifi-iwlwifi-fw-avoid-using-an-uninitialized-variabl.patch b/queue-6.13/wifi-iwlwifi-fw-avoid-using-an-uninitialized-variabl.patch new file mode 100644 index 0000000000..ca72ee7ed5 --- /dev/null +++ b/queue-6.13/wifi-iwlwifi-fw-avoid-using-an-uninitialized-variabl.patch @@ -0,0 +1,43 @@ +From 045f29e06349f1e5413ae2f994929d487cf5bf89 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 9 Feb 2025 14:34:46 +0200 +Subject: wifi: iwlwifi: fw: avoid using an uninitialized variable + +From: Miri Korenblit + +[ Upstream commit 3f8aa0b8a53df2247a84eaf3b3aa38b6ef86cb1c ] + +iwl_fwrt_read_err_table can return true also when it failed to read +the memory. In this case, err_id argument is not initialized, +but the callers are still using it. + +Simply initialize it to 0. If the error table was read successfully it'll +be overridden. + +Fixes: 43e0b2ada519 ("wifi: iwlwifi: fw: add an error table status getter") +Signed-off-by: Miri Korenblit +Reviewed-by: Emmanuel Grumbach +Link: https://patch.msgid.link/20250209143303.37cdbba4eb56.I95fe9bd95303b8179f946766558a9f15f4fe254c@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/intel/iwlwifi/fw/dump.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c +index 8e0c85a1240d7..c7b261c8ec969 100644 +--- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c ++++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c +@@ -540,6 +540,9 @@ bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id) + } err_info = {}; + int ret; + ++ if (err_id) ++ *err_id = 0; ++ + if (!base) + return false; + +-- +2.39.5 + diff --git a/queue-6.13/wifi-iwlwifi-limit-printed-string-from-fw-file.patch b/queue-6.13/wifi-iwlwifi-limit-printed-string-from-fw-file.patch new file mode 100644 index 0000000000..37e3f8267e --- /dev/null +++ b/queue-6.13/wifi-iwlwifi-limit-printed-string-from-fw-file.patch @@ -0,0 +1,43 @@ +From e54505d4f4a09d72d9dbeff0f870330747a6f787 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 9 Feb 2025 14:34:51 +0200 +Subject: wifi: iwlwifi: limit printed string from FW file + +From: Johannes Berg + +[ 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 +Signed-off-by: Miri Korenblit +Link: https://patch.msgid.link/20250209143303.cb5f9d0c2f5d.Idec695d53c6c2234aade306f7647b576c7e3d928@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/wifi-iwlwifi-mvm-clean-up-roc-on-failure.patch b/queue-6.13/wifi-iwlwifi-mvm-clean-up-roc-on-failure.patch new file mode 100644 index 0000000000..dd48552c26 --- /dev/null +++ b/queue-6.13/wifi-iwlwifi-mvm-clean-up-roc-on-failure.patch @@ -0,0 +1,48 @@ +From c16f5f3d92d23385951af6bddb5ce39abd1d264c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 9 Feb 2025 14:34:47 +0200 +Subject: wifi: iwlwifi: mvm: clean up ROC on failure + +From: Johannes Berg + +[ 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 +Reviewed-by: Emmanuel Grumbach +Signed-off-by: Miri Korenblit +Link: https://patch.msgid.link/20250209143303.0fe36c291068.I67f5dac742170dd937f11e4d4f937f45f71b7cb4@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/wifi-iwlwifi-mvm-don-t-dump-the-firmware-state-upon-.patch b/queue-6.13/wifi-iwlwifi-mvm-don-t-dump-the-firmware-state-upon-.patch new file mode 100644 index 0000000000..0e7518070b --- /dev/null +++ b/queue-6.13/wifi-iwlwifi-mvm-don-t-dump-the-firmware-state-upon-.patch @@ -0,0 +1,167 @@ +From 244ba27c92af735706ab8207f5487b36339571c7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 9 Feb 2025 14:34:48 +0200 +Subject: wifi: iwlwifi: mvm: don't dump the firmware state upon RFKILL while + suspend + +From: Emmanuel Grumbach + +[ Upstream commit d48ff3ce92259bae7e77732c7cfd7cbc7992c021 ] + +This is not really a firmware error. We need to reload the firmware, but +this doesn't mean that we should consider this as a firmware error. +When the firmware was restarted upon resume, this wasn't felt by the +driver. Now that we keep the firmware running during suspend even if we +don't have wowlan, this started to pop-up. + +Fixes: e8bb19c1d590 ("wifi: iwlwifi: support fast resume") +Signed-off-by: Emmanuel Grumbach +Signed-off-by: Miri Korenblit +Link: https://patch.msgid.link/20250209143303.a10463a40318.I14131781c3124b58e60e1f5e9d793a2bc88b464c@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 77 ++++++++++++++------- + 1 file changed, 51 insertions(+), 26 deletions(-) + +diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +index 33794780d5c9e..5797d28b6a0d1 100644 +--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c ++++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +@@ -3099,8 +3099,14 @@ static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, + ieee80211_resume_disconnect(vif); + } + +-static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm, +- struct ieee80211_vif *vif) ++enum rt_status { ++ FW_ALIVE, ++ FW_NEEDS_RESET, ++ FW_ERROR, ++}; ++ ++static enum rt_status iwl_mvm_check_rt_status(struct iwl_mvm *mvm, ++ struct ieee80211_vif *vif) + { + u32 err_id; + +@@ -3108,29 +3114,35 @@ static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm, + if (iwl_fwrt_read_err_table(mvm->trans, + mvm->trans->dbg.lmac_error_event_table[0], + &err_id)) { +- if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) { +- struct cfg80211_wowlan_wakeup wakeup = { +- .rfkill_release = true, +- }; +- ieee80211_report_wowlan_wakeup(vif, &wakeup, +- GFP_KERNEL); ++ if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN) { ++ IWL_WARN(mvm, "Rfkill was toggled during suspend\n"); ++ if (vif) { ++ struct cfg80211_wowlan_wakeup wakeup = { ++ .rfkill_release = true, ++ }; ++ ++ ieee80211_report_wowlan_wakeup(vif, &wakeup, ++ GFP_KERNEL); ++ } ++ ++ return FW_NEEDS_RESET; + } +- return true; ++ return FW_ERROR; + } + + /* check if we have lmac2 set and check for error */ + if (iwl_fwrt_read_err_table(mvm->trans, + mvm->trans->dbg.lmac_error_event_table[1], + NULL)) +- return true; ++ return FW_ERROR; + + /* check for umac error */ + if (iwl_fwrt_read_err_table(mvm->trans, + mvm->trans->dbg.umac_error_event_table, + NULL)) +- return true; ++ return FW_ERROR; + +- return false; ++ return FW_ALIVE; + } + + /* +@@ -3499,6 +3511,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) + bool d0i3_first = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_D0I3_END_FIRST); + bool resume_notif_based = iwl_mvm_d3_resume_notif_based(mvm); ++ enum rt_status rt_status; + bool keep = false; + + mutex_lock(&mvm->mutex); +@@ -3522,14 +3535,19 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) + + iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); + +- if (iwl_mvm_check_rt_status(mvm, vif)) { +- IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); ++ rt_status = iwl_mvm_check_rt_status(mvm, vif); ++ if (rt_status != FW_ALIVE) { + set_bit(STATUS_FW_ERROR, &mvm->trans->status); +- iwl_mvm_dump_nic_error_log(mvm); +- iwl_dbg_tlv_time_point(&mvm->fwrt, +- IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); +- iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert, +- false, 0); ++ if (rt_status == FW_ERROR) { ++ IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); ++ iwl_mvm_dump_nic_error_log(mvm); ++ iwl_dbg_tlv_time_point(&mvm->fwrt, ++ IWL_FW_INI_TIME_POINT_FW_ASSERT, ++ NULL); ++ iwl_fw_dbg_collect_desc(&mvm->fwrt, ++ &iwl_dump_desc_assert, ++ false, 0); ++ } + ret = 1; + goto err; + } +@@ -3686,6 +3704,7 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm) + .notif_expected = + IWL_D3_NOTIF_D3_END_NOTIF, + }; ++ enum rt_status rt_status; + int ret; + + lockdep_assert_held(&mvm->mutex); +@@ -3695,14 +3714,20 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm) + mvm->last_reset_or_resume_time_jiffies = jiffies; + iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); + +- if (iwl_mvm_check_rt_status(mvm, NULL)) { +- IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); ++ rt_status = iwl_mvm_check_rt_status(mvm, NULL); ++ if (rt_status != FW_ALIVE) { + set_bit(STATUS_FW_ERROR, &mvm->trans->status); +- iwl_mvm_dump_nic_error_log(mvm); +- iwl_dbg_tlv_time_point(&mvm->fwrt, +- IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); +- iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert, +- false, 0); ++ if (rt_status == FW_ERROR) { ++ IWL_ERR(mvm, ++ "iwl_mvm_check_rt_status failed, device is gone during suspend\n"); ++ iwl_mvm_dump_nic_error_log(mvm); ++ iwl_dbg_tlv_time_point(&mvm->fwrt, ++ IWL_FW_INI_TIME_POINT_FW_ASSERT, ++ NULL); ++ iwl_fw_dbg_collect_desc(&mvm->fwrt, ++ &iwl_dump_desc_assert, ++ false, 0); ++ } + mvm->trans->state = IWL_TRANS_NO_FW; + ret = -ENODEV; + +-- +2.39.5 + diff --git a/queue-6.13/wifi-iwlwifi-mvm-don-t-try-to-talk-to-a-dead-firmwar.patch b/queue-6.13/wifi-iwlwifi-mvm-don-t-try-to-talk-to-a-dead-firmwar.patch new file mode 100644 index 0000000000..ac4256bbfe --- /dev/null +++ b/queue-6.13/wifi-iwlwifi-mvm-don-t-try-to-talk-to-a-dead-firmwar.patch @@ -0,0 +1,53 @@ +From 169fd9c12e2d2fd8c39d34c7992aa57558dad7d2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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: + + ? __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 +Signed-off-by: Miri Korenblit +Link: https://patch.msgid.link/20250209143303.8e1597b62c70.I12ea71dd9b805b095c9fc12a10c9f34a4e801b61@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/wifi-iwlwifi-mvm-log-error-for-failures-after-d3.patch b/queue-6.13/wifi-iwlwifi-mvm-log-error-for-failures-after-d3.patch new file mode 100644 index 0000000000..467d5682ab --- /dev/null +++ b/queue-6.13/wifi-iwlwifi-mvm-log-error-for-failures-after-d3.patch @@ -0,0 +1,51 @@ +From 9f5b5a7a1f2c247cd8772d626a56a48739bcd553 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Dec 2024 17:44:46 +0200 +Subject: wifi: iwlwifi: mvm: log error for failures after D3 + +From: Benjamin Berg + +[ Upstream commit f8f13ea27fffff51ee257171a8604f944c876fd4 ] + +We only logged an error in the fast resume path. However, as the +hardware is being restarted it makes sense to log an error to make it +easier to understand what is happening. + +Add a new error message into the normal resume path and update the +error in the fast resume path to match. + +Signed-off-by: Benjamin Berg +Signed-off-by: Miri Korenblit +Link: https://patch.msgid.link/20241226174257.df1e451d4928.Ibe286bc010ad7fecebba5650097e16ed22a654e4@changeid +Signed-off-by: Johannes Berg +Stable-dep-of: d48ff3ce9225 ("wifi: iwlwifi: mvm: don't dump the firmware state upon RFKILL while suspend") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +index 4d1daff1e070d..33794780d5c9e 100644 +--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c ++++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +@@ -3523,6 +3523,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) + iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); + + if (iwl_mvm_check_rt_status(mvm, vif)) { ++ IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); + set_bit(STATUS_FW_ERROR, &mvm->trans->status); + iwl_mvm_dump_nic_error_log(mvm); + iwl_dbg_tlv_time_point(&mvm->fwrt, +@@ -3695,8 +3696,7 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm) + iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); + + if (iwl_mvm_check_rt_status(mvm, NULL)) { +- IWL_ERR(mvm, +- "iwl_mvm_check_rt_status failed, device is gone during suspend\n"); ++ IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); + set_bit(STATUS_FW_ERROR, &mvm->trans->status); + iwl_mvm_dump_nic_error_log(mvm); + iwl_dbg_tlv_time_point(&mvm->fwrt, +-- +2.39.5 + diff --git a/queue-6.13/wifi-mac80211-fix-mle-non-inheritance-parsing.patch b/queue-6.13/wifi-mac80211-fix-mle-non-inheritance-parsing.patch new file mode 100644 index 0000000000..cba7ec2915 --- /dev/null +++ b/queue-6.13/wifi-mac80211-fix-mle-non-inheritance-parsing.patch @@ -0,0 +1,265 @@ +From c60fb5bcd7db88ce8b26ad5d4845057ee9852c5c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 21 Feb 2025 11:24:50 +0100 +Subject: wifi: mac80211: fix MLE non-inheritance parsing + +From: Johannes Berg + +[ 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 +Reviewed-by: Miriam Rachel Korenblit +Link: https://patch.msgid.link/20250221112451.b46d42f45b66.If5b95dc3c80208e0c62d8895fb6152aa54b6620b@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + 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 61c318f5239fa..0e3db0c2920bc 100644 +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -4825,6 +4825,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 + diff --git a/queue-6.13/wifi-mac80211-fix-vendor-specific-inheritance.patch b/queue-6.13/wifi-mac80211-fix-vendor-specific-inheritance.patch new file mode 100644 index 0000000000..a295bc5dee --- /dev/null +++ b/queue-6.13/wifi-mac80211-fix-vendor-specific-inheritance.patch @@ -0,0 +1,68 @@ +From 0182b2d363f5525363f365718a997e6a435b0d92 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 21 Feb 2025 11:24:51 +0100 +Subject: wifi: mac80211: fix vendor-specific inheritance + +From: Johannes Berg + +[ 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 +Reviewed-by: Miriam Rachel Korenblit +Link: https://patch.msgid.link/20250221112451.fd71e5268840.I9db3e6a3367e6ff38d052d07dc07005f0dd3bd5c@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.13/wifi-mac80211-support-parsing-epcs-ml-element.patch b/queue-6.13/wifi-mac80211-support-parsing-epcs-ml-element.patch new file mode 100644 index 0000000000..37ef8dafd8 --- /dev/null +++ b/queue-6.13/wifi-mac80211-support-parsing-epcs-ml-element.patch @@ -0,0 +1,109 @@ +From 177c46c46dcf9e0865c6d6a0a4c2082a5ca5de4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jan 2025 16:20:01 +0200 +Subject: wifi: mac80211: Support parsing EPCS ML element + +From: Ilan Peer + +[ 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 +Reviewed-by: Johannes Berg +Signed-off-by: Miri Korenblit +Link: https://patch.msgid.link/20250102161730.5afdf65cff46.I0ffa30b40fbad47bc5b608b5fd46047a8c44e904@changeid +Signed-off-by: Johannes Berg +Stable-dep-of: 99ca2c28e6b6 ("wifi: mac80211: fix MLE non-inheritance parsing") +Signed-off-by: Sasha Levin +--- + 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 9f0db39b28ffc..c39b813a81992 100644 +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -1749,6 +1749,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]; + +@@ -1779,6 +1780,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 + diff --git a/queue-6.13/x86-sgx-fix-size-overflows-in-sgx_encl_create.patch b/queue-6.13/x86-sgx-fix-size-overflows-in-sgx_encl_create.patch new file mode 100644 index 0000000000..2e5407938d --- /dev/null +++ b/queue-6.13/x86-sgx-fix-size-overflows-in-sgx_encl_create.patch @@ -0,0 +1,55 @@ +From e2410f530d4731a10c7b3d55a5e8f21ea050b32d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Mar 2025 07:00:05 +0200 +Subject: x86/sgx: Fix size overflows in sgx_encl_create() + +From: Jarkko Sakkinen + +[ 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 +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Ingo Molnar +Acked-by: Dave Hansen +Cc: Peter Zijlstra +Cc: "H. Peter Anvin" +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 +--- + 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 +