From: Sasha Levin Date: Sat, 1 Aug 2026 22:13:03 +0000 (-0400) Subject: Fixes for all trees X-Git-Tag: v6.6.148~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7e00201606ee37da1687241bd6dbe19729638f6b;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/staging-5.15/gve-fix-rx-queue-stall-on-alloc-failure.patch b/staging-5.15/gve-fix-rx-queue-stall-on-alloc-failure.patch new file mode 100644 index 0000000000..573f4918a8 --- /dev/null +++ b/staging-5.15/gve-fix-rx-queue-stall-on-alloc-failure.patch @@ -0,0 +1,166 @@ +From ab971037b7cee0a2ded3750a2eabe64334fde3b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Jul 2026 22:43:37 -0700 +Subject: gve: fix Rx queue stall on alloc failure + +From: Eddie Phillips + +commit b65352a1bac64442ad95e64f385b40ccb9f1b0db upstream. + +When the system is under extreme memory pressure, page allocations can +fail during the Rx buffer refill loop. If the number of buffers posted +to hardware falls below a critical low threshold and the refill loop +exits due to allocation failures, the queue can stall: + +1. The device drops incoming packets because there are no descriptors. +2. Since no packets are processed, no Rx completions are generated. +3. Because no completions occur, NAPI is never scheduled, preventing + the refill loop from running again even after memory is freed. + +This results in a permanent queue stall. + +Resolve this by introducing a starvation recovery timer for each Rx queue. +If the number of buffers posted to hardware falls below a critical low +threshold, start a timer to periodically reschedule NAPI. Once NAPI runs +and successfully refills the queue above the threshold, the timer is +not rescheduled. + +The threshold is set to 32 because a single maximum-sized Receive Segment +Coalescing (RSC) packet can consume up to 19 descriptors in the Rx path. +Lower thresholds (such as 8 or 16) would be insufficient to process a +complete maximum-sized RSC packet, risking packet drops or unexpected +hardware behavior under memory pressure. Setting the threshold to 32 +guarantees a safe margin to handle at least one full RSC packet. + +Cc: stable@vger.kernel.org +Fixes: 9b8dd5e5ea48 ("gve: DQO: Add RX path") +Reviewed-by: Jordan Rhee +Signed-off-by: Eddie Phillips +Signed-off-by: Harshitha Ramamurthy +Reviewed-by: Przemek Kitszel +Link: https://patch.msgid.link/20260709211906.3322883-1-hramamurthy@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/google/gve/gve.h | 3 ++ + drivers/net/ethernet/google/gve/gve_main.c | 3 ++ + drivers/net/ethernet/google/gve/gve_rx_dqo.c | 33 ++++++++++++++++++++ + 3 files changed, 39 insertions(+) + +diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h +index 822bdaff66f6..75773524ba81 100644 +--- a/drivers/net/ethernet/google/gve/gve.h ++++ b/drivers/net/ethernet/google/gve/gve.h +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + + #include "gve_desc.h" +@@ -35,6 +36,7 @@ + + /* Interval to schedule a stats report update, 20000ms. */ + #define GVE_STATS_REPORT_TIMER_PERIOD 20000 ++#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */ + + /* Numbers of NIC tx/rx stats in stats report. */ + #define NIC_TX_STATS_REPORT_NUM 0 +@@ -218,6 +220,7 @@ struct gve_rx_ring { + struct u64_stats_sync statss; /* sync stats for 32bit archs */ + + struct gve_rx_ctx ctx; /* Info for packet currently being processed in this ring. */ ++ struct timer_list starvation_timer; /* for queue starvation recovery */ + }; + + /* A TX desc ring entry */ +diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c +index a8fb51e77fea..38333731f5d0 100644 +--- a/drivers/net/ethernet/google/gve/gve_main.c ++++ b/drivers/net/ethernet/google/gve/gve_main.c +@@ -508,6 +508,9 @@ static void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) + { + struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; + ++ if (block->rx && !gve_is_gqi(priv)) ++ timer_shutdown_sync(&block->rx->starvation_timer); ++ + netif_napi_del(&block->napi); + } + +diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c +index 8756f9cbd631..c9bc85293c27 100644 +--- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c ++++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c +@@ -16,6 +16,16 @@ + #include + #include + ++static void gve_rx_starvation_timer(struct timer_list *t) ++{ ++ struct gve_rx_ring *rx = from_timer(rx, t, starvation_timer); ++ struct gve_priv *priv = rx->gve; ++ struct gve_notify_block *block; ++ ++ block = &priv->ntfy_blocks[rx->ntfy_id]; ++ napi_schedule(&block->napi); ++} ++ + static int gve_buf_ref_cnt(struct gve_rx_buf_state_dqo *bs) + { + return page_count(bs->page_info.page) - bs->page_info.pagecnt_bias; +@@ -185,6 +195,7 @@ static void gve_rx_free_ring_dqo(struct gve_priv *priv, int idx) + completion_queue_slots = rx->dqo.complq.mask + 1; + buffer_queue_slots = rx->dqo.bufq.mask + 1; + ++ timer_shutdown_sync(&rx->starvation_timer); + gve_rx_remove_from_block(priv, idx); + + if (rx->q_resources) { +@@ -237,6 +248,7 @@ static int gve_rx_alloc_ring_dqo(struct gve_priv *priv, int idx) + memset(rx, 0, sizeof(*rx)); + rx->gve = priv; + rx->q_num = idx; ++ timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0); + rx->dqo.bufq.mask = buffer_queue_slots - 1; + rx->dqo.complq.num_free_slots = completion_queue_slots; + rx->dqo.complq.mask = completion_queue_slots - 1; +@@ -334,6 +346,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) + struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq; + struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq; + struct gve_priv *priv = rx->gve; ++ u32 num_bufs_avail_to_hw; + u32 num_avail_slots; + u32 num_full_slots; + u32 num_posted = 0; +@@ -374,6 +387,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) + } + + rx->fill_cnt += num_posted; ++ ++ /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors ++ * visible to the hardware, the hardware is in danger of starving ++ * and cannot trigger interrupts. ++ * ++ * We use a threshold of 32 because a single maximum-sized RSC ++ * packet can consume up to 19 descriptors in the Rx path. Lower ++ * thresholds (e.g., 8 or 16) would be unsafe as they could cause ++ * the device to drop/stall on a maximum-sized RSC packet. ++ * ++ * Start the timer to periodically reschedule NAPI and recover. ++ */ ++ num_bufs_avail_to_hw = ++ ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) - ++ bufq->head) & bufq->mask; ++ ++ if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) { ++ mod_timer(&rx->starvation_timer, ++ jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS)); ++ } + } + + static void gve_try_recycle_buf(struct gve_priv *priv, struct gve_rx_ring *rx, +-- +2.53.0 + diff --git a/staging-5.15/series b/staging-5.15/series index a83ad165d1..0ae7b0ed9f 100644 --- a/staging-5.15/series +++ b/staging-5.15/series @@ -1,3 +1,4 @@ net-mpls-initialize-rtm_tos-in-mpls_getroute.patch media-uvcvideo-implement-dual-stream-quirk-to-fix-lo.patch media-uvcvideo-fix-sequence-number-when-no-eof.patch +gve-fix-rx-queue-stall-on-alloc-failure.patch diff --git a/staging-6.1/gve-fix-rx-queue-stall-on-alloc-failure.patch b/staging-6.1/gve-fix-rx-queue-stall-on-alloc-failure.patch new file mode 100644 index 0000000000..335b467b4c --- /dev/null +++ b/staging-6.1/gve-fix-rx-queue-stall-on-alloc-failure.patch @@ -0,0 +1,166 @@ +From 1afcce56562944eaaa6e3b9b9ec127195df91072 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 31 Jul 2026 10:52:09 -0700 +Subject: gve: fix Rx queue stall on alloc failure + +From: Eddie Phillips + +commit b65352a1bac64442ad95e64f385b40ccb9f1b0db upstream. + +When the system is under extreme memory pressure, page allocations can +fail during the Rx buffer refill loop. If the number of buffers posted +to hardware falls below a critical low threshold and the refill loop +exits due to allocation failures, the queue can stall: + +1. The device drops incoming packets because there are no descriptors. +2. Since no packets are processed, no Rx completions are generated. +3. Because no completions occur, NAPI is never scheduled, preventing + the refill loop from running again even after memory is freed. + +This results in a permanent queue stall. + +Resolve this by introducing a starvation recovery timer for each Rx queue. +If the number of buffers posted to hardware falls below a critical low +threshold, start a timer to periodically reschedule NAPI. Once NAPI runs +and successfully refills the queue above the threshold, the timer is +not rescheduled. + +The threshold is set to 32 because a single maximum-sized Receive Segment +Coalescing (RSC) packet can consume up to 19 descriptors in the Rx path. +Lower thresholds (such as 8 or 16) would be insufficient to process a +complete maximum-sized RSC packet, risking packet drops or unexpected +hardware behavior under memory pressure. Setting the threshold to 32 +guarantees a safe margin to handle at least one full RSC packet. + +Cc: stable@vger.kernel.org +Fixes: 9b8dd5e5ea48 ("gve: DQO: Add RX path") +Reviewed-by: Jordan Rhee +Signed-off-by: Eddie Phillips +Signed-off-by: Harshitha Ramamurthy +Reviewed-by: Przemek Kitszel +Link: https://patch.msgid.link/20260709211906.3322883-1-hramamurthy@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/google/gve/gve.h | 3 ++ + drivers/net/ethernet/google/gve/gve_main.c | 3 ++ + drivers/net/ethernet/google/gve/gve_rx_dqo.c | 33 ++++++++++++++++++++ + 3 files changed, 39 insertions(+) + +diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h +index c5e1312b9283..7de492b0d361 100644 +--- a/drivers/net/ethernet/google/gve/gve.h ++++ b/drivers/net/ethernet/google/gve/gve.h +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + + #include "gve_desc.h" +@@ -35,6 +36,7 @@ + + /* Interval to schedule a stats report update, 20000ms. */ + #define GVE_STATS_REPORT_TIMER_PERIOD 20000 ++#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */ + + /* Numbers of NIC tx/rx stats in stats report. */ + #define NIC_TX_STATS_REPORT_NUM 0 +@@ -226,6 +228,7 @@ struct gve_rx_ring { + struct u64_stats_sync statss; /* sync stats for 32bit archs */ + + struct gve_rx_ctx ctx; /* Info for packet currently being processed in this ring. */ ++ struct timer_list starvation_timer; /* for queue starvation recovery */ + }; + + /* A TX desc ring entry */ +diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c +index 209e9526a6fd..fc2a516a3bee 100644 +--- a/drivers/net/ethernet/google/gve/gve_main.c ++++ b/drivers/net/ethernet/google/gve/gve_main.c +@@ -529,6 +529,9 @@ static void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) + { + struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; + ++ if (block->rx && !gve_is_gqi(priv)) ++ timer_shutdown_sync(&block->rx->starvation_timer); ++ + netif_napi_del(&block->napi); + disable_irq(block->irq); + } +diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c +index 0a36b284de10..adae0f5181ea 100644 +--- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c ++++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c +@@ -16,6 +16,16 @@ + #include + #include + ++static void gve_rx_starvation_timer(struct timer_list *t) ++{ ++ struct gve_rx_ring *rx = from_timer(rx, t, starvation_timer); ++ struct gve_priv *priv = rx->gve; ++ struct gve_notify_block *block; ++ ++ block = &priv->ntfy_blocks[rx->ntfy_id]; ++ napi_schedule(&block->napi); ++} ++ + static int gve_buf_ref_cnt(struct gve_rx_buf_state_dqo *bs) + { + return page_count(bs->page_info.page) - bs->page_info.pagecnt_bias; +@@ -185,6 +195,7 @@ static void gve_rx_free_ring_dqo(struct gve_priv *priv, int idx) + completion_queue_slots = rx->dqo.complq.mask + 1; + buffer_queue_slots = rx->dqo.bufq.mask + 1; + ++ timer_shutdown_sync(&rx->starvation_timer); + gve_rx_remove_from_block(priv, idx); + + if (rx->q_resources) { +@@ -237,6 +248,7 @@ static int gve_rx_alloc_ring_dqo(struct gve_priv *priv, int idx) + memset(rx, 0, sizeof(*rx)); + rx->gve = priv; + rx->q_num = idx; ++ timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0); + rx->dqo.bufq.mask = buffer_queue_slots - 1; + rx->dqo.complq.num_free_slots = completion_queue_slots; + rx->dqo.complq.mask = completion_queue_slots - 1; +@@ -337,6 +349,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) + u32 num_avail_slots; + u32 num_full_slots; + u32 num_posted = 0; ++ u32 num_bufs_avail_to_hw; + + num_full_slots = (bufq->tail - bufq->head) & bufq->mask; + num_avail_slots = bufq->mask - num_full_slots; +@@ -374,6 +387,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) + } + + rx->fill_cnt += num_posted; ++ ++ /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors ++ * visible to the hardware, the hardware is in danger of starving ++ * and cannot trigger interrupts. ++ * ++ * We use a threshold of 32 because a single maximum-sized RSC ++ * packet can consume up to 19 descriptors in the Rx path. Lower ++ * thresholds (e.g., 8 or 16) would be unsafe as they could cause ++ * the device to drop/stall on a maximum-sized RSC packet. ++ * ++ * Start the timer to periodically reschedule NAPI and recover. ++ */ ++ num_bufs_avail_to_hw = ++ ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) - ++ bufq->head) & bufq->mask; ++ ++ if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) { ++ mod_timer(&rx->starvation_timer, ++ jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS)); ++ } + } + + static void gve_try_recycle_buf(struct gve_priv *priv, struct gve_rx_ring *rx, +-- +2.53.0 + diff --git a/staging-6.1/series b/staging-6.1/series index ca65a4d42c..0022a102cd 100644 --- a/staging-6.1/series +++ b/staging-6.1/series @@ -6,3 +6,4 @@ netfilter-br_netfilter-reallocate-headroom-if-necess.patch net-mpls-initialize-rtm_tos-in-mpls_getroute.patch media-uvcvideo-implement-dual-stream-quirk-to-fix-lo.patch media-uvcvideo-fix-sequence-number-when-no-eof.patch +gve-fix-rx-queue-stall-on-alloc-failure.patch diff --git a/staging-6.18/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch b/staging-6.18/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch new file mode 100644 index 0000000000..edd4947d02 --- /dev/null +++ b/staging-6.18/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch @@ -0,0 +1,35 @@ +From 7713f5837451d137e37294c7dfca649e26cbe737 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 May 2026 17:41:01 +0200 +Subject: ALSA: hda/realtek: add quirk for HP Dragonfly Folio G3 2-in-1 + +From: Fabian Lippold + +[ Upstream commit 0a10faad5ca58332ad70f7663ba82611f4daf736 ] + +Add PCI quirk for HP Dragonfly Folio G3 (PCI ID 103c:8a06) to select the +CS35L41 SPI4 & GPIO LED fixup variant. + +Signed-off-by: Fabian Lippold +Link: https://patch.msgid.link/20260526154418.1850568-3-fabianlippold1184@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/realtek/alc269.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +index 40a75d72f712..d84d17c6e049 100644 +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -6839,6 +6839,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), + SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), ++ SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a1f, "HP Laptop 14s-dr5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), +-- +2.53.0 + diff --git a/staging-6.18/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch-17022 b/staging-6.18/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch-17022 new file mode 100644 index 0000000000..20977aa7c6 --- /dev/null +++ b/staging-6.18/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch-17022 @@ -0,0 +1,49 @@ +From 133717389d9cfe70dca2e8743bd9c388eccdc630 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 09:19:21 +0000 +Subject: ALSA: hda/realtek: Add quirk for HP Dragonfly Folio G3 2-in-1 + (103c:8a05) + +From: Michael Diesen + +[ Upstream commit bed0c8084044364f5ac3f3e89e1bbad423f6b0d4 ] + +The HP Dragonfly Folio G3 2-in-1 also ships with PCI SSID 103c:8a05. +On this unit the ALC245 codec reports subsystem id 103c:8a06 - the SSID +that is already covered by commit 0a10faad5ca5 ("ALSA: hda/realtek: add +quirk for HP Dragonfly Folio G3 2-in-1") - while the PCI SSID that +SND_PCI_QUIRK matches against is 103c:8a05: + + snd_hda_codec_alc269 ehdaudio0D0: ALC245: picked fixup for PCI SSID 103c:8a05 + cs35l41-hda spi1-CSC3551:00-cs35l41-hda.0: CS35L41 Bound - SSID: 103C8A06 + +The existing entry therefore never applies here, the four CS35L41 +amplifiers on SPI are not registered and the internal speakers stay +silent. + +Add the same fixup that the 8a06 entry uses: the four amplifiers bind +and the speaker mute LED (codec GPIO 0x04) works. + +Signed-off-by: Michael Diesen +Link: https://patch.msgid.link/20260727091920.4634-1-michael.diesen@posteo.de +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/realtek/alc269.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +index d84d17c6e049..538395307912 100644 +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -6839,6 +6839,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), + SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), ++ SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a1f, "HP Laptop 14s-dr5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), +-- +2.53.0 + diff --git a/staging-6.18/series b/staging-6.18/series index cc1362d923..aa107a4745 100644 --- a/staging-6.18/series +++ b/staging-6.18/series @@ -4,3 +4,5 @@ kunit-tool-terminate-kernel-under-test-on-sigint.patch netfilter-br_netfilter-reallocate-headroom-if-necess.patch net-mpls-initialize-rtm_tos-in-mpls_getroute.patch drm-gpusvm-publish-dpagemap-early-to-avoid-device-ma.patch +alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch +alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch-17022 diff --git a/staging-6.6/gve-fix-rx-queue-stall-on-alloc-failure.patch b/staging-6.6/gve-fix-rx-queue-stall-on-alloc-failure.patch new file mode 100644 index 0000000000..704fc6dee8 --- /dev/null +++ b/staging-6.6/gve-fix-rx-queue-stall-on-alloc-failure.patch @@ -0,0 +1,166 @@ +From 493ece9aff57973d58c82f54393c5450d235c287 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 31 Jul 2026 08:59:25 -0700 +Subject: gve: fix Rx queue stall on alloc failure + +From: Eddie Phillips + +commit b65352a1bac64442ad95e64f385b40ccb9f1b0db upstream. + +When the system is under extreme memory pressure, page allocations can +fail during the Rx buffer refill loop. If the number of buffers posted +to hardware falls below a critical low threshold and the refill loop +exits due to allocation failures, the queue can stall: + +1. The device drops incoming packets because there are no descriptors. +2. Since no packets are processed, no Rx completions are generated. +3. Because no completions occur, NAPI is never scheduled, preventing + the refill loop from running again even after memory is freed. + +This results in a permanent queue stall. + +Resolve this by introducing a starvation recovery timer for each Rx queue. +If the number of buffers posted to hardware falls below a critical low +threshold, start a timer to periodically reschedule NAPI. Once NAPI runs +and successfully refills the queue above the threshold, the timer is +not rescheduled. + +The threshold is set to 32 because a single maximum-sized Receive Segment +Coalescing (RSC) packet can consume up to 19 descriptors in the Rx path. +Lower thresholds (such as 8 or 16) would be insufficient to process a +complete maximum-sized RSC packet, risking packet drops or unexpected +hardware behavior under memory pressure. Setting the threshold to 32 +guarantees a safe margin to handle at least one full RSC packet. + +Cc: stable@vger.kernel.org +Fixes: 9b8dd5e5ea48 ("gve: DQO: Add RX path") +Reviewed-by: Jordan Rhee +Signed-off-by: Eddie Phillips +Signed-off-by: Harshitha Ramamurthy +Reviewed-by: Przemek Kitszel +Link: https://patch.msgid.link/20260709211906.3322883-1-hramamurthy@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/google/gve/gve.h | 3 ++ + drivers/net/ethernet/google/gve/gve_main.c | 3 ++ + drivers/net/ethernet/google/gve/gve_rx_dqo.c | 33 ++++++++++++++++++++ + 3 files changed, 39 insertions(+) + +diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h +index f6e43cf96a46..84fb3deca21f 100644 +--- a/drivers/net/ethernet/google/gve/gve.h ++++ b/drivers/net/ethernet/google/gve/gve.h +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -36,6 +37,7 @@ + + /* Interval to schedule a stats report update, 20000ms. */ + #define GVE_STATS_REPORT_TIMER_PERIOD 20000 ++#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */ + + /* Numbers of NIC tx/rx stats in stats report. */ + #define NIC_TX_STATS_REPORT_NUM 0 +@@ -281,6 +283,7 @@ struct gve_rx_ring { + struct xdp_rxq_info xsk_rxq; + struct xsk_buff_pool *xsk_pool; + struct page_frag_cache page_cache; /* Page cache to allocate XDP frames */ ++ struct timer_list starvation_timer; /* for queue starvation recovery */ + }; + + /* A TX desc ring entry */ +diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c +index 08f444ee10c7..9a287a376479 100644 +--- a/drivers/net/ethernet/google/gve/gve_main.c ++++ b/drivers/net/ethernet/google/gve/gve_main.c +@@ -583,6 +583,9 @@ static void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) + { + struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; + ++ if (block->rx && !gve_is_gqi(priv)) ++ timer_shutdown_sync(&block->rx->starvation_timer); ++ + netif_napi_del(&block->napi); + disable_irq(block->irq); + } +diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c +index 3d60ea25711f..12520c9813ac 100644 +--- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c ++++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c +@@ -16,6 +16,16 @@ + #include + #include + ++static void gve_rx_starvation_timer(struct timer_list *t) ++{ ++ struct gve_rx_ring *rx = from_timer(rx, t, starvation_timer); ++ struct gve_priv *priv = rx->gve; ++ struct gve_notify_block *block; ++ ++ block = &priv->ntfy_blocks[rx->ntfy_id]; ++ napi_schedule(&block->napi); ++} ++ + static int gve_buf_ref_cnt(struct gve_rx_buf_state_dqo *bs) + { + return page_count(bs->page_info.page) - bs->page_info.pagecnt_bias; +@@ -211,6 +221,7 @@ static void gve_rx_free_ring_dqo(struct gve_priv *priv, int idx) + completion_queue_slots = rx->dqo.complq.mask + 1; + buffer_queue_slots = rx->dqo.bufq.mask + 1; + ++ timer_shutdown_sync(&rx->starvation_timer); + gve_rx_remove_from_block(priv, idx); + + if (rx->q_resources) { +@@ -268,6 +279,7 @@ static int gve_rx_alloc_ring_dqo(struct gve_priv *priv, int idx) + memset(rx, 0, sizeof(*rx)); + rx->gve = priv; + rx->q_num = idx; ++ timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0); + rx->dqo.bufq.mask = buffer_queue_slots - 1; + rx->dqo.complq.num_free_slots = completion_queue_slots; + rx->dqo.complq.mask = completion_queue_slots - 1; +@@ -374,6 +386,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) + struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq; + struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq; + struct gve_priv *priv = rx->gve; ++ u32 num_bufs_avail_to_hw; + u32 num_avail_slots; + u32 num_full_slots; + u32 num_posted = 0; +@@ -414,6 +427,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) + } + + rx->fill_cnt += num_posted; ++ ++ /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors ++ * visible to the hardware, the hardware is in danger of starving ++ * and cannot trigger interrupts. ++ * ++ * We use a threshold of 32 because a single maximum-sized RSC ++ * packet can consume up to 19 descriptors in the Rx path. Lower ++ * thresholds (e.g., 8 or 16) would be unsafe as they could cause ++ * the device to drop/stall on a maximum-sized RSC packet. ++ * ++ * Start the timer to periodically reschedule NAPI and recover. ++ */ ++ num_bufs_avail_to_hw = ++ ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) - ++ bufq->head) & bufq->mask; ++ ++ if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) { ++ mod_timer(&rx->starvation_timer, ++ jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS)); ++ } + } + + static void gve_try_recycle_buf(struct gve_priv *priv, struct gve_rx_ring *rx, +-- +2.53.0 + diff --git a/staging-6.6/series b/staging-6.6/series index 7464fefac5..e69d2a5006 100644 --- a/staging-6.6/series +++ b/staging-6.6/series @@ -1,3 +1,4 @@ netfilter-nf_conntrack_expect-restore-helper-propaga.patch netfilter-br_netfilter-reallocate-headroom-if-necess.patch net-mpls-initialize-rtm_tos-in-mpls_getroute.patch +gve-fix-rx-queue-stall-on-alloc-failure.patch diff --git a/staging-7.1/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch b/staging-7.1/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch new file mode 100644 index 0000000000..2013d3cc60 --- /dev/null +++ b/staging-7.1/alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch @@ -0,0 +1,49 @@ +From 296df1e1a2e23b33d6192a264a436306790423e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 09:19:21 +0000 +Subject: ALSA: hda/realtek: Add quirk for HP Dragonfly Folio G3 2-in-1 + (103c:8a05) + +From: Michael Diesen + +[ Upstream commit bed0c8084044364f5ac3f3e89e1bbad423f6b0d4 ] + +The HP Dragonfly Folio G3 2-in-1 also ships with PCI SSID 103c:8a05. +On this unit the ALC245 codec reports subsystem id 103c:8a06 - the SSID +that is already covered by commit 0a10faad5ca5 ("ALSA: hda/realtek: add +quirk for HP Dragonfly Folio G3 2-in-1") - while the PCI SSID that +SND_PCI_QUIRK matches against is 103c:8a05: + + snd_hda_codec_alc269 ehdaudio0D0: ALC245: picked fixup for PCI SSID 103c:8a05 + cs35l41-hda spi1-CSC3551:00-cs35l41-hda.0: CS35L41 Bound - SSID: 103C8A06 + +The existing entry therefore never applies here, the four CS35L41 +amplifiers on SPI are not registered and the internal speakers stay +silent. + +Add the same fixup that the 8a06 entry uses: the four amplifiers bind +and the speaker mute LED (codec GPIO 0x04) works. + +Signed-off-by: Michael Diesen +Link: https://patch.msgid.link/20260727091920.4634-1-michael.diesen@posteo.de +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/realtek/alc269.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +index 427caf08145d..cfc313f49381 100644 +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -7086,6 +7086,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), + SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), ++ SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a1f, "HP Laptop 14s-dr5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), +-- +2.53.0 + diff --git a/staging-7.1/lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch b/staging-7.1/lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch new file mode 100644 index 0000000000..ed0fdb0c08 --- /dev/null +++ b/staging-7.1/lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch @@ -0,0 +1,76 @@ +From 205a324e7b7a54557ae539d777d68bfad465e1bd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 1 Aug 2026 12:25:32 +0000 +Subject: lib/alloc_tag: introduce mem_alloc_profiling_permanently_disabled() + +From: Harry Yoo (Oracle) + +commit a37b0066a10aabf3c968b4566706fb866eaf9a85 upstream. + +mem_alloc_profiling_enabled() tells whether memalloc profiling is +currently enabled. However, even when this function returns false, +it can be enabled later. + +However, this is not enough. Some optimizations can be applied only when +memalloc profiling is permanently disabled. For example, to skip the +creation of KMALLOC_NO_OBJ_EXT caches at boot time, mem_profiling must +be set to "never", "0" w/ debugging on, or have been shutdown so that +it can no longer be enabled. + +Introduce mem_alloc_profiling_permanently_disabled() for this purpose. + +Signed-off-by: Harry Yoo (Oracle) +Acked-by: Suren Baghdasaryan +Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-3-47c7bd138de7@kernel.org +Signed-off-by: Vlastimil Babka (SUSE) +Signed-off-by: Harry Yoo +Signed-off-by: Sasha Levin +--- + include/linux/alloc_tag.h | 3 +++ + lib/alloc_tag.c | 9 +++++++++ + 2 files changed, 12 insertions(+) + +diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h +index 02de2ede560f..7e7cdc7612be 100644 +--- a/include/linux/alloc_tag.h ++++ b/include/linux/alloc_tag.h +@@ -134,6 +134,8 @@ static inline bool mem_alloc_profiling_enabled(void) + &mem_alloc_profiling_key); + } + ++bool mem_alloc_profiling_permanently_disabled(void); ++ + static inline struct alloc_tag_counters alloc_tag_read(struct alloc_tag *tag) + { + struct alloc_tag_counters v = { 0, 0 }; +@@ -239,6 +241,7 @@ static inline bool alloc_tag_is_inaccurate(struct alloc_tag *tag) + + #define DEFINE_ALLOC_TAG(_alloc_tag) + static inline bool mem_alloc_profiling_enabled(void) { return false; } ++static inline bool mem_alloc_profiling_permanently_disabled(void) { return true; } + static inline void alloc_tag_add(union codetag_ref *ref, struct alloc_tag *tag, + size_t bytes) {} + static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) {} +diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c +index a9ab88f416b9..e3a923594604 100644 +--- a/lib/alloc_tag.c ++++ b/lib/alloc_tag.c +@@ -26,6 +26,15 @@ static bool mem_profiling_support = true; + static bool mem_profiling_support; + #endif + ++/* ++ * Memory allocation profiling is permanently disabled and cannot be enabled. ++ * Must be called after setup_early_mem_profiling(). ++ */ ++bool mem_alloc_profiling_permanently_disabled(void) ++{ ++ return !mem_profiling_support; ++} ++ + static struct codetag_type *alloc_tag_cttype; + + #ifdef CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU +-- +2.53.0 + diff --git a/staging-7.1/mm-slab-decouple-slab_no_sheaves-from-slab_no_obj_ex.patch b/staging-7.1/mm-slab-decouple-slab_no_sheaves-from-slab_no_obj_ex.patch new file mode 100644 index 0000000000..0b00573beb --- /dev/null +++ b/staging-7.1/mm-slab-decouple-slab_no_sheaves-from-slab_no_obj_ex.patch @@ -0,0 +1,107 @@ +From 6cc36babd72119bf1848f25d338ec8cc3f59b392 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 1 Aug 2026 12:25:31 +0000 +Subject: mm/slab: decouple SLAB_NO_SHEAVES from SLAB_NO_OBJ_EXT + +From: Harry Yoo (Oracle) + +commit 982e31382d9a1a3c8c4e6a13702a53711f4efe9f upstream. + +Bootstrap caches are created with SLAB_NO_OBJ_EXT to disallow sheaves +and obj_exts. + +To allow disabling obj_exts while allowing sheaves, decouple +SLAB_NO_SHEAVES from SLAB_NO_OBJ_EXT. Bootstrap caches now have both +SLAB_NO_SHEAVES and SLAB_NO_OBJ_EXT. + +No functional change intended. + +Reviewed-by: Vlastimil Babka (SUSE) +Signed-off-by: Harry Yoo (Oracle) +Reviewed-by: Suren Baghdasaryan +Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-2-47c7bd138de7@kernel.org +Signed-off-by: Vlastimil Babka (SUSE) +Signed-off-by: Harry Yoo +Signed-off-by: Sasha Levin +--- + include/linux/slab.h | 13 +++++++++++-- + mm/slub.c | 10 ++++++---- + 2 files changed, 17 insertions(+), 6 deletions(-) + +diff --git a/include/linux/slab.h b/include/linux/slab.h +index 1a69255bb87f..fda7e24a762b 100644 +--- a/include/linux/slab.h ++++ b/include/linux/slab.h +@@ -58,10 +58,13 @@ enum _slab_flag_bits { + #endif + _SLAB_OBJECT_POISON, + _SLAB_CMPXCHG_DOUBLE, ++#ifdef CONFIG_SLAB_OBJ_EXT + _SLAB_NO_OBJ_EXT, +-#if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT) ++#ifdef CONFIG_64BIT + _SLAB_OBJ_EXT_IN_OBJ, + #endif ++#endif ++ _SLAB_NO_SHEAVES, + _SLAB_FLAGS_LAST_BIT + }; + +@@ -239,8 +242,14 @@ enum _slab_flag_bits { + #endif + #define SLAB_TEMPORARY SLAB_RECLAIM_ACCOUNT /* Objects are short-lived */ + +-/* Slab created using create_boot_cache */ ++/* Slab caches without obj_exts array */ ++#ifdef CONFIG_SLAB_OBJ_EXT + #define SLAB_NO_OBJ_EXT __SLAB_FLAG_BIT(_SLAB_NO_OBJ_EXT) ++#else ++#define SLAB_NO_OBJ_EXT __SLAB_FLAG_UNUSED ++#endif ++ ++#define SLAB_NO_SHEAVES __SLAB_FLAG_BIT(_SLAB_NO_SHEAVES) + + #if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT) + #define SLAB_OBJ_EXT_IN_OBJ __SLAB_FLAG_BIT(_SLAB_OBJ_EXT_IN_OBJ) +diff --git a/mm/slub.c b/mm/slub.c +index 12ace2932927..b0e8c4ae2f37 100644 +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -7724,12 +7724,12 @@ static unsigned int calculate_sheaf_capacity(struct kmem_cache *s, + return 0; + + /* +- * Bootstrap caches can't have sheaves for now (SLAB_NO_OBJ_EXT). ++ * Bootstrap caches can't have sheaves for now (SLAB_NO_SHEAVES). + * SLAB_NOLEAKTRACE caches (e.g., kmemleak's object_cache) must not + * have sheaves to avoid recursion when sheaf allocation triggers + * kmemleak tracking. + */ +- if (s->flags & (SLAB_NO_OBJ_EXT | SLAB_NOLEAKTRACE)) ++ if (s->flags & (SLAB_NO_SHEAVES | SLAB_NOLEAKTRACE)) + return 0; + + /* +@@ -8511,7 +8511,8 @@ void __init kmem_cache_init(void) + + create_boot_cache(kmem_cache_node, "kmem_cache_node", + sizeof(struct kmem_cache_node), +- SLAB_HWCACHE_ALIGN | SLAB_NO_OBJ_EXT, 0, 0); ++ SLAB_HWCACHE_ALIGN | SLAB_NO_SHEAVES | SLAB_NO_OBJ_EXT, ++ 0, 0); + + hotplug_node_notifier(slab_memory_callback, SLAB_CALLBACK_PRI); + +@@ -8521,7 +8522,8 @@ void __init kmem_cache_init(void) + create_boot_cache(kmem_cache, "kmem_cache", + offsetof(struct kmem_cache, per_node) + + nr_node_ids * sizeof(struct kmem_cache_per_node_ptrs), +- SLAB_HWCACHE_ALIGN | SLAB_NO_OBJ_EXT, 0, 0); ++ SLAB_HWCACHE_ALIGN | SLAB_NO_SHEAVES | SLAB_NO_OBJ_EXT, ++ 0, 0); + + kmem_cache = bootstrap(&boot_kmem_cache); + kmem_cache_node = bootstrap(&boot_kmem_cache_node); +-- +2.53.0 + diff --git a/staging-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch b/staging-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch new file mode 100644 index 0000000000..2fb3405c54 --- /dev/null +++ b/staging-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch @@ -0,0 +1,407 @@ +From 11dcf1754cbe34979933bbf03e34e11d16748ce4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 1 Aug 2026 12:25:33 +0000 +Subject: mm/slab: prevent unbounded recursion in free path with new kmalloc + type + +From: Harry Yoo (Oracle) + +commit d9e6a7623938968e3752b67e37eaff097e559a54 upstream. + +Commit 280ea9c3154b ("mm/slab: avoid allocating slabobj_ext array from +its own slab") avoided recursive allocation of obj_exts from kmalloc +caches of the same size, by bumping the obj_exts array's allocation +size whenever the array size equals the size of the object being +allocated. + +However, as reported by Danielle Costantino and Shakeel Butt, +even slabs from kmalloc caches of different sizes can form a cycle +by allocating obj_exts arrays from each other [1]: + + What happened: a KMALLOC_NORMAL slab's obj_exts array (used by + allocation profiling / memcg accounting) is itself kmalloc()'d from a + KMALLOC_NORMAL cache, so the "slab holds another slab's obj_exts array" + relation can form cycles. With sizeof(struct slabobj_ext) == 16 and + the host's geometry: + + - kmalloc-512 has 64 objects/slab -> array is 64*16 == 1024 bytes, + served from kmalloc-1k; + - kmalloc-1k has 32 objects/slab -> array is 32*16 == 512 bytes, + served from kmalloc-512. + + A kmalloc-512 slab and a kmalloc-1k slab therefore hold each other's + obj_exts array. Discarding one frees the other's array, which empties + and discards that slab, which frees the first's array, and so on: + __free_slab() -> free_slab_obj_exts() -> kfree() -> discard_slab() -> + __free_slab() recurses along the cycle until the stack is exhausted. + +With memory allocation profiling, this allows unbounded recursion +in the free path and led to a stack overflow on a production host in +the Meta fleet [1]: + + BUG: TASK stack guard page was hit + Oops: stack guard page + RIP: 0010:kfree+0x8/0x5d0 + Call Trace: + __free_slab+0x66/0xc0 + kfree+0x3f0/0x5d0 + ... ( ~125x __free_slab <-> kfree ) ... + + do_syscall_64 + +It is proposed [1] to resolve this issue by always serving the obj_exts +array allocation from kmalloc caches (or large kmalloc) of sizes larger +than the object size. However, as pointed out by Vlastimil Babka [2], +this can waste an excessive amount of memory as slabs from large +kmalloc sizes (e.g. kmalloc-8k) generally need obj_exts arrays much +smaller than the object size. + +Therefore, rather than bumping the size, let us take a different +approach; disallow formation of cycles between kmalloc types when +allocating obj_exts arrays. Currently, all obj_exts arrays are served +from normal kmalloc caches. Cycles cannot be created if obj_exts arrays +of normal kmalloc caches are served from a special kmalloc type that can +never have obj_exts arrays. + +To achieve this, create a new kmalloc type called KMALLOC_NO_OBJ_EXT. +KMALLOC_NO_OBJ_EXT caches are created with SLAB_NO_OBJ_EXT flag when +either 1) memory allocation profiling is not permanently disabled, +or 2) kmalloc types with a priority higher than KMALLOC_CGROUP are +aliased with KMALLOC_NORMAL. + +Sheaf bootstrapping for KMALLOC_NO_OBJ_EXT caches now must be deferred +because allocation of a barn can trigger obj_exts array allocation of +normal kmalloc caches when the KMALLOC_NO_OBJ_EXT cache for that size +is not ready yet. For simplicity, perform bootstrapping of sheaves for +all kmalloc caches later. + +Introduce a new slab alloc flag, SLAB_ALLOC_NO_OBJ_EXT, to prevent +allocation of obj_exts arrays, and let kmalloc_slab() override the type +to KMALLOC_NO_OBJ_EXT when specified. Note that kmalloc_type() remains +unchanged because kmalloc_flags() bypasses the kmalloc fastpath. + +Do not pass SLAB_ALLOC_NO_RECURSE to kmalloc_flags() in +alloc_slab_obj_exts() and instead use SLAB_ALLOC_NO_OBJ_EXT only when +the objects are allocated from normal kmalloc caches. While this +prevents unbounded recursive allocation of obj_exts, it allows +KMALLOC_NO_OBJ_EXT caches to have sheaves. + +Since sheaf allocations specify SLAB_ALLOC_NO_RECURSE that prevents +allocation of both sheaves and obj_exts arrays, the recursion depth +is bounded. + +obj_exts arrays for non-kmalloc-normal caches can now have a valid tag. +Do not call mark_obj_codetag_empty() when freeing an obj_exts array to +avoid false warnings. KMALLOC_NO_OBJ_EXT don't need this as they never +allocate those arrays. + +Reported-by: Danielle Costantino +Reported-by: Shakeel Butt +Closes: https://lore.kernel.org/linux-mm/20260625230029.703750-1-shakeel.butt@linux.dev [1] +Fixes: 4b8736964640 ("mm/slab: add allocation accounting into slab allocation and free paths") +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/linux-mm/c5c4208d-a6f0-413e-bad9-49be12f12d55@kernel.org [2] +Signed-off-by: Harry Yoo (Oracle) +Reviewed-by: Suren Baghdasaryan +Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-4-47c7bd138de7@kernel.org +Signed-off-by: Vlastimil Babka (SUSE) +[harry@kernel.org: Backport notes: + - Fix a minor conflict due to missing partitioned + kmalloc caches in 7.1. + + - Use __GFP_NO_OBJ_EXT instead of SLAB_ALLOC_NO_OBJ_EXT + since slab's internal alloc_flags do not exist in 7.1. + + - Since there is no way to distinguish between no obj_exts vs. + no recursion in 7.1 due to lack of SLAB_ALLOC_* flags, sheaves for + normal kmalloc caches are allocated from kmalloc-no-objext-* unlike + upstream. + + Also, allocations from kmalloc-no-objext cannot allocate sheaves and + may observe slightly higher contention. Only 7.1 has this problem as + kmalloc caches don't have sheaves in LTS kernels. + + - Apply the __GFP_NO_OBJ_EXT flag to the !allow_spin path in + alloc_slab_obj_exts(). ] +Signed-off-by: Harry Yoo +Signed-off-by: Sasha Levin +--- + include/linux/slab.h | 6 +++ + mm/slab.h | 28 +++++++++++++- + mm/slab_common.c | 13 +++++++ + mm/slub.c | 87 +++++++++++++++----------------------------- + 4 files changed, 75 insertions(+), 59 deletions(-) + +diff --git a/include/linux/slab.h b/include/linux/slab.h +index fda7e24a762b..739f474c5d92 100644 +--- a/include/linux/slab.h ++++ b/include/linux/slab.h +@@ -642,6 +642,9 @@ enum kmalloc_cache_type { + #endif + #ifndef CONFIG_MEMCG + KMALLOC_CGROUP = KMALLOC_NORMAL, ++#endif ++#ifndef CONFIG_SLAB_OBJ_EXT ++ KMALLOC_NO_OBJ_EXT = KMALLOC_NORMAL, + #endif + KMALLOC_RANDOM_START = KMALLOC_NORMAL, + KMALLOC_RANDOM_END = KMALLOC_RANDOM_START + RANDOM_KMALLOC_CACHES_NR, +@@ -655,6 +658,9 @@ enum kmalloc_cache_type { + #endif + #ifdef CONFIG_MEMCG + KMALLOC_CGROUP, ++#endif ++#ifdef CONFIG_SLAB_OBJ_EXT ++ KMALLOC_NO_OBJ_EXT, + #endif + NR_KMALLOC_TYPES + }; +diff --git a/mm/slab.h b/mm/slab.h +index bf2f87acf5e3..50414bd2b919 100644 +--- a/mm/slab.h ++++ b/mm/slab.h +@@ -365,9 +365,13 @@ static inline struct kmem_cache * + kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, unsigned long caller) + { + unsigned int index; ++ enum kmalloc_cache_type type = kmalloc_type(flags, caller); ++ ++ if (flags & __GFP_NO_OBJ_EXT) ++ type = KMALLOC_NO_OBJ_EXT; + + if (!b) +- b = &kmalloc_caches[kmalloc_type(flags, caller)]; ++ b = &kmalloc_caches[type]; + if (size <= 192) + index = kmalloc_size_index[size_index_elem(size)]; + else +@@ -402,7 +406,8 @@ static inline bool is_kmalloc_normal(struct kmem_cache *s) + { + if (!is_kmalloc_cache(s)) + return false; +- return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT)); ++ ++ return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT|SLAB_NO_OBJ_EXT)); + } + + bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj); +@@ -505,6 +510,25 @@ static inline void metadata_access_disable(void) + kasan_enable_current(); + } + ++/* ++ * Return true if KMALLOC_NORMAL caches may need obj_exts arrays. ++ * ++ * Memory allocation profiling requires obj_exts for all caches. ++ * Memcg usually doesn't need them for normal kmalloc caches, but kmalloc types ++ * with a priority higher than KMALLOC_CGROUP can be aliased with KMALLOC_NORMAL. ++ */ ++static inline bool need_kmalloc_no_objext(void) ++{ ++ if (!mem_alloc_profiling_permanently_disabled()) ++ return true; ++ ++ if (!mem_cgroup_kmem_disabled() && ++ (KMALLOC_NORMAL == KMALLOC_RECLAIM)) ++ return true; ++ ++ return false; ++} ++ + #ifdef CONFIG_SLAB_OBJ_EXT + + /* +diff --git a/mm/slab_common.c b/mm/slab_common.c +index 8b661fff5eed..214374233fb8 100644 +--- a/mm/slab_common.c ++++ b/mm/slab_common.c +@@ -843,6 +843,12 @@ EXPORT_SYMBOL(kmalloc_size_roundup); + #define KMALLOC_RANDOM_NAME(N, sz) + #endif + ++#ifdef CONFIG_SLAB_OBJ_EXT ++#define KMALLOC_NO_OBJ_EXT_NAME(sz) .name[KMALLOC_NO_OBJ_EXT] = "kmalloc-no-objext-" #sz, ++#else ++#define KMALLOC_NO_OBJ_EXT_NAME(sz) ++#endif ++ + #define INIT_KMALLOC_INFO(__size, __short_size) \ + { \ + .name[KMALLOC_NORMAL] = "kmalloc-" #__short_size, \ +@@ -850,6 +856,7 @@ EXPORT_SYMBOL(kmalloc_size_roundup); + KMALLOC_CGROUP_NAME(__short_size) \ + KMALLOC_DMA_NAME(__short_size) \ + KMALLOC_RANDOM_NAME(RANDOM_KMALLOC_CACHES_NR, __short_size) \ ++ KMALLOC_NO_OBJ_EXT_NAME(__short_size) \ + .size = __size, \ + } + +@@ -957,6 +964,12 @@ new_kmalloc_cache(int idx, enum kmalloc_cache_type type) + return; + } + flags |= SLAB_ACCOUNT; ++ } else if (IS_ENABLED(CONFIG_SLAB_OBJ_EXT) && type == KMALLOC_NO_OBJ_EXT) { ++ if (!need_kmalloc_no_objext()) { ++ kmalloc_caches[type][idx] = kmalloc_caches[KMALLOC_NORMAL][idx]; ++ return; ++ } ++ flags |= SLAB_NO_OBJ_EXT | SLAB_NO_MERGE; + } else if (IS_ENABLED(CONFIG_ZONE_DMA) && (type == KMALLOC_DMA)) { + flags |= SLAB_CACHE_DMA; + } +diff --git a/mm/slub.c b/mm/slub.c +index b0e8c4ae2f37..b2fdb10b6b65 100644 +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -2106,42 +2106,6 @@ static inline void init_slab_obj_exts(struct slab *slab) + slab->obj_exts = 0; + } + +-/* +- * Calculate the allocation size for slabobj_ext array. +- * +- * When memory allocation profiling is enabled, the obj_exts array +- * could be allocated from the same slab cache it's being allocated for. +- * This would prevent the slab from ever being freed because it would +- * always contain at least one allocated object (its own obj_exts array). +- * +- * To avoid this, increase the allocation size when we detect the array +- * may come from the same cache, forcing it to use a different cache. +- */ +-static inline size_t obj_exts_alloc_size(struct kmem_cache *s, +- struct slab *slab, gfp_t gfp) +-{ +- size_t sz = sizeof(struct slabobj_ext) * slab->objects; +- struct kmem_cache *obj_exts_cache; +- +- if (sz > KMALLOC_MAX_CACHE_SIZE) +- return sz; +- +- if (!is_kmalloc_normal(s)) +- return sz; +- +- obj_exts_cache = kmalloc_slab(sz, NULL, gfp, 0); +- /* +- * We can't simply compare s with obj_exts_cache, because random kmalloc +- * caches have multiple caches per size, selected by caller address. +- * Since caller address may differ between kmalloc_slab() and actual +- * allocation, bump size when sizes are equal. +- */ +- if (s->object_size == obj_exts_cache->object_size) +- return obj_exts_cache->object_size + 1; +- +- return sz; +-} +- + int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, + gfp_t gfp, bool new_slab) + { +@@ -2150,13 +2114,17 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, + unsigned long new_exts; + unsigned long old_exts; + struct slabobj_ext *vec; +- size_t sz; ++ size_t sz = sizeof(struct slabobj_ext) * slab->objects; + + gfp &= ~OBJCGS_CLEAR_MASK; +- /* Prevent recursive extension vector allocation */ +- gfp |= __GFP_NO_OBJ_EXT; + +- sz = obj_exts_alloc_size(s, slab, gfp); ++ /* ++ * In most cases, obj_exts arrays are allocated from normal kmalloc. ++ * However, normal kmalloc caches must allocate them from ++ * KMALLOC_NO_OBJ_EXT caches to prevent recursion. ++ */ ++ if (is_kmalloc_normal(s)) ++ gfp |= __GFP_NO_OBJ_EXT; + + /* + * Note that allow_spin may be false during early boot and its +@@ -2165,10 +2133,11 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, + * very early allocations on those. + */ + if (unlikely(!allow_spin)) +- vec = kmalloc_nolock(sz, __GFP_ZERO | __GFP_NO_OBJ_EXT, ++ vec = kmalloc_nolock(sz, __GFP_ZERO | (gfp & __GFP_NO_OBJ_EXT), + slab_nid(slab)); + else +- vec = kmalloc_node(sz, gfp | __GFP_ZERO, slab_nid(slab)); ++ vec = kmalloc_node(sz, gfp | __GFP_ZERO, ++ slab_nid(slab)); + + if (!vec) { + /* +@@ -2183,8 +2152,21 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, + return -ENOMEM; + } + +- VM_WARN_ON_ONCE(virt_to_slab(vec) != NULL && +- virt_to_slab(vec)->slab_cache == s); ++ if (IS_ENABLED(CONFIG_DEBUG_VM)) { ++ struct kmem_cache *exts_cache; ++ struct slab *exts_slab; ++ ++ exts_slab = virt_to_slab(vec); ++ if (exts_slab) { ++ /* ++ * The vector must be allocated from either normal or ++ * KMALLOC_NO_OBJ_EXT kmalloc caches to avoid cycles. ++ */ ++ exts_cache = exts_slab->slab_cache; ++ WARN_ON_ONCE(!is_kmalloc_normal(exts_cache) && ++ !(exts_cache->flags & SLAB_NO_OBJ_EXT)); ++ } ++ } + + new_exts = (unsigned long)vec; + #ifdef CONFIG_MEMCG +@@ -2207,7 +2189,6 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, + * assign slabobj_exts in parallel. In this case the existing + * objcg vector should be reused. + */ +- mark_obj_codetag_empty(vec); + if (unlikely(!allow_spin)) + kfree_nolock(vec); + else +@@ -2243,14 +2224,6 @@ static inline void free_slab_obj_exts(struct slab *slab, bool allow_spin) + return; + } + +- /* +- * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its +- * corresponding extension will be NULL. alloc_tag_sub() will throw a +- * warning if slab has extensions but the extension of an object is +- * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that +- * the extension for obj_exts is expected to be NULL. +- */ +- mark_obj_codetag_empty(obj_exts); + if (allow_spin) + kfree(obj_exts); + else +@@ -7904,10 +7877,10 @@ static int calculate_sizes(struct kmem_cache_args *args, struct kmem_cache *s) + s->allocflags |= __GFP_RECLAIMABLE; + + /* +- * For KMALLOC_NORMAL caches we enable sheaves later by +- * bootstrap_kmalloc_sheaves() to avoid recursion ++ * For kmalloc caches we enable sheaves later by ++ * bootstrap_kmalloc_sheaves() to avoid recursion. + */ +- if (!is_kmalloc_normal(s)) ++ if (!is_kmalloc_cache(s)) + s->sheaf_capacity = calculate_sheaf_capacity(s, args); + + /* +@@ -8473,7 +8446,7 @@ static void __init bootstrap_kmalloc_sheaves(void) + { + enum kmalloc_cache_type type; + +- for (type = KMALLOC_NORMAL; type <= KMALLOC_RANDOM_END; type++) { ++ for (type = KMALLOC_NORMAL; type < NR_KMALLOC_TYPES; type++) { + for (int idx = 0; idx < KMALLOC_SHIFT_HIGH + 1; idx++) { + struct kmem_cache *s = kmalloc_caches[type][idx]; + +-- +2.53.0 + diff --git a/staging-7.1/series b/staging-7.1/series index 20e097a07d..519f7143ad 100644 --- a/staging-7.1/series +++ b/staging-7.1/series @@ -1 +1,5 @@ net-mpls-initialize-rtm_tos-in-mpls_getroute.patch +mm-slab-decouple-slab_no_sheaves-from-slab_no_obj_ex.patch +lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch +mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch +alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch