From: Sasha Levin Date: Sat, 20 Jun 2026 11:54:30 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a8f0cffd16a27e29be3ec3ef03d078b8f2700896;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/drm-amd-display-bound-vbios-record-chain-walk-loops.patch b/queue-5.10/drm-amd-display-bound-vbios-record-chain-walk-loops.patch new file mode 100644 index 0000000000..a28866c64c --- /dev/null +++ b/queue-5.10/drm-amd-display-bound-vbios-record-chain-walk-loops.patch @@ -0,0 +1,215 @@ +From 0eb4b39a7355d0afe95d896b3280d8bce03224b7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 15:24:22 -0400 +Subject: drm/amd/display: Bound VBIOS record-chain walk loops + +From: Harry Wentland + +[ Upstream commit ff287df16a1a58aca78b08d1f3ee09fc44da0351 ] + +[Why & How] +All record-chain walk loops in bios_parser.c and bios_parser2.c use +for(;;) and only terminate on a 0xFF record_type sentinel or zero +record_size. A malformed VBIOS image missing the terminator record +causes unbounded iteration at probe time, potentially hundreds of +thousands of iterations with record_size=1. In the final iterations +near the BIOS image boundary, struct casts beyond the 2-byte header +validated by GET_IMAGE can also read out of bounds. + +Cap all 14 record-chain walk loops to BIOS_MAX_NUM_RECORD (256) +iterations. The atombios.h defines up to 22 distinct record types +and atomfirmware.h has 13. Assuming an average of less than 10 +records per type (which is reasonable since most are connector- +based) 256 is a generous upper bound. + +Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") +Assisted-by: Copilot:claude-opus-4.6 Mythos +Reviewed-by: Alex Hung +Signed-off-by: Harry Wentland +Signed-off-by: Ray Wu +Tested-by: Daniel Wheeler +Signed-off-by: Alex Deucher +(cherry picked from commit 95700a3d660287ed657d6892f7be9ffc0e294a93) +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/display/dc/bios/bios_parser.c | 15 ++++++++++----- + .../gpu/drm/amd/display/dc/bios/bios_parser2.c | 12 ++++++++---- + .../drm/amd/display/dc/bios/bios_parser_helper.h | 5 +++++ + 3 files changed, 23 insertions(+), 9 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +index d37ee8277480dc..b7b9e4c99537b7 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +@@ -223,6 +223,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + ATOM_COMMON_RECORD_HEADER *header; + ATOM_I2C_RECORD *record; + struct bios_parser *bp = BP_FROM_DCB(dcb); ++ int i; + + if (!info) + return BP_RESULT_BADINPUT; +@@ -235,7 +236,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -294,11 +295,12 @@ static enum bp_result bios_parser_get_device_tag_record( + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -870,6 +872,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -879,7 +882,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -1576,6 +1579,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -1585,7 +1589,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -2667,6 +2671,7 @@ enum bp_result update_slot_layout_info( + unsigned int record_offset) + { + unsigned int j; ++ unsigned int n; + struct bios_parser *bp; + ATOM_BRACKET_LAYOUT_RECORD *record; + ATOM_COMMON_RECORD_HEADER *record_header; +@@ -2676,7 +2681,7 @@ enum bp_result update_slot_layout_info( + record = NULL; + record_header = NULL; + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (ATOM_COMMON_RECORD_HEADER *) + GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +index 2ad36721212404..9e9476f87f6190 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +@@ -296,6 +296,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + struct atom_i2c_record *record; + struct atom_i2c_record dummy_record = {0}; + struct bios_parser *bp = BP_FROM_DCB(dcb); ++ int i; + + if (!info) + return BP_RESULT_BADINPUT; +@@ -316,7 +317,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -446,6 +447,7 @@ static struct atom_hpd_int_record *get_hpd_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -455,7 +457,7 @@ static struct atom_hpd_int_record *get_hpd_record( + offset = le16_to_cpu(object->disp_recordoffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -1423,6 +1425,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -1431,7 +1434,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( + + offset = object->encoder_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -1870,6 +1873,7 @@ static enum bp_result update_slot_layout_info( + { + unsigned int record_offset; + unsigned int j; ++ unsigned int n; + struct atom_display_object_path_v2 *object; + struct atom_bracket_layout_record *record; + struct atom_common_record_header *record_header; +@@ -1891,7 +1895,7 @@ static enum bp_result update_slot_layout_info( + (object->disp_recordoffset) + + (unsigned int)(bp->object_info_tbl_offset); + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (struct atom_common_record_header *) + GET_IMAGE(struct atom_common_record_header, +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +index 75a29e68fb2782..991bbca950978e 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +@@ -38,4 +38,9 @@ uint32_t bios_get_vga_enabled_displays(struct dc_bios *bios); + + #define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) + ++/* Upper bound on the number of records in a VBIOS record chain. Prevents ++ * unbounded looping if the VBIOS image is malformed and lacks a terminator. ++ */ ++#define BIOS_MAX_NUM_RECORD 256 ++ + #endif +-- +2.53.0 + diff --git a/queue-5.10/drm-amd-display-use-krealloc_array-in-dal_vector_res.patch b/queue-5.10/drm-amd-display-use-krealloc_array-in-dal_vector_res.patch new file mode 100644 index 0000000000..3de5b22fb8 --- /dev/null +++ b/queue-5.10/drm-amd-display-use-krealloc_array-in-dal_vector_res.patch @@ -0,0 +1,50 @@ +From 4952ea016121522daf0f95da1644ff8313a58456 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 08:55:08 -0400 +Subject: drm/amd/display: Use krealloc_array() in dal_vector_reserve() + +From: Harry Wentland + +[ Upstream commit da48bc4461b8a5ebfb9264c9b191a701d8e99009 ] + +[Why & How] +dal_vector_reserve() computes the allocation size as +"capacity * vector->struct_size" using uint32_t arithmetic, which can +silently wrap to a small value on overflow. This would cause krealloc to +return a smaller buffer than expected, leading to heap overflows on +subsequent vector appends. + +Replace krealloc() with krealloc_array() which performs an internal +overflow check and returns NULL on wrap, preventing the issue. + +Fixes: 2004f45ef83f ("drm/amd/display: Use kernel alloc/free") +Assisted-by: Copilot:claude-opus-4.6 +Reviewed-by: Alex Hung +Signed-off-by: Harry Wentland +Signed-off-by: Ray Wu +Tested-by: Daniel Wheeler +Signed-off-by: Alex Deucher +(cherry picked from commit 37668568641ccc4cc1dbca4923d0a16609dd5707) +Cc: stable@vger.kernel.org +[ changed `krealloc_array(p, capacity, struct_size)` to `krealloc(p, array_size(capacity, struct_size))` since krealloc_array() is absent in 5.10 ] +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/display/dc/basics/vector.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/basics/vector.c b/drivers/gpu/drm/amd/display/dc/basics/vector.c +index 8f93d25f91ee2b..68c34a4e253ac1 100644 +--- a/drivers/gpu/drm/amd/display/dc/basics/vector.c ++++ b/drivers/gpu/drm/amd/display/dc/basics/vector.c +@@ -292,7 +292,7 @@ bool dal_vector_reserve(struct vector *vector, uint32_t capacity) + return true; + + new_container = krealloc(vector->container, +- capacity * vector->struct_size, GFP_KERNEL); ++ array_size(capacity, vector->struct_size), GFP_KERNEL); + + if (new_container) { + vector->container = new_container; +-- +2.53.0 + diff --git a/queue-5.10/ip6_vti-set-netns_immutable-on-the-fallback-device.patch b/queue-5.10/ip6_vti-set-netns_immutable-on-the-fallback-device.patch new file mode 100644 index 0000000000..85d580eba6 --- /dev/null +++ b/queue-5.10/ip6_vti-set-netns_immutable-on-the-fallback-device.patch @@ -0,0 +1,49 @@ +From e4271332158bb94a0b1788f083b88a5898331f99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Jun 2026 15:59:18 +0000 +Subject: ip6_vti: set netns_immutable on the fallback device. + +From: Eric Dumazet + +[ Upstream commit d289d5307762d1838aaece22c6b6fcad9e8865f9 ] + +john1988 and Noam Rathaus reported that vti6_init_net() does not set the +netns_immutable flag on the per-netns fallback tunnel device (ip6_vti0). + +Other similar tunnel drivers (like ip6_tunnel, sit, ip6_gre, and ip_tunnel) +correctly set this flag during their fallback device initialization to +prevent them from being moved to another network namespace. + +Fixes: 61220ab34948 ("vti6: Enable namespace changing") +Reported-by: Noam Rathaus +Signed-off-by: Eric Dumazet +Cc: Steffen Klassert +Reviewed-by: Nicolas Dichtel +Link: https://patch.msgid.link/20260608155918.787644-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +[Salvatore Bonaccorso: Backport for version without 0c493da86374 ("net: +rename netns_local to netns_immutable") in v6.15-rc1 and without +05c1280a2bcf ("netdev_features: convert NETIF_F_NETNS_LOCAL to +dev->netns_local") in v6.12-rc1 and use NETIF_F_NETNS_LOCAL device +feature.] +Signed-off-by: Salvatore Bonaccorso +Signed-off-by: Sasha Levin +--- + net/ipv6/ip6_vti.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c +index e471a5821b0de5..a67c9d6fbf7712 100644 +--- a/net/ipv6/ip6_vti.c ++++ b/net/ipv6/ip6_vti.c +@@ -1163,6 +1163,7 @@ static int __net_init vti6_init_net(struct net *net) + goto err_alloc_dev; + dev_net_set(ip6n->fb_tnl_dev, net); + ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops; ++ ip6n->fb_tnl_dev->features |= NETIF_F_NETNS_LOCAL; + + err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); + if (err < 0) +-- +2.53.0 + diff --git a/queue-5.10/net-9p-fix-refcount-leak-in-p9_read_work-error-handl.patch b/queue-5.10/net-9p-fix-refcount-leak-in-p9_read_work-error-handl.patch new file mode 100644 index 0000000000..94112d6b58 --- /dev/null +++ b/queue-5.10/net-9p-fix-refcount-leak-in-p9_read_work-error-handl.patch @@ -0,0 +1,40 @@ +From d89f35d726d8b12b78a0b4d0cd2263cf0ca0473c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 15:19:21 +0000 +Subject: net: 9p: fix refcount leak in p9_read_work() error handling + +From: Hangyu Hua + +commit 4ac7573e1f9333073fa8d303acc941c9b7ab7f61 upstream. + +p9_req_put need to be called when m->rreq->rc.sdata is NULL to avoid +temporary refcount leak. + +Link: https://lkml.kernel.org/r/20220712104438.30800-1-hbh25y@gmail.com +Fixes: 728356dedeff ("9p: Add refcount to p9_req_t") +Signed-off-by: Hangyu Hua +[Dominique: commit wording adjustments, p9_req_put argument fixes for rebase] +Signed-off-by: Dominique Martinet +[Alexander: this branch doesn't contain 8b11ff098af4 ("9p: Add client parameter + to p9_req_put()"), therefore the parameter is removed from the added line] +Signed-off-by: Alexander Martyniuk +Signed-off-by: Sasha Levin +--- + net/9p/trans_fd.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c +index 40d458c438df1e..a75668534c81c3 100644 +--- a/net/9p/trans_fd.c ++++ b/net/9p/trans_fd.c +@@ -346,6 +346,7 @@ static void p9_read_work(struct work_struct *work) + p9_debug(P9_DEBUG_ERROR, + "No recv fcall for tag %d (req %p), disconnecting!\n", + m->rc.tag, m->rreq); ++ p9_req_put(m->rreq); + m->rreq = NULL; + err = -EIO; + goto error; +-- +2.53.0 + diff --git a/queue-5.10/net-add-skb_header_pointer_careful-helper.patch b/queue-5.10/net-add-skb_header_pointer_careful-helper.patch new file mode 100644 index 0000000000..20efa13e4d --- /dev/null +++ b/queue-5.10/net-add-skb_header_pointer_careful-helper.patch @@ -0,0 +1,54 @@ +From d6f941a8a1f7daf42358641d187c42cfa5746739 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 01:08:06 -0700 +Subject: net: add skb_header_pointer_careful() helper + +From: Eric Dumazet + +[ Upstream commit 13e00fdc9236bd4d0bff4109d2983171fbcb74c4 ] + +This variant of skb_header_pointer() should be used in contexts +where @offset argument is user-controlled and could be negative. + +Negative offsets are supported, as long as the zone starts +between skb->head and skb->data. + +Signed-off-by: Eric Dumazet +Link: https://patch.msgid.link/20260128141539.3404400-2-edumazet@google.com +Signed-off-by: Jakub Kicinski +[ Adjust context ] +Signed-off-by: Bin Lan +Signed-off-by: Greg Kroah-Hartman +[ Shivani: Modified to apply on 5.10.y ] +Signed-off-by: Shivani Agarwal +Signed-off-by: Sasha Levin +--- + include/linux/skbuff.h | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h +index 4b5731245bf15b..667de403b2ebbd 100644 +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -3686,6 +3686,18 @@ skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) + skb_headlen(skb), buffer); + } + ++/* Variant of skb_header_pointer() where @offset is user-controlled ++ * and potentially negative. ++ */ ++static inline void * __must_check ++skb_header_pointer_careful(const struct sk_buff *skb, int offset, ++ int len, void *buffer) ++{ ++ if (unlikely(offset < 0 && -offset > skb_headroom(skb))) ++ return NULL; ++ return skb_header_pointer(skb, offset, len, buffer); ++} ++ + /** + * skb_needs_linearize - check if we need to linearize a given skb + * depending on the given device features. +-- +2.53.0 + diff --git a/queue-5.10/net-sched-cls_u32-use-skb_header_pointer_careful.patch b/queue-5.10/net-sched-cls_u32-use-skb_header_pointer_careful.patch new file mode 100644 index 0000000000..7f271c28ed --- /dev/null +++ b/queue-5.10/net-sched-cls_u32-use-skb_header_pointer_careful.patch @@ -0,0 +1,74 @@ +From 4c9221fac64d28ed5f8d470d48af0e4715f7cfb3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 01:08:07 -0700 +Subject: net/sched: cls_u32: use skb_header_pointer_careful() + +From: Eric Dumazet + +[ Upstream commit cabd1a976375780dabab888784e356f574bbaed8 ] + +skb_header_pointer() does not fully validate negative @offset values. + +Use skb_header_pointer_careful() instead. + +GangMin Kim provided a report and a repro fooling u32_classify(): + +BUG: KASAN: slab-out-of-bounds in u32_classify+0x1180/0x11b0 +net/sched/cls_u32.c:221 + +Fixes: fbc2e7d9cf49 ("cls_u32: use skb_header_pointer() to dereference data safely") +Reported-by: GangMin Kim +Closes: https://lore.kernel.org/netdev/CANn89iJkyUZ=mAzLzC4GdcAgLuPnUoivdLaOs6B9rq5_erj76w@mail.gmail.com/T/ +Signed-off-by: Eric Dumazet +Link: https://patch.msgid.link/20260128141539.3404400-3-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Bin Lan +Signed-off-by: Greg Kroah-Hartman +[ Shivani: Modified to apply on 5.10.y ] +Signed-off-by: Shivani Agarwal +Signed-off-by: Sasha Levin +--- + net/sched/cls_u32.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c +index f2a0c10682fc81..e501390ccd7585 100644 +--- a/net/sched/cls_u32.c ++++ b/net/sched/cls_u32.c +@@ -149,10 +149,8 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, + int toff = off + key->off + (off2 & key->offmask); + __be32 *data, hdata; + +- if (skb_headroom(skb) + toff > INT_MAX) +- goto out; +- +- data = skb_header_pointer(skb, toff, 4, &hdata); ++ data = skb_header_pointer_careful(skb, toff, 4, ++ &hdata); + if (!data) + goto out; + if ((*data ^ key->val) & key->mask) { +@@ -202,8 +200,9 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, + if (ht->divisor) { + __be32 *data, hdata; + +- data = skb_header_pointer(skb, off + n->sel.hoff, 4, +- &hdata); ++ data = skb_header_pointer_careful(skb, ++ off + n->sel.hoff, ++ 4, &hdata); + if (!data) + goto out; + sel = ht->divisor & u32_hash_fold(*data, &n->sel, +@@ -217,7 +216,7 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, + if (n->sel.flags & TC_U32_VAROFFSET) { + __be16 *data, hdata; + +- data = skb_header_pointer(skb, ++ data = skb_header_pointer_careful(skb, + off + n->sel.offoff, + 2, &hdata); + if (!data) +-- +2.53.0 + diff --git a/queue-5.10/netdevsim-fix-memory-leak-of-nsim_dev-fa_cookie.patch b/queue-5.10/netdevsim-fix-memory-leak-of-nsim_dev-fa_cookie.patch new file mode 100644 index 0000000000..76c308f536 --- /dev/null +++ b/queue-5.10/netdevsim-fix-memory-leak-of-nsim_dev-fa_cookie.patch @@ -0,0 +1,64 @@ +From 2fb4fe605a3f03b9480e43ca4d612a1731ac6182 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 12:15:06 +0300 +Subject: netdevsim: Fix memory leak of nsim_dev->fa_cookie + +From: Wang Yufen + +commit 064bc7312bd09a48798418663090be0c776183db upstream. + +kmemleak reports this issue: + +unreferenced object 0xffff8881bac872d0 (size 8): + comm "sh", pid 58603, jiffies 4481524462 (age 68.065s) + hex dump (first 8 bytes): + 04 00 00 00 de ad be ef ........ + backtrace: + [<00000000c80b8577>] __kmalloc+0x49/0x150 + [<000000005292b8c6>] nsim_dev_trap_fa_cookie_write+0xc1/0x210 [netdevsim] + [<0000000093d78e77>] full_proxy_write+0xf3/0x180 + [<000000005a662c16>] vfs_write+0x1c5/0xaf0 + [<000000007aabf84a>] ksys_write+0xed/0x1c0 + [<000000005f1d2e47>] do_syscall_64+0x3b/0x90 + [<000000006001c6ec>] entry_SYSCALL_64_after_hwframe+0x63/0xcd + +The issue occurs in the following scenarios: + +nsim_dev_trap_fa_cookie_write() + kmalloc() fa_cookie + nsim_dev->fa_cookie = fa_cookie +.. +nsim_drv_remove() + +The fa_cookie allocked in nsim_dev_trap_fa_cookie_write() is not freed. To +fix, add kfree(nsim_dev->fa_cookie) to nsim_drv_remove(). + +Fixes: d3cbb907ae57 ("netdevsim: add ACL trap reporting cookie as a metadata") +Signed-off-by: Wang Yufen +Cc: Jiri Pirko +Link: https://lore.kernel.org/r/1668504625-14698-1-git-send-email-wangyufen@huawei.com +Signed-off-by: Jakub Kicinski +[ The context change is due to the commit 5e388f3dc38c +("netdevsim: move vfconfig to nsim_dev") in v5.16 +which is irrelevant to the logic of this patch. ] +Signed-off-by: Mikhail Dmitrichenko +Signed-off-by: Sasha Levin +--- + drivers/net/netdevsim/dev.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c +index c8834ea84732bd..a106365ce485e3 100644 +--- a/drivers/net/netdevsim/dev.c ++++ b/drivers/net/netdevsim/dev.c +@@ -1173,6 +1173,7 @@ void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev) + ARRAY_SIZE(nsim_devlink_params)); + devlink_unregister(devlink); + devlink_resources_unregister(devlink, NULL); ++ kfree(nsim_dev->fa_cookie); + devlink_free(devlink); + } + +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index 2153636ec7..aa62c35b04 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -8,3 +8,11 @@ net-sched-act_pedit-parse-l3-header-for-l4-offset.patch net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch +slimbus-qcom-ngd-ctrl-register-callbacks-after-creat.patch +drm-amd-display-bound-vbios-record-chain-walk-loops.patch +ip6_vti-set-netns_immutable-on-the-fallback-device.patch +net-add-skb_header_pointer_careful-helper.patch +net-sched-cls_u32-use-skb_header_pointer_careful.patch +drm-amd-display-use-krealloc_array-in-dal_vector_res.patch +net-9p-fix-refcount-leak-in-p9_read_work-error-handl.patch +netdevsim-fix-memory-leak-of-nsim_dev-fa_cookie.patch diff --git a/queue-5.10/slimbus-qcom-ngd-ctrl-register-callbacks-after-creat.patch b/queue-5.10/slimbus-qcom-ngd-ctrl-register-callbacks-after-creat.patch new file mode 100644 index 0000000000..43c162dba0 --- /dev/null +++ b/queue-5.10/slimbus-qcom-ngd-ctrl-register-callbacks-after-creat.patch @@ -0,0 +1,84 @@ +From 47bdb4381f628d4b5b7559efeb63a6c8f33419d6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 17 Jun 2026 22:19:09 -0400 +Subject: slimbus: qcom-ngd-ctrl: Register callbacks after creating the ngd + +From: Bjorn Andersson + +[ Upstream commit 2a9d50e9ea406e0c8735938484adc20515ef1b47 ] + +When the remoteproc starts in parallel with the NGD driver being probed, +or the remoteproc is already up when the PDR lookup is being registered, +or in the theoretical event that we get an interrupt from the hardware, +these callbacks will operate on uninitialized data. This result in +issues to boot the affected boards. + +One such example can be seen in the following fault, where +qcom_slim_ngd_ssr_pdr_notify() schedules work on the NULL ngd_up_work. + +[ 21.858578] ------------[ cut here ]------------ +[ 21.858745] WARNING: kernel/workqueue.c:2338 at __queue_work+0x5e0/0x790, CPU#2: kworker/2:2/116 +... +[ 21.859251] Call trace: +[ 21.859255] __queue_work+0x5e0/0x790 (P) +[ 21.859265] queue_work_on+0x6c/0xf0 +[ 21.859273] qcom_slim_ngd_ssr_pdr_notify+0x110/0x150 [slim_qcom_ngd_ctrl] +[ 21.859304] qcom_slim_ngd_ssr_notify+0x24/0x40 [slim_qcom_ngd_ctrl] +[ 21.859318] notifier_call_chain+0xa4/0x230 +[ 21.859329] srcu_notifier_call_chain+0x64/0xb8 +[ 21.859338] ssr_notify_start+0x40/0x78 [qcom_common] +[ 21.859355] rproc_start+0x130/0x230 +[ 21.859367] rproc_boot+0x3d4/0x518 +... + +Move the enablement of interrupts, and the registration of SSR and PDR +until after the NGD device has been registered. + +This could be further refined by moving initialization to the control +driver probe and by removing the platform driver model from the picture. + +Fixes: 917809e2280b ("slimbus: ngd: Add qcom SLIMBus NGD driver") +Cc: stable@vger.kernel.org +Reviewed-by: Mukesh Ojha +Signed-off-by: Bjorn Andersson +Signed-off-by: Srinivas Kandagatla +Link: https://patch.msgid.link/20260530204421.116824-6-srini@kernel.org +Signed-off-by: Greg Kroah-Hartman +[ dropped absent PDR/SSR registration block and kept 5.10's `res->start`/`dev_err` IRQ idiom, adding only `IRQF_NO_AUTOEN` plus a deferred `enable_irq(res->start)` after `of_qcom_slim_ngd_register()` ] +Signed-off-by: Sasha Levin +--- + drivers/slimbus/qcom-ngd-ctrl.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c +index 8384f55ccd43e3..0c864bb2d4bdb1 100644 +--- a/drivers/slimbus/qcom-ngd-ctrl.c ++++ b/drivers/slimbus/qcom-ngd-ctrl.c +@@ -1444,7 +1444,8 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) + } + + ret = devm_request_irq(dev, res->start, qcom_slim_ngd_interrupt, +- IRQF_TRIGGER_HIGH, "slim-ngd", ctrl); ++ IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, ++ "slim-ngd", ctrl); + if (ret) { + dev_err(&pdev->dev, "request IRQ failed\n"); + return ret; +@@ -1468,7 +1469,13 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) + init_completion(&ctrl->qmi.qmi_comp); + + platform_driver_register(&qcom_slim_ngd_driver); +- return of_qcom_slim_ngd_register(dev, ctrl); ++ ret = of_qcom_slim_ngd_register(dev, ctrl); ++ if (ret) ++ return ret; ++ ++ enable_irq(res->start); ++ ++ return 0; + } + + static int qcom_slim_ngd_ctrl_remove(struct platform_device *pdev) +-- +2.53.0 + diff --git a/queue-5.15/drm-amd-display-bound-vbios-record-chain-walk-loops.patch b/queue-5.15/drm-amd-display-bound-vbios-record-chain-walk-loops.patch new file mode 100644 index 0000000000..30774ed894 --- /dev/null +++ b/queue-5.15/drm-amd-display-bound-vbios-record-chain-walk-loops.patch @@ -0,0 +1,232 @@ +From 6621af3956b6ff02855171bbdbc7c46f8fe4e0e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 15:24:22 -0400 +Subject: drm/amd/display: Bound VBIOS record-chain walk loops + +From: Harry Wentland + +[ Upstream commit ff287df16a1a58aca78b08d1f3ee09fc44da0351 ] + +[Why & How] +All record-chain walk loops in bios_parser.c and bios_parser2.c use +for(;;) and only terminate on a 0xFF record_type sentinel or zero +record_size. A malformed VBIOS image missing the terminator record +causes unbounded iteration at probe time, potentially hundreds of +thousands of iterations with record_size=1. In the final iterations +near the BIOS image boundary, struct casts beyond the 2-byte header +validated by GET_IMAGE can also read out of bounds. + +Cap all 14 record-chain walk loops to BIOS_MAX_NUM_RECORD (256) +iterations. The atombios.h defines up to 22 distinct record types +and atomfirmware.h has 13. Assuming an average of less than 10 +records per type (which is reasonable since most are connector- +based) 256 is a generous upper bound. + +Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") +Assisted-by: Copilot:claude-opus-4.6 Mythos +Reviewed-by: Alex Hung +Signed-off-by: Harry Wentland +Signed-off-by: Ray Wu +Tested-by: Daniel Wheeler +Signed-off-by: Alex Deucher +(cherry picked from commit 95700a3d660287ed657d6892f7be9ffc0e294a93) +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/display/dc/bios/bios_parser.c | 15 ++++++++++----- + .../gpu/drm/amd/display/dc/bios/bios_parser2.c | 15 ++++++++++----- + .../drm/amd/display/dc/bios/bios_parser_helper.h | 5 +++++ + 3 files changed, 25 insertions(+), 10 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +index d8982aca8ef680..d2730af8e8030f 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +@@ -223,6 +223,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + ATOM_COMMON_RECORD_HEADER *header; + ATOM_I2C_RECORD *record; + struct bios_parser *bp = BP_FROM_DCB(dcb); ++ int i; + + if (!info) + return BP_RESULT_BADINPUT; +@@ -235,7 +236,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -294,11 +295,12 @@ static enum bp_result bios_parser_get_device_tag_record( + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -870,6 +872,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -879,7 +882,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -1572,6 +1575,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -1581,7 +1585,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -2665,6 +2669,7 @@ static enum bp_result update_slot_layout_info( + unsigned int record_offset) + { + unsigned int j; ++ unsigned int n; + struct bios_parser *bp; + ATOM_BRACKET_LAYOUT_RECORD *record; + ATOM_COMMON_RECORD_HEADER *record_header; +@@ -2674,7 +2679,7 @@ static enum bp_result update_slot_layout_info( + record = NULL; + record_header = NULL; + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (ATOM_COMMON_RECORD_HEADER *) + GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +index e186d4daae55b6..2192b2597edbc3 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +@@ -296,6 +296,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + struct atom_i2c_record *record; + struct atom_i2c_record dummy_record = {0}; + struct bios_parser *bp = BP_FROM_DCB(dcb); ++ int i; + + if (!info) + return BP_RESULT_BADINPUT; +@@ -316,7 +317,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -446,6 +447,7 @@ static struct atom_hpd_int_record *get_hpd_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -455,7 +457,7 @@ static struct atom_hpd_int_record *get_hpd_record( + offset = le16_to_cpu(object->disp_recordoffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -1614,6 +1616,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -1622,7 +1625,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( + + offset = object->encoder_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -1651,6 +1654,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -1659,7 +1663,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2420,6 +2424,7 @@ static enum bp_result update_slot_layout_info( + { + unsigned int record_offset; + unsigned int j; ++ unsigned int n; + struct atom_display_object_path_v2 *object; + struct atom_bracket_layout_record *record; + struct atom_common_record_header *record_header; +@@ -2441,7 +2446,7 @@ static enum bp_result update_slot_layout_info( + (object->disp_recordoffset) + + (unsigned int)(bp->object_info_tbl_offset); + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (struct atom_common_record_header *) + GET_IMAGE(struct atom_common_record_header, +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +index e1b4a40a353db1..da1e30de3c59a0 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +@@ -38,4 +38,9 @@ uint32_t bios_get_vga_enabled_displays(struct dc_bios *bios); + + #define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) + ++/* Upper bound on the number of records in a VBIOS record chain. Prevents ++ * unbounded looping if the VBIOS image is malformed and lacks a terminator. ++ */ ++#define BIOS_MAX_NUM_RECORD 256 ++ + #endif +-- +2.53.0 + diff --git a/queue-5.15/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch b/queue-5.15/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch new file mode 100644 index 0000000000..bbeb7cb26e --- /dev/null +++ b/queue-5.15/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch @@ -0,0 +1,63 @@ +From bc7a7e67b3e9a69d3f828200c9ff5623076eec2a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 14:50:15 -0300 +Subject: drm/v3d: Skip CSD when it has zeroed workgroups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 7f93fad5ea0affc9e1505dd0f7596c0fdb496213 ] + +A compute shader dispatch encodes its workgroup counts in the CFG0..CFG2 +registers. Kicking off a dispatch with a zero count in any of the three +dimensions is invalid. First, the hardware will process 0 as 65536, +while the user-space driver exposes a maximum of 65535. Over that, a +submission with a zeroed workgroup dimension should be a no-op. + +These zeroed counts can reach the dispatch path through an indirect CSD +job, whose workgroup counts are only known once the indirect buffer is +read and may legitimately be zero, but such scenario should only result in +a no-op. + +Overwrite the indirect CSD job workgroup counts with the indirect BO +ones, even if they are zeroed, and don't submit the job to the hardware +when any of the workgroup counts is zero, so the job completes immediately +instead of running the shader. + +Cc: stable@vger.kernel.org +Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.") +Suggested-by: Jose Maria Casanova Crespo +Reviewed-by: Iago Toral Quiroga +Link: https://patch.msgid.link/20260602-v3d-fix-indirect-csd-v4-2-654309e32bc0@igalia.com +Signed-off-by: Maíra Canal +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_sched.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c +index db98855741ee85..46c52d89cb2498 100644 +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -257,6 +257,16 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) + return NULL; + } + ++ /* The HW interprets a workgroup size of 0 as 65536; however, the ++ * user-space driver exposes a maximum of 65535. Therefore, a 0 in ++ * any dimension means that we have no workgroups and the compute ++ * shader should not be dispatched. ++ */ ++ if (!V3D_GET_FIELD(job->args.cfg[0], V3D_CSD_QUEUED_CFG0_NUM_WGS_X) || ++ !V3D_GET_FIELD(job->args.cfg[1], V3D_CSD_QUEUED_CFG1_NUM_WGS_Y) || ++ !V3D_GET_FIELD(job->args.cfg[2], V3D_CSD_QUEUED_CFG2_NUM_WGS_Z)) ++ return NULL; ++ + v3d->queue[V3D_CSD].active_job = &job->base; + + v3d_invalidate_caches(v3d); +-- +2.53.0 + diff --git a/queue-5.15/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch b/queue-5.15/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch new file mode 100644 index 0000000000..a872bbff4d --- /dev/null +++ b/queue-5.15/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch @@ -0,0 +1,237 @@ +From ebf76d23b3b78375e30d09ba43944d0c67f4ee79 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 Aug 2025 11:18:59 -0300 +Subject: drm/v3d: Store the active job inside the queue's state +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 0d3768826d38c0ac740f8b45cd13346630535f2b ] + +Instead of storing the queue's active job in four different variables, +store the active job inside the queue's state. This way, it's possible +to access all active jobs using an index based in `enum v3d_queue`. + +Reviewed-by: Iago Toral Quiroga +Reviewed-by: Melissa Wen +Link: https://lore.kernel.org/r/20250826-v3d-queue-lock-v3-2-979efc43e490@igalia.com +Signed-off-by: Maíra Canal +Stable-dep-of: 7f93fad5ea0a ("drm/v3d: Skip CSD when it has zeroed workgroups") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_drv.h | 8 +++----- + drivers/gpu/drm/v3d/v3d_gem.c | 5 +++-- + drivers/gpu/drm/v3d/v3d_irq.c | 24 ++++++++++++++---------- + drivers/gpu/drm/v3d/v3d_sched.c | 26 ++++++++++++++++++-------- + 4 files changed, 38 insertions(+), 25 deletions(-) + +diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h +index 0d551b1d9b05d4..14002d2b4add36 100644 +--- a/drivers/gpu/drm/v3d/v3d_drv.h ++++ b/drivers/gpu/drm/v3d/v3d_drv.h +@@ -35,6 +35,9 @@ struct v3d_queue_state { + + u64 fence_context; + u64 emit_seqno; ++ ++ /* Currently active job for this queue */ ++ struct v3d_job *active_job; + }; + + /* Performance monitor object. The perform lifetime is controlled by userspace +@@ -119,11 +122,6 @@ struct v3d_dev { + + struct work_struct overflow_mem_work; + +- struct v3d_bin_job *bin_job; +- struct v3d_render_job *render_job; +- struct v3d_tfu_job *tfu_job; +- struct v3d_csd_job *csd_job; +- + struct v3d_queue_state queue[V3D_MAX_QUEUES]; + + /* Spinlock used to synchronize the overflow memory +diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c +index ecd03ad9699a08..3911550f72c22f 100644 +--- a/drivers/gpu/drm/v3d/v3d_gem.c ++++ b/drivers/gpu/drm/v3d/v3d_gem.c +@@ -950,14 +950,15 @@ void + v3d_gem_destroy(struct drm_device *dev) + { + struct v3d_dev *v3d = to_v3d_dev(dev); ++ enum v3d_queue q; + + v3d_sched_fini(v3d); + + /* Waiting for jobs to finish would need to be done before + * unregistering V3D. + */ +- WARN_ON(v3d->bin_job); +- WARN_ON(v3d->render_job); ++ for (q = 0; q < V3D_MAX_QUEUES; q++) ++ WARN_ON(v3d->queue[q].active_job); + + drm_mm_takedown(&v3d->mm); + +diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c +index 9aba78e6d7a5a6..3d5505159ff5e8 100644 +--- a/drivers/gpu/drm/v3d/v3d_irq.c ++++ b/drivers/gpu/drm/v3d/v3d_irq.c +@@ -40,6 +40,8 @@ v3d_overflow_mem_work(struct work_struct *work) + container_of(work, struct v3d_dev, overflow_mem_work); + struct drm_device *dev = &v3d->drm; + struct v3d_bo *bo = v3d_bo_create(dev, NULL /* XXX: GMP */, 256 * 1024); ++ struct v3d_queue_state *queue = &v3d->queue[V3D_BIN]; ++ struct v3d_bin_job *bin_job; + struct drm_gem_object *obj; + unsigned long irqflags; + +@@ -59,13 +61,15 @@ v3d_overflow_mem_work(struct work_struct *work) + * some binner pool anyway. + */ + spin_lock_irqsave(&v3d->job_lock, irqflags); +- if (!v3d->bin_job) { ++ bin_job = (struct v3d_bin_job *)queue->active_job; ++ ++ if (!bin_job) { + spin_unlock_irqrestore(&v3d->job_lock, irqflags); + goto out; + } + + drm_gem_object_get(obj); +- list_add_tail(&bo->unref_head, &v3d->bin_job->render->unref_list); ++ list_add_tail(&bo->unref_head, &bin_job->render->unref_list); + spin_unlock_irqrestore(&v3d->job_lock, irqflags); + + V3D_CORE_WRITE(0, V3D_PTB_BPOA, bo->node.start << PAGE_SHIFT); +@@ -99,11 +103,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_FLDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->bin_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_BIN].active_job->irq_fence); + + trace_v3d_bcl_irq(&v3d->drm, fence->seqno); + +- v3d->bin_job = NULL; ++ v3d->queue[V3D_BIN].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -111,11 +115,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_FRDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->render_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_RENDER].active_job->irq_fence); + + trace_v3d_rcl_irq(&v3d->drm, fence->seqno); + +- v3d->render_job = NULL; ++ v3d->queue[V3D_RENDER].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -123,11 +127,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_CSDDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->csd_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_CSD].active_job->irq_fence); + + trace_v3d_csd_irq(&v3d->drm, fence->seqno); + +- v3d->csd_job = NULL; ++ v3d->queue[V3D_CSD].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -162,11 +166,11 @@ v3d_hub_irq(int irq, void *arg) + + if (intsts & V3D_HUB_INT_TFUC) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->tfu_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_TFU].active_job->irq_fence); + + trace_v3d_tfu_irq(&v3d->drm, fence->seqno); + +- v3d->tfu_job = NULL; ++ v3d->queue[V3D_TFU].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c +index c357229256b73a..db98855741ee85 100644 +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -103,14 +103,18 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) + struct dma_fence *fence; + unsigned long irqflags; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ spin_lock_irqsave(&v3d->job_lock, irqflags); ++ v3d->queue[V3D_BIN].active_job = NULL; ++ spin_unlock_irqrestore(&v3d->job_lock, irqflags); + return NULL; ++ } + + /* Lock required around bin_job update vs + * v3d_overflow_mem_work(). + */ + spin_lock_irqsave(&v3d->job_lock, irqflags); +- v3d->bin_job = job; ++ v3d->queue[V3D_BIN].active_job = &job->base; + /* Clear out the overflow allocation, so we don't + * reuse the overflow attached to a previous job. + */ +@@ -157,10 +161,12 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_RENDER].active_job = NULL; + return NULL; ++ } + +- v3d->render_job = job; ++ v3d->queue[V3D_RENDER].active_job = &job->base; + + /* Can we avoid this flush? We need to be careful of + * scheduling, though -- imagine job0 rendering to texture and +@@ -202,10 +208,12 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job) + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_TFU].active_job = NULL; + return NULL; ++ } + +- v3d->tfu_job = job; ++ v3d->queue[V3D_TFU].active_job = &job->base; + + fence = v3d_fence_create(v3d, V3D_TFU); + if (IS_ERR(fence)) +@@ -244,10 +252,12 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) + struct dma_fence *fence; + int i; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_CSD].active_job = NULL; + return NULL; ++ } + +- v3d->csd_job = job; ++ v3d->queue[V3D_CSD].active_job = &job->base; + + v3d_invalidate_caches(v3d); + +-- +2.53.0 + diff --git a/queue-5.15/ip6_vti-set-netns_immutable-on-the-fallback-device.patch b/queue-5.15/ip6_vti-set-netns_immutable-on-the-fallback-device.patch new file mode 100644 index 0000000000..a1dc351c5e --- /dev/null +++ b/queue-5.15/ip6_vti-set-netns_immutable-on-the-fallback-device.patch @@ -0,0 +1,49 @@ +From 4a3a34db67a0944cc5c0af57fa06628f754fbbd1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Jun 2026 15:59:18 +0000 +Subject: ip6_vti: set netns_immutable on the fallback device. + +From: Eric Dumazet + +[ Upstream commit d289d5307762d1838aaece22c6b6fcad9e8865f9 ] + +john1988 and Noam Rathaus reported that vti6_init_net() does not set the +netns_immutable flag on the per-netns fallback tunnel device (ip6_vti0). + +Other similar tunnel drivers (like ip6_tunnel, sit, ip6_gre, and ip_tunnel) +correctly set this flag during their fallback device initialization to +prevent them from being moved to another network namespace. + +Fixes: 61220ab34948 ("vti6: Enable namespace changing") +Reported-by: Noam Rathaus +Signed-off-by: Eric Dumazet +Cc: Steffen Klassert +Reviewed-by: Nicolas Dichtel +Link: https://patch.msgid.link/20260608155918.787644-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +[Salvatore Bonaccorso: Backport for version without 0c493da86374 ("net: +rename netns_local to netns_immutable") in v6.15-rc1 and without +05c1280a2bcf ("netdev_features: convert NETIF_F_NETNS_LOCAL to +dev->netns_local") in v6.12-rc1 and use NETIF_F_NETNS_LOCAL device +feature.] +Signed-off-by: Salvatore Bonaccorso +Signed-off-by: Sasha Levin +--- + net/ipv6/ip6_vti.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c +index 077aae4b1bb332..e5f3293d190d39 100644 +--- a/net/ipv6/ip6_vti.c ++++ b/net/ipv6/ip6_vti.c +@@ -1167,6 +1167,7 @@ static int __net_init vti6_init_net(struct net *net) + goto err_alloc_dev; + dev_net_set(ip6n->fb_tnl_dev, net); + ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops; ++ ip6n->fb_tnl_dev->features |= NETIF_F_NETNS_LOCAL; + + err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); + if (err < 0) +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index b9e0218d2b..eb8c434509 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -3,3 +3,7 @@ net-sched-act_pedit-check-static-offsets-a-priori.patch net-sched-act_pedit-rate-limit-datapath-messages.patch net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch +drm-amd-display-bound-vbios-record-chain-walk-loops.patch +ip6_vti-set-netns_immutable-on-the-fallback-device.patch +drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch +drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch diff --git a/queue-6.1/drm-amd-display-bound-vbios-record-chain-walk-loops.patch b/queue-6.1/drm-amd-display-bound-vbios-record-chain-walk-loops.patch new file mode 100644 index 0000000000..7795c1cc8e --- /dev/null +++ b/queue-6.1/drm-amd-display-bound-vbios-record-chain-walk-loops.patch @@ -0,0 +1,300 @@ +From f10326a60adc6b1ce0d51994bbd18deb31fe02a7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 15:24:22 -0400 +Subject: drm/amd/display: Bound VBIOS record-chain walk loops + +From: Harry Wentland + +[ Upstream commit ff287df16a1a58aca78b08d1f3ee09fc44da0351 ] + +[Why & How] +All record-chain walk loops in bios_parser.c and bios_parser2.c use +for(;;) and only terminate on a 0xFF record_type sentinel or zero +record_size. A malformed VBIOS image missing the terminator record +causes unbounded iteration at probe time, potentially hundreds of +thousands of iterations with record_size=1. In the final iterations +near the BIOS image boundary, struct casts beyond the 2-byte header +validated by GET_IMAGE can also read out of bounds. + +Cap all 14 record-chain walk loops to BIOS_MAX_NUM_RECORD (256) +iterations. The atombios.h defines up to 22 distinct record types +and atomfirmware.h has 13. Assuming an average of less than 10 +records per type (which is reasonable since most are connector- +based) 256 is a generous upper bound. + +Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") +Assisted-by: Copilot:claude-opus-4.6 Mythos +Reviewed-by: Alex Hung +Signed-off-by: Harry Wentland +Signed-off-by: Ray Wu +Tested-by: Daniel Wheeler +Signed-off-by: Alex Deucher +(cherry picked from commit 95700a3d660287ed657d6892f7be9ffc0e294a93) +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + .../gpu/drm/amd/display/dc/bios/bios_parser.c | 15 +++++++---- + .../drm/amd/display/dc/bios/bios_parser2.c | 27 ++++++++++++------- + .../amd/display/dc/bios/bios_parser_helper.h | 5 ++++ + 3 files changed, 33 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +index d8982aca8ef680..d2730af8e8030f 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +@@ -223,6 +223,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + ATOM_COMMON_RECORD_HEADER *header; + ATOM_I2C_RECORD *record; + struct bios_parser *bp = BP_FROM_DCB(dcb); ++ int i; + + if (!info) + return BP_RESULT_BADINPUT; +@@ -235,7 +236,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -294,11 +295,12 @@ static enum bp_result bios_parser_get_device_tag_record( + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -870,6 +872,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -879,7 +882,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -1572,6 +1575,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -1581,7 +1585,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -2665,6 +2669,7 @@ static enum bp_result update_slot_layout_info( + unsigned int record_offset) + { + unsigned int j; ++ unsigned int n; + struct bios_parser *bp; + ATOM_BRACKET_LAYOUT_RECORD *record; + ATOM_COMMON_RECORD_HEADER *record_header; +@@ -2674,7 +2679,7 @@ static enum bp_result update_slot_layout_info( + record = NULL; + record_header = NULL; + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (ATOM_COMMON_RECORD_HEADER *) + GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +index af8c6682e6bee1..5d6428b2e4f5c8 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +@@ -393,6 +393,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + struct atom_i2c_record *record; + struct atom_i2c_record dummy_record = {0}; + struct bios_parser *bp = BP_FROM_DCB(dcb); ++ int i; + + if (!info) + return BP_RESULT_BADINPUT; +@@ -426,7 +427,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + break; + } + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -532,6 +533,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -540,7 +542,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3( + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -610,6 +612,7 @@ static struct atom_hpd_int_record *get_hpd_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -619,7 +622,7 @@ static struct atom_hpd_int_record *get_hpd_record( + offset = le16_to_cpu(object->disp_recordoffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2127,6 +2130,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -2135,7 +2139,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( + + offset = object->encoder_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2164,6 +2168,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -2172,7 +2177,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2201,6 +2206,7 @@ static struct atom_connector_caps_record *get_connector_caps_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -2209,7 +2215,7 @@ static struct atom_connector_caps_record *get_connector_caps_record( + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2290,6 +2296,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -2298,7 +2305,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record( + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -3162,6 +3169,7 @@ static enum bp_result update_slot_layout_info( + { + unsigned int record_offset; + unsigned int j; ++ unsigned int n; + struct atom_display_object_path_v2 *object; + struct atom_bracket_layout_record *record; + struct atom_common_record_header *record_header; +@@ -3183,7 +3191,7 @@ static enum bp_result update_slot_layout_info( + (object->disp_recordoffset) + + (unsigned int)(bp->object_info_tbl_offset); + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (struct atom_common_record_header *) + GET_IMAGE(struct atom_common_record_header, +@@ -3277,6 +3285,7 @@ static enum bp_result update_slot_layout_info_v2( + struct slot_layout_info *slot_layout_info) + { + unsigned int record_offset; ++ unsigned int n; + struct atom_display_object_path_v3 *object; + struct atom_bracket_layout_record_v2 *record; + struct atom_common_record_header *record_header; +@@ -3299,7 +3308,7 @@ static enum bp_result update_slot_layout_info_v2( + (object->disp_recordoffset) + + (unsigned int)(bp->object_info_tbl_offset); + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (struct atom_common_record_header *) + GET_IMAGE(struct atom_common_record_header, +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +index e1b4a40a353db1..da1e30de3c59a0 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +@@ -38,4 +38,9 @@ uint32_t bios_get_vga_enabled_displays(struct dc_bios *bios); + + #define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) + ++/* Upper bound on the number of records in a VBIOS record chain. Prevents ++ * unbounded looping if the VBIOS image is malformed and lacks a terminator. ++ */ ++#define BIOS_MAX_NUM_RECORD 256 ++ + #endif +-- +2.53.0 + diff --git a/queue-6.1/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch b/queue-6.1/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch new file mode 100644 index 0000000000..7c3d484e10 --- /dev/null +++ b/queue-6.1/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch @@ -0,0 +1,63 @@ +From afce05038bc0bfe4fc1bd590e8381d4e04e8739e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 14:50:15 -0300 +Subject: drm/v3d: Skip CSD when it has zeroed workgroups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 7f93fad5ea0affc9e1505dd0f7596c0fdb496213 ] + +A compute shader dispatch encodes its workgroup counts in the CFG0..CFG2 +registers. Kicking off a dispatch with a zero count in any of the three +dimensions is invalid. First, the hardware will process 0 as 65536, +while the user-space driver exposes a maximum of 65535. Over that, a +submission with a zeroed workgroup dimension should be a no-op. + +These zeroed counts can reach the dispatch path through an indirect CSD +job, whose workgroup counts are only known once the indirect buffer is +read and may legitimately be zero, but such scenario should only result in +a no-op. + +Overwrite the indirect CSD job workgroup counts with the indirect BO +ones, even if they are zeroed, and don't submit the job to the hardware +when any of the workgroup counts is zero, so the job completes immediately +instead of running the shader. + +Cc: stable@vger.kernel.org +Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.") +Suggested-by: Jose Maria Casanova Crespo +Reviewed-by: Iago Toral Quiroga +Link: https://patch.msgid.link/20260602-v3d-fix-indirect-csd-v4-2-654309e32bc0@igalia.com +Signed-off-by: Maíra Canal +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_sched.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c +index ff91cdb75bb912..ab872bc818004a 100644 +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -234,6 +234,16 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) + return NULL; + } + ++ /* The HW interprets a workgroup size of 0 as 65536; however, the ++ * user-space driver exposes a maximum of 65535. Therefore, a 0 in ++ * any dimension means that we have no workgroups and the compute ++ * shader should not be dispatched. ++ */ ++ if (!V3D_GET_FIELD(job->args.cfg[0], V3D_CSD_QUEUED_CFG0_NUM_WGS_X) || ++ !V3D_GET_FIELD(job->args.cfg[1], V3D_CSD_QUEUED_CFG1_NUM_WGS_Y) || ++ !V3D_GET_FIELD(job->args.cfg[2], V3D_CSD_QUEUED_CFG2_NUM_WGS_Z)) ++ return NULL; ++ + v3d->queue[V3D_CSD].active_job = &job->base; + + v3d_invalidate_caches(v3d); +-- +2.53.0 + diff --git a/queue-6.1/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch b/queue-6.1/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch new file mode 100644 index 0000000000..daaec4eb62 --- /dev/null +++ b/queue-6.1/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch @@ -0,0 +1,237 @@ +From c30a79f3770963840a5b11b23aa8b35fccaeff88 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 Aug 2025 11:18:59 -0300 +Subject: drm/v3d: Store the active job inside the queue's state +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 0d3768826d38c0ac740f8b45cd13346630535f2b ] + +Instead of storing the queue's active job in four different variables, +store the active job inside the queue's state. This way, it's possible +to access all active jobs using an index based in `enum v3d_queue`. + +Reviewed-by: Iago Toral Quiroga +Reviewed-by: Melissa Wen +Link: https://lore.kernel.org/r/20250826-v3d-queue-lock-v3-2-979efc43e490@igalia.com +Signed-off-by: Maíra Canal +Stable-dep-of: 7f93fad5ea0a ("drm/v3d: Skip CSD when it has zeroed workgroups") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_drv.h | 8 +++----- + drivers/gpu/drm/v3d/v3d_gem.c | 5 +++-- + drivers/gpu/drm/v3d/v3d_irq.c | 24 ++++++++++++++---------- + drivers/gpu/drm/v3d/v3d_sched.c | 26 ++++++++++++++++++-------- + 4 files changed, 38 insertions(+), 25 deletions(-) + +diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h +index a366ea208787df..c1e389635de816 100644 +--- a/drivers/gpu/drm/v3d/v3d_drv.h ++++ b/drivers/gpu/drm/v3d/v3d_drv.h +@@ -26,6 +26,9 @@ struct v3d_queue_state { + + u64 fence_context; + u64 emit_seqno; ++ ++ /* Currently active job for this queue */ ++ struct v3d_job *active_job; + }; + + /* Performance monitor object. The perform lifetime is controlled by userspace +@@ -110,11 +113,6 @@ struct v3d_dev { + + struct work_struct overflow_mem_work; + +- struct v3d_bin_job *bin_job; +- struct v3d_render_job *render_job; +- struct v3d_tfu_job *tfu_job; +- struct v3d_csd_job *csd_job; +- + struct v3d_queue_state queue[V3D_MAX_QUEUES]; + + /* Spinlock used to synchronize the overflow memory +diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c +index 97755a6b556335..71019a57958ba3 100644 +--- a/drivers/gpu/drm/v3d/v3d_gem.c ++++ b/drivers/gpu/drm/v3d/v3d_gem.c +@@ -1120,14 +1120,15 @@ void + v3d_gem_destroy(struct drm_device *dev) + { + struct v3d_dev *v3d = to_v3d_dev(dev); ++ enum v3d_queue q; + + v3d_sched_fini(v3d); + + /* Waiting for jobs to finish would need to be done before + * unregistering V3D. + */ +- WARN_ON(v3d->bin_job); +- WARN_ON(v3d->render_job); ++ for (q = 0; q < V3D_MAX_QUEUES; q++) ++ WARN_ON(v3d->queue[q].active_job); + + drm_mm_takedown(&v3d->mm); + +diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c +index 641315dbee8b29..1717504742a665 100644 +--- a/drivers/gpu/drm/v3d/v3d_irq.c ++++ b/drivers/gpu/drm/v3d/v3d_irq.c +@@ -40,6 +40,8 @@ v3d_overflow_mem_work(struct work_struct *work) + container_of(work, struct v3d_dev, overflow_mem_work); + struct drm_device *dev = &v3d->drm; + struct v3d_bo *bo = v3d_bo_create(dev, NULL /* XXX: GMP */, 256 * 1024); ++ struct v3d_queue_state *queue = &v3d->queue[V3D_BIN]; ++ struct v3d_bin_job *bin_job; + struct drm_gem_object *obj; + unsigned long irqflags; + +@@ -59,13 +61,15 @@ v3d_overflow_mem_work(struct work_struct *work) + * some binner pool anyway. + */ + spin_lock_irqsave(&v3d->job_lock, irqflags); +- if (!v3d->bin_job) { ++ bin_job = (struct v3d_bin_job *)queue->active_job; ++ ++ if (!bin_job) { + spin_unlock_irqrestore(&v3d->job_lock, irqflags); + goto out; + } + + drm_gem_object_get(obj); +- list_add_tail(&bo->unref_head, &v3d->bin_job->render->unref_list); ++ list_add_tail(&bo->unref_head, &bin_job->render->unref_list); + spin_unlock_irqrestore(&v3d->job_lock, irqflags); + + V3D_CORE_WRITE(0, V3D_PTB_BPOA, bo->node.start << PAGE_SHIFT); +@@ -99,11 +103,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_FLDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->bin_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_BIN].active_job->irq_fence); + + trace_v3d_bcl_irq(&v3d->drm, fence->seqno); + +- v3d->bin_job = NULL; ++ v3d->queue[V3D_BIN].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -111,11 +115,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_FRDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->render_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_RENDER].active_job->irq_fence); + + trace_v3d_rcl_irq(&v3d->drm, fence->seqno); + +- v3d->render_job = NULL; ++ v3d->queue[V3D_RENDER].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -123,11 +127,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_CSDDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->csd_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_CSD].active_job->irq_fence); + + trace_v3d_csd_irq(&v3d->drm, fence->seqno); + +- v3d->csd_job = NULL; ++ v3d->queue[V3D_CSD].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -162,11 +166,11 @@ v3d_hub_irq(int irq, void *arg) + + if (intsts & V3D_HUB_INT_TFUC) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->tfu_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_TFU].active_job->irq_fence); + + trace_v3d_tfu_irq(&v3d->drm, fence->seqno); + +- v3d->tfu_job = NULL; ++ v3d->queue[V3D_TFU].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c +index 41493cf3d03b81..ff91cdb75bb912 100644 +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -80,14 +80,18 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) + struct dma_fence *fence; + unsigned long irqflags; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ spin_lock_irqsave(&v3d->job_lock, irqflags); ++ v3d->queue[V3D_BIN].active_job = NULL; ++ spin_unlock_irqrestore(&v3d->job_lock, irqflags); + return NULL; ++ } + + /* Lock required around bin_job update vs + * v3d_overflow_mem_work(). + */ + spin_lock_irqsave(&v3d->job_lock, irqflags); +- v3d->bin_job = job; ++ v3d->queue[V3D_BIN].active_job = &job->base; + /* Clear out the overflow allocation, so we don't + * reuse the overflow attached to a previous job. + */ +@@ -134,10 +138,12 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_RENDER].active_job = NULL; + return NULL; ++ } + +- v3d->render_job = job; ++ v3d->queue[V3D_RENDER].active_job = &job->base; + + /* Can we avoid this flush? We need to be careful of + * scheduling, though -- imagine job0 rendering to texture and +@@ -179,10 +185,12 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job) + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_TFU].active_job = NULL; + return NULL; ++ } + +- v3d->tfu_job = job; ++ v3d->queue[V3D_TFU].active_job = &job->base; + + fence = v3d_fence_create(v3d, V3D_TFU); + if (IS_ERR(fence)) +@@ -221,10 +229,12 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) + struct dma_fence *fence; + int i; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_CSD].active_job = NULL; + return NULL; ++ } + +- v3d->csd_job = job; ++ v3d->queue[V3D_CSD].active_job = &job->base; + + v3d_invalidate_caches(v3d); + +-- +2.53.0 + diff --git a/queue-6.1/ip6_vti-set-netns_immutable-on-the-fallback-device.patch b/queue-6.1/ip6_vti-set-netns_immutable-on-the-fallback-device.patch new file mode 100644 index 0000000000..434444dc77 --- /dev/null +++ b/queue-6.1/ip6_vti-set-netns_immutable-on-the-fallback-device.patch @@ -0,0 +1,49 @@ +From 3fd8f6d9ae3d423f2d5f06851a208b859300b0eb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Jun 2026 15:59:18 +0000 +Subject: ip6_vti: set netns_immutable on the fallback device. + +From: Eric Dumazet + +[ Upstream commit d289d5307762d1838aaece22c6b6fcad9e8865f9 ] + +john1988 and Noam Rathaus reported that vti6_init_net() does not set the +netns_immutable flag on the per-netns fallback tunnel device (ip6_vti0). + +Other similar tunnel drivers (like ip6_tunnel, sit, ip6_gre, and ip_tunnel) +correctly set this flag during their fallback device initialization to +prevent them from being moved to another network namespace. + +Fixes: 61220ab34948 ("vti6: Enable namespace changing") +Reported-by: Noam Rathaus +Signed-off-by: Eric Dumazet +Cc: Steffen Klassert +Reviewed-by: Nicolas Dichtel +Link: https://patch.msgid.link/20260608155918.787644-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +[Salvatore Bonaccorso: Backport for version without 0c493da86374 ("net: +rename netns_local to netns_immutable") in v6.15-rc1 and without +05c1280a2bcf ("netdev_features: convert NETIF_F_NETNS_LOCAL to +dev->netns_local") in v6.12-rc1 and use NETIF_F_NETNS_LOCAL device +feature.] +Signed-off-by: Salvatore Bonaccorso +Signed-off-by: Sasha Levin +--- + net/ipv6/ip6_vti.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c +index 8808067df5168c..aa37ce710cf49a 100644 +--- a/net/ipv6/ip6_vti.c ++++ b/net/ipv6/ip6_vti.c +@@ -1168,6 +1168,7 @@ static int __net_init vti6_init_net(struct net *net) + goto err_alloc_dev; + dev_net_set(ip6n->fb_tnl_dev, net); + ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops; ++ ip6n->fb_tnl_dev->features |= NETIF_F_NETNS_LOCAL; + + err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); + if (err < 0) +-- +2.53.0 + diff --git a/queue-6.1/netfilter-nf_tables-always-increment-set-element-cou.patch b/queue-6.1/netfilter-nf_tables-always-increment-set-element-cou.patch new file mode 100644 index 0000000000..9227aa750d --- /dev/null +++ b/queue-6.1/netfilter-nf_tables-always-increment-set-element-cou.patch @@ -0,0 +1,52 @@ +From f78323c471860f7b9b7445f03f1825b0321199cb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 02:28:48 -0700 +Subject: netfilter: nf_tables: always increment set element count + +From: Florian Westphal + +[ Upstream commit d4b7f29eb85c93893bc27388b37709efbc3c9a0e ] + +At this time, set->nelems counter only increments when the set has +a maximum size. + +All set elements decrement the counter unconditionally, this is +confusing. + +Increment the counter unconditionally to make this symmetrical. +This would also allow changing the set maximum size after set creation +in a later patch. + +Signed-off-by: Florian Westphal +[ Shivani: Modified to apply on 6.1.y ] +Signed-off-by: Shivani Agarwal +Signed-off-by: Sasha Levin +--- + net/netfilter/nf_tables_api.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 201e2cc0453992..b6b7b0b3539dc9 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -6699,10 +6699,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, + goto err_element_clash; + } + +- if (!(flags & NFT_SET_ELEM_CATCHALL) && set->size && +- !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) { +- err = -ENFILE; +- goto err_set_full; ++ if (!(flags & NFT_SET_ELEM_CATCHALL)) { ++ unsigned int max = set->size ? set->size + set->ndeact : UINT_MAX; ++ ++ if (!atomic_add_unless(&set->nelems, 1, max)) { ++ err = -ENFILE; ++ goto err_set_full; ++ } + } + + nft_trans_elem(trans) = elem; +-- +2.53.0 + diff --git a/queue-6.1/netfilter-nf_tables-fix-set-size-with-rbtree-backend.patch b/queue-6.1/netfilter-nf_tables-fix-set-size-with-rbtree-backend.patch new file mode 100644 index 0000000000..0cdf9115a0 --- /dev/null +++ b/queue-6.1/netfilter-nf_tables-fix-set-size-with-rbtree-backend.patch @@ -0,0 +1,220 @@ +From f238a9690304b92e342f798e21f64de513ff5ca1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 02:28:49 -0700 +Subject: netfilter: nf_tables: fix set size with rbtree backend + +From: Pablo Neira Ayuso + +[ Upstream commit 8d738c1869f611955d91d8d0fd0012d9ef207201 ] + +The existing rbtree implementation uses singleton elements to represent +ranges, however, userspace provides a set size according to the number +of ranges in the set. + +Adjust provided userspace set size to the number of singleton elements +in the kernel by multiplying the range by two. + +Check if the no-match all-zero element is already in the set, in such +case release one slot in the set size. + +Fixes: 0ed6389c483d ("netfilter: nf_tables: rename set implementations") +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +[ Shivani: Modified to apply on 6.1.y ] +Signed-off-by: Shivani Agarwal +Signed-off-by: Sasha Levin +--- + include/net/netfilter/nf_tables.h | 6 ++++ + net/netfilter/nf_tables_api.c | 49 +++++++++++++++++++++++++++++-- + net/netfilter/nft_set_rbtree.c | 43 +++++++++++++++++++++++++++ + 3 files changed, 96 insertions(+), 2 deletions(-) + +diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h +index dafa0a32e6e1df..3329c2eaea9f07 100644 +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -422,6 +422,9 @@ struct nft_set_ext; + * @remove: remove element from set + * @walk: iterate over all set elements + * @get: get set elements ++ * @ksize: kernel set size ++ * @usize: userspace set size ++ * @adjust_maxsize: delta to adjust maximum set size + * @privsize: function to return size of set private data + * @init: initialize private data of new set instance + * @destroy: destroy private data of set instance +@@ -470,6 +473,9 @@ struct nft_set_ops { + const struct nft_set *set, + const struct nft_set_elem *elem, + unsigned int flags); ++ u32 (*ksize)(u32 size); ++ u32 (*usize)(u32 size); ++ u32 (*adjust_maxsize)(const struct nft_set *set); + void (*commit)(struct nft_set *set); + void (*abort)(const struct nft_set *set); + u64 (*privsize)(const struct nlattr * const nla[], +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index b6b7b0b3539dc9..0d406dab5cac19 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -4277,6 +4277,14 @@ static int nf_tables_fill_set_concat(struct sk_buff *skb, + return 0; + } + ++static u32 nft_set_userspace_size(const struct nft_set_ops *ops, u32 size) ++{ ++ if (ops->usize) ++ return ops->usize(size); ++ ++ return size; ++} ++ + static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, + const struct nft_set *set, u16 event, u16 flags) + { +@@ -4341,7 +4349,8 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, + if (!nest) + goto nla_put_failure; + if (set->size && +- nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size))) ++ nla_put_be32(skb, NFTA_SET_DESC_SIZE, ++ htonl(nft_set_userspace_size(set->ops, set->size)))) + goto nla_put_failure; + + if (set->field_count > 1 && +@@ -4711,6 +4720,15 @@ static bool nft_set_is_same(const struct nft_set *set, + return true; + } + ++static u32 nft_set_kernel_size(const struct nft_set_ops *ops, ++ const struct nft_set_desc *desc) ++{ ++ if (ops->ksize) ++ return ops->ksize(desc->size); ++ ++ return desc->size; ++} ++ + static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, + const struct nlattr * const nla[]) + { +@@ -4893,6 +4911,9 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, + if (err < 0) + return err; + ++ if (desc.size) ++ desc.size = nft_set_kernel_size(set->ops, &desc); ++ + err = 0; + if (!nft_set_is_same(set, &desc, exprs, num_exprs, flags)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_SET_NAME]); +@@ -4915,6 +4936,9 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, + if (IS_ERR(ops)) + return PTR_ERR(ops); + ++ if (desc.size) ++ desc.size = nft_set_kernel_size(ops, &desc); ++ + udlen = 0; + if (nla[NFTA_SET_USERDATA]) + udlen = nla_len(nla[NFTA_SET_USERDATA]); +@@ -6356,6 +6380,27 @@ static bool nft_setelem_valid_key_end(const struct nft_set *set, + return true; + } + ++static u32 nft_set_maxsize(const struct nft_set *set) ++{ ++ u32 maxsize, delta; ++ ++ if (!set->size) ++ return UINT_MAX; ++ ++ if (set->ops->adjust_maxsize) ++ delta = set->ops->adjust_maxsize(set); ++ else ++ delta = 0; ++ ++ if (check_add_overflow(set->size, set->ndeact, &maxsize)) ++ return UINT_MAX; ++ ++ if (check_add_overflow(maxsize, delta, &maxsize)) ++ return UINT_MAX; ++ ++ return maxsize; ++} ++ + static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, + const struct nlattr *attr, u32 nlmsg_flags) + { +@@ -6700,7 +6745,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, + } + + if (!(flags & NFT_SET_ELEM_CATCHALL)) { +- unsigned int max = set->size ? set->size + set->ndeact : UINT_MAX; ++ unsigned int max = nft_set_maxsize(set); + + if (!atomic_add_unless(&set->nelems, 1, max)) { + err = -ENFILE; +diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c +index 23e4e656f7f0cb..433094c4200f97 100644 +--- a/net/netfilter/nft_set_rbtree.c ++++ b/net/netfilter/nft_set_rbtree.c +@@ -773,6 +773,46 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features, + return true; + } + ++/* rbtree stores ranges as singleton elements, each range is composed of two ++ * elements ... ++ */ ++static u32 nft_rbtree_ksize(u32 size) ++{ ++ return size * 2; ++} ++ ++/* ... hide this detail to userspace. */ ++static u32 nft_rbtree_usize(u32 size) ++{ ++ if (!size) ++ return 0; ++ ++ return size / 2; ++} ++ ++static u32 nft_rbtree_adjust_maxsize(const struct nft_set *set) ++{ ++ struct nft_rbtree *priv = nft_set_priv(set); ++ struct nft_rbtree_elem *rbe; ++ struct rb_node *node; ++ const void *key; ++ ++ node = rb_last(&priv->root); ++ if (!node) ++ return 0; ++ ++ rbe = rb_entry(node, struct nft_rbtree_elem, node); ++ if (!nft_rbtree_interval_end(rbe)) ++ return 0; ++ ++ key = nft_set_ext_key(&rbe->ext); ++ if (memchr(key, 1, set->klen)) ++ return 0; ++ ++ /* this is the all-zero no-match element. */ ++ return 1; ++} ++ + const struct nft_set_type nft_set_rbtree_type = { + .features = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT | NFT_SET_TIMEOUT, + .ops = { +@@ -789,5 +829,8 @@ const struct nft_set_type nft_set_rbtree_type = { + .lookup = nft_rbtree_lookup, + .walk = nft_rbtree_walk, + .get = nft_rbtree_get, ++ .ksize = nft_rbtree_ksize, ++ .usize = nft_rbtree_usize, ++ .adjust_maxsize = nft_rbtree_adjust_maxsize, + }, + }; +-- +2.53.0 + diff --git a/queue-6.1/netfilter-nf_tables-unconditionally-bump-set-nelems-.patch b/queue-6.1/netfilter-nf_tables-unconditionally-bump-set-nelems-.patch new file mode 100644 index 0000000000..ca7f92d2ef --- /dev/null +++ b/queue-6.1/netfilter-nf_tables-unconditionally-bump-set-nelems-.patch @@ -0,0 +1,102 @@ +From ec6344bdb904b27fcd1082ef2f4ac46a53401180 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 02:28:50 -0700 +Subject: netfilter: nf_tables: unconditionally bump set->nelems before + insertion + +From: Pablo Neira Ayuso + +[ Upstream commit def602e498a4f951da95c95b1b8ce8ae68aa733a ] + +In case that the set is full, a new element gets published then removed +without waiting for the RCU grace period, while RCU reader can be +walking over it already. + +To address this issue, add the element transaction even if set is full, +but toggle the set_full flag to report -ENFILE so the abort path safely +unwinds the set to its previous state. + +As for element updates, decrement set->nelems to restore it. + +A simpler fix is to call synchronize_rcu() in the error path. +However, with a large batch adding elements to already maxed-out set, +this could cause noticeable slowdown of such batches. + +Fixes: 35d0ac9070ef ("netfilter: nf_tables: fix set->nelems counting with no NLM_F_EXCL") +Reported-by: Inseo An +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Florian Westphal +[ Minor conflict resolved. ] +Signed-off-by: Li hongliang <1468888505@139.com> +Signed-off-by: Sasha Levin +[ Shivani: Modified to apply on 6.1.y ] +Signed-off-by: Shivani Agarwal +Signed-off-by: Sasha Levin +--- + net/netfilter/nf_tables_api.c | 28 +++++++++++++++------------- + 1 file changed, 15 insertions(+), 13 deletions(-) + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 0d406dab5cac19..735c0210104076 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -6417,6 +6417,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, + struct nft_data_desc desc; + enum nft_registers dreg; + struct nft_trans *trans; ++ bool set_full = false; + u64 timeout; + u64 expiration; + int err, i; +@@ -6709,10 +6710,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, + if (err < 0) + goto err_elem_free; + ++ if (!(flags & NFT_SET_ELEM_CATCHALL)) { ++ unsigned int max = nft_set_maxsize(set), nelems; ++ ++ nelems = atomic_inc_return(&set->nelems); ++ if (nelems > max) ++ set_full = true; ++ } ++ + trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); + if (trans == NULL) { + err = -ENOMEM; +- goto err_elem_free; ++ goto err_set_size; + } + + ext->genmask = nft_genmask_cur(ctx->net); +@@ -6744,23 +6753,16 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, + goto err_element_clash; + } + +- if (!(flags & NFT_SET_ELEM_CATCHALL)) { +- unsigned int max = nft_set_maxsize(set); +- +- if (!atomic_add_unless(&set->nelems, 1, max)) { +- err = -ENFILE; +- goto err_set_full; +- } +- } +- + nft_trans_elem(trans) = elem; + nft_trans_commit_list_add_tail(ctx->net, trans); +- return 0; + +-err_set_full: +- nft_setelem_remove(ctx->net, set, &elem); ++ return set_full ? -ENFILE : 0; ++ + err_element_clash: + kfree(trans); ++err_set_size: ++ if (!(flags & NFT_SET_ELEM_CATCHALL)) ++ atomic_dec(&set->nelems); + err_elem_free: + nf_tables_set_elem_destroy(ctx, set, elem.priv); + err_parse_data: +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-check-for-timeout-in-perf_link-test.patch b/queue-6.1/selftests-bpf-check-for-timeout-in-perf_link-test.patch new file mode 100644 index 0000000000..fffe3dc510 --- /dev/null +++ b/queue-6.1/selftests-bpf-check-for-timeout-in-perf_link-test.patch @@ -0,0 +1,77 @@ +From ba722512da7e1ed5cc6d9f67e432ee8da4493864 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 12:59:48 +0800 +Subject: selftests/bpf: Check for timeout in perf_link test + +From: Ihor Solodrai + +commit e6c209da7e0e9aaf955a7b59e91ed78c2b6c96fb upstream. + +Recently perf_link test started unreliably failing on libbpf CI: + * https://github.com/libbpf/libbpf/actions/runs/11260672407/job/31312405473 + * https://github.com/libbpf/libbpf/actions/runs/11260992334/job/31315514626 + * https://github.com/libbpf/libbpf/actions/runs/11263162459/job/31320458251 + +Part of the test is running a dummy loop for a while and then checking +for a counter incremented by the test program. + +Instead of waiting for an arbitrary number of loop iterations once, +check for the test counter in a loop and use get_time_ns() helper to +enforce a 100ms timeout. + +v1: https://lore.kernel.org/bpf/zuRd072x9tumn2iN4wDNs5av0nu5nekMNV4PkR-YwCT10eFFTrUtZBRkLWFbrcCe7guvLStGQlhibo8qWojCO7i2-NGajes5GYIyynexD-w=@pm.me/ + +Signed-off-by: Ihor Solodrai +Signed-off-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20241011153104.249800-1-ihor.solodrai@pm.me +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + .../testing/selftests/bpf/prog_tests/perf_link.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/perf_link.c b/tools/testing/selftests/bpf/prog_tests/perf_link.c +index 224eba6fef2ee1..d734526d6a16b1 100644 +--- a/tools/testing/selftests/bpf/prog_tests/perf_link.c ++++ b/tools/testing/selftests/bpf/prog_tests/perf_link.c +@@ -4,8 +4,12 @@ + #include + #include + #include ++#include "testing_helpers.h" + #include "test_perf_link.skel.h" + ++#define BURN_TIMEOUT_MS 100 ++#define BURN_TIMEOUT_NS BURN_TIMEOUT_MS * 1000000 ++ + static void burn_cpu(void) + { + volatile int j = 0; +@@ -32,6 +36,7 @@ void serial_test_perf_link(void) + int run_cnt_before, run_cnt_after; + struct bpf_link_info info; + __u32 info_len = sizeof(info); ++ __u64 timeout_time_ns; + + /* create perf event */ + memset(&attr, 0, sizeof(attr)); +@@ -63,8 +68,14 @@ void serial_test_perf_link(void) + ASSERT_GT(info.prog_id, 0, "link_prog_id"); + + /* ensure we get at least one perf_event prog execution */ +- burn_cpu(); +- ASSERT_GT(skel->bss->run_cnt, 0, "run_cnt"); ++ timeout_time_ns = get_time_ns() + BURN_TIMEOUT_NS; ++ while (true) { ++ burn_cpu(); ++ if (skel->bss->run_cnt > 0) ++ break; ++ if (!ASSERT_LT(get_time_ns(), timeout_time_ns, "run_cnt_timeout")) ++ break; ++ } + + /* perf_event is still active, but we close link and BPF program + * shouldn't be executed anymore +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-move-get_time_ns-to-testing_helpers.h.patch b/queue-6.1/selftests-bpf-move-get_time_ns-to-testing_helpers.h.patch new file mode 100644 index 0000000000..59e2989887 --- /dev/null +++ b/queue-6.1/selftests-bpf-move-get_time_ns-to-testing_helpers.h.patch @@ -0,0 +1,96 @@ +From bbf381823f990ea60ba5a71f70d83a1ed4d1e563 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Jun 2026 12:59:47 +0800 +Subject: selftests/bpf: Move get_time_ns to testing_helpers.h + +From: Jiri Olsa + +commit 3830d04a7401f277185e4439eb259c1b13e76788 upstream. + +We'd like to have single copy of get_time_ns used b bench and test_progs, +but we can't just include bench.h, because of conflicting 'struct env' +objects. + +Moving get_time_ns to testing_helpers.h which is being included by both +bench and test_progs objects. + +Signed-off-by: Jiri Olsa +Link: https://lore.kernel.org/r/20230809083440.3209381-19-jolsa@kernel.org +Signed-off-by: Alexei Starovoitov +Stable-dep-of: e6c209da7e0e ("selftests/bpf: Check for timeout in perf_link test") +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/bench.h | 9 --------- + .../selftests/bpf/prog_tests/kprobe_multi_test.c | 8 -------- + tools/testing/selftests/bpf/testing_helpers.h | 10 ++++++++++ + 3 files changed, 10 insertions(+), 17 deletions(-) + +diff --git a/tools/testing/selftests/bpf/bench.h b/tools/testing/selftests/bpf/bench.h +index 66959387148375..2c94c57885af85 100644 +--- a/tools/testing/selftests/bpf/bench.h ++++ b/tools/testing/selftests/bpf/bench.h +@@ -79,15 +79,6 @@ void grace_period_latency_basic_stats(struct bench_res res[], int res_cnt, + void grace_period_ticks_basic_stats(struct bench_res res[], int res_cnt, + struct basic_stats *gp_stat); + +-static inline __u64 get_time_ns(void) +-{ +- struct timespec t; +- +- clock_gettime(CLOCK_MONOTONIC, &t); +- +- return (u64)t.tv_sec * 1000000000 + t.tv_nsec; +-} +- + static inline void atomic_inc(long *value) + { + (void)__atomic_add_fetch(value, 1, __ATOMIC_RELAXED); +diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c +index 0d82e28aed1ac0..8086e28346466f 100644 +--- a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c ++++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c +@@ -304,14 +304,6 @@ static void test_attach_api_fails(void) + kprobe_multi__destroy(skel); + } + +-static inline __u64 get_time_ns(void) +-{ +- struct timespec t; +- +- clock_gettime(CLOCK_MONOTONIC, &t); +- return (__u64) t.tv_sec * 1000000000 + t.tv_nsec; +-} +- + static size_t symbol_hash(const void *key, void *ctx __maybe_unused) + { + return str_hash((const char *) key); +diff --git a/tools/testing/selftests/bpf/testing_helpers.h b/tools/testing/selftests/bpf/testing_helpers.h +index f72fb24f8e90fb..ba4f205e7bc757 100644 +--- a/tools/testing/selftests/bpf/testing_helpers.h ++++ b/tools/testing/selftests/bpf/testing_helpers.h +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + + int parse_num_list(const char *s, bool **set, int *set_len); + __u32 link_info_prog_id(const struct bpf_link *link, struct bpf_link_info *info); +@@ -30,4 +31,13 @@ int load_bpf_testmod(bool verbose); + void unload_bpf_testmod(bool verbose); + int kern_sync_rcu(void); + ++static inline __u64 get_time_ns(void) ++{ ++ struct timespec t; ++ ++ clock_gettime(CLOCK_MONOTONIC, &t); ++ ++ return (u64)t.tv_sec * 1000000000 + t.tv_nsec; ++} ++ + #endif /* __TESTING_HELPERS_H */ +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 5be786350e..33d9e82774 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -5,3 +5,12 @@ net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch selftests-bpf-move-sys-macro-into-the-test_progs.h.patch kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch +drm-amd-display-bound-vbios-record-chain-walk-loops.patch +ip6_vti-set-netns_immutable-on-the-fallback-device.patch +netfilter-nf_tables-always-increment-set-element-cou.patch +netfilter-nf_tables-fix-set-size-with-rbtree-backend.patch +netfilter-nf_tables-unconditionally-bump-set-nelems-.patch +selftests-bpf-move-get_time_ns-to-testing_helpers.h.patch +selftests-bpf-check-for-timeout-in-perf_link-test.patch +drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch +drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch diff --git a/queue-6.12/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch b/queue-6.12/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch new file mode 100644 index 0000000000..e0d2425671 --- /dev/null +++ b/queue-6.12/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch @@ -0,0 +1,80 @@ +From 8a0b8e4ab12bb62db9880c6dc558e0cbd466fa14 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 14:50:15 -0300 +Subject: drm/v3d: Skip CSD when it has zeroed workgroups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 7f93fad5ea0affc9e1505dd0f7596c0fdb496213 ] + +A compute shader dispatch encodes its workgroup counts in the CFG0..CFG2 +registers. Kicking off a dispatch with a zero count in any of the three +dimensions is invalid. First, the hardware will process 0 as 65536, +while the user-space driver exposes a maximum of 65535. Over that, a +submission with a zeroed workgroup dimension should be a no-op. + +These zeroed counts can reach the dispatch path through an indirect CSD +job, whose workgroup counts are only known once the indirect buffer is +read and may legitimately be zero, but such scenario should only result in +a no-op. + +Overwrite the indirect CSD job workgroup counts with the indirect BO +ones, even if they are zeroed, and don't submit the job to the hardware +when any of the workgroup counts is zero, so the job completes immediately +instead of running the shader. + +Cc: stable@vger.kernel.org +Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.") +Suggested-by: Jose Maria Casanova Crespo +Reviewed-by: Iago Toral Quiroga +Link: https://patch.msgid.link/20260602-v3d-fix-indirect-csd-v4-2-654309e32bc0@igalia.com +Signed-off-by: Maíra Canal +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_sched.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c +index f2a2c17a58c687..f2bac920af899d 100644 +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -368,6 +368,16 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) + return NULL; + } + ++ /* The HW interprets a workgroup size of 0 as 65536; however, the ++ * user-space driver exposes a maximum of 65535. Therefore, a 0 in ++ * any dimension means that we have no workgroups and the compute ++ * shader should not be dispatched. ++ */ ++ if (!V3D_GET_FIELD(job->args.cfg[0], V3D_CSD_QUEUED_CFG0_NUM_WGS_X) || ++ !V3D_GET_FIELD(job->args.cfg[1], V3D_CSD_QUEUED_CFG1_NUM_WGS_Y) || ++ !V3D_GET_FIELD(job->args.cfg[2], V3D_CSD_QUEUED_CFG2_NUM_WGS_Z)) ++ return NULL; ++ + v3d->queue[V3D_CSD].active_job = &job->base; + + v3d_invalidate_caches(v3d); +@@ -418,13 +428,13 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job) + + wg_counts = (uint32_t *)(bo->vaddr + indirect_csd->offset); + +- if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) +- goto unmap_bo; +- + args->cfg[0] = wg_counts[0] << V3D_CSD_CFG012_WG_COUNT_SHIFT; + args->cfg[1] = wg_counts[1] << V3D_CSD_CFG012_WG_COUNT_SHIFT; + args->cfg[2] = wg_counts[2] << V3D_CSD_CFG012_WG_COUNT_SHIFT; + ++ if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0) ++ goto unmap_bo; ++ + num_batches = DIV_ROUND_UP(indirect_csd->wg_size, 16) * + (wg_counts[0] * wg_counts[1] * wg_counts[2]); + +-- +2.53.0 + diff --git a/queue-6.12/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch b/queue-6.12/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch new file mode 100644 index 0000000000..631c11a1ca --- /dev/null +++ b/queue-6.12/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch @@ -0,0 +1,265 @@ +From 34da1e3b182ae0bf1af3d625686d6e74254cbd45 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 Aug 2025 11:18:59 -0300 +Subject: drm/v3d: Store the active job inside the queue's state +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 0d3768826d38c0ac740f8b45cd13346630535f2b ] + +Instead of storing the queue's active job in four different variables, +store the active job inside the queue's state. This way, it's possible +to access all active jobs using an index based in `enum v3d_queue`. + +Reviewed-by: Iago Toral Quiroga +Reviewed-by: Melissa Wen +Link: https://lore.kernel.org/r/20250826-v3d-queue-lock-v3-2-979efc43e490@igalia.com +Signed-off-by: Maíra Canal +Stable-dep-of: 7f93fad5ea0a ("drm/v3d: Skip CSD when it has zeroed workgroups") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_drv.h | 7 ++-- + drivers/gpu/drm/v3d/v3d_gem.c | 7 ++-- + drivers/gpu/drm/v3d/v3d_irq.c | 62 +++++++++++++-------------------- + drivers/gpu/drm/v3d/v3d_sched.c | 26 +++++++++----- + 4 files changed, 48 insertions(+), 54 deletions(-) + +diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h +index d4b0549205c29e..b6e11968fba47b 100644 +--- a/drivers/gpu/drm/v3d/v3d_drv.h ++++ b/drivers/gpu/drm/v3d/v3d_drv.h +@@ -59,6 +59,9 @@ struct v3d_queue_state { + + /* Stores the GPU stats for this queue in the global context. */ + struct v3d_stats stats; ++ ++ /* Currently active job for this queue */ ++ struct v3d_job *active_job; + }; + + /* Performance monitor object. The perform lifetime is controlled by userspace +@@ -147,10 +150,6 @@ struct v3d_dev { + + struct work_struct overflow_mem_work; + +- struct v3d_bin_job *bin_job; +- struct v3d_render_job *render_job; +- struct v3d_tfu_job *tfu_job; +- struct v3d_csd_job *csd_job; + struct v3d_cpu_job *cpu_job; + + struct v3d_queue_state queue[V3D_MAX_QUEUES]; +diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c +index 6b6ba7a68fcb40..cf3b93101429c2 100644 +--- a/drivers/gpu/drm/v3d/v3d_gem.c ++++ b/drivers/gpu/drm/v3d/v3d_gem.c +@@ -304,16 +304,15 @@ void + v3d_gem_destroy(struct drm_device *dev) + { + struct v3d_dev *v3d = to_v3d_dev(dev); ++ enum v3d_queue q; + + v3d_sched_fini(v3d); + + /* Waiting for jobs to finish would need to be done before + * unregistering V3D. + */ +- WARN_ON(v3d->bin_job); +- WARN_ON(v3d->render_job); +- WARN_ON(v3d->tfu_job); +- WARN_ON(v3d->csd_job); ++ for (q = 0; q < V3D_MAX_QUEUES; q++) ++ WARN_ON(v3d->queue[q].active_job); + + drm_mm_takedown(&v3d->mm); + +diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c +index b98e1a4b33c71c..2464ea4d935d04 100644 +--- a/drivers/gpu/drm/v3d/v3d_irq.c ++++ b/drivers/gpu/drm/v3d/v3d_irq.c +@@ -42,6 +42,8 @@ v3d_overflow_mem_work(struct work_struct *work) + container_of(work, struct v3d_dev, overflow_mem_work); + struct drm_device *dev = &v3d->drm; + struct v3d_bo *bo = v3d_bo_create(dev, NULL /* XXX: GMP */, 256 * 1024); ++ struct v3d_queue_state *queue = &v3d->queue[V3D_BIN]; ++ struct v3d_bin_job *bin_job; + struct drm_gem_object *obj; + unsigned long irqflags; + +@@ -61,13 +63,15 @@ v3d_overflow_mem_work(struct work_struct *work) + * some binner pool anyway. + */ + spin_lock_irqsave(&v3d->job_lock, irqflags); +- if (!v3d->bin_job) { ++ bin_job = (struct v3d_bin_job *)queue->active_job; ++ ++ if (!bin_job) { + spin_unlock_irqrestore(&v3d->job_lock, irqflags); + goto out; + } + + drm_gem_object_get(obj); +- list_add_tail(&bo->unref_head, &v3d->bin_job->render->unref_list); ++ list_add_tail(&bo->unref_head, &bin_job->render->unref_list); + spin_unlock_irqrestore(&v3d->job_lock, irqflags); + + v3d_mmu_flush_all(v3d); +@@ -79,6 +83,20 @@ v3d_overflow_mem_work(struct work_struct *work) + drm_gem_object_put(obj); + } + ++static void ++v3d_irq_signal_fence(struct v3d_dev *v3d, enum v3d_queue q, ++ void (*trace_irq)(struct drm_device *, uint64_t)) ++{ ++ struct v3d_queue_state *queue = &v3d->queue[q]; ++ struct v3d_fence *fence = to_v3d_fence(queue->active_job->irq_fence); ++ ++ v3d_job_update_stats(queue->active_job, q); ++ trace_irq(&v3d->drm, fence->seqno); ++ ++ queue->active_job = NULL; ++ dma_fence_signal(&fence->base); ++} ++ + static irqreturn_t + v3d_irq(int irq, void *arg) + { +@@ -102,41 +120,17 @@ v3d_irq(int irq, void *arg) + } + + if (intsts & V3D_INT_FLDONE) { +- struct v3d_fence *fence = +- to_v3d_fence(v3d->bin_job->base.irq_fence); +- +- v3d_job_update_stats(&v3d->bin_job->base, V3D_BIN); +- trace_v3d_bcl_irq(&v3d->drm, fence->seqno); +- +- v3d->bin_job = NULL; +- dma_fence_signal(&fence->base); +- ++ v3d_irq_signal_fence(v3d, V3D_BIN, trace_v3d_bcl_irq); + status = IRQ_HANDLED; + } + + if (intsts & V3D_INT_FRDONE) { +- struct v3d_fence *fence = +- to_v3d_fence(v3d->render_job->base.irq_fence); +- +- v3d_job_update_stats(&v3d->render_job->base, V3D_RENDER); +- trace_v3d_rcl_irq(&v3d->drm, fence->seqno); +- +- v3d->render_job = NULL; +- dma_fence_signal(&fence->base); +- ++ v3d_irq_signal_fence(v3d, V3D_RENDER, trace_v3d_rcl_irq); + status = IRQ_HANDLED; + } + + if (intsts & V3D_INT_CSDDONE(v3d->ver)) { +- struct v3d_fence *fence = +- to_v3d_fence(v3d->csd_job->base.irq_fence); +- +- v3d_job_update_stats(&v3d->csd_job->base, V3D_CSD); +- trace_v3d_csd_irq(&v3d->drm, fence->seqno); +- +- v3d->csd_job = NULL; +- dma_fence_signal(&fence->base); +- ++ v3d_irq_signal_fence(v3d, V3D_CSD, trace_v3d_csd_irq); + status = IRQ_HANDLED; + } + +@@ -168,15 +162,7 @@ v3d_hub_irq(int irq, void *arg) + V3D_WRITE(V3D_HUB_INT_CLR, intsts); + + if (intsts & V3D_HUB_INT_TFUC) { +- struct v3d_fence *fence = +- to_v3d_fence(v3d->tfu_job->base.irq_fence); +- +- v3d_job_update_stats(&v3d->tfu_job->base, V3D_TFU); +- trace_v3d_tfu_irq(&v3d->drm, fence->seqno); +- +- v3d->tfu_job = NULL; +- dma_fence_signal(&fence->base); +- ++ v3d_irq_signal_fence(v3d, V3D_TFU, trace_v3d_tfu_irq); + status = IRQ_HANDLED; + } + +diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c +index 8e3c8ffc2a428f..f2a2c17a58c687 100644 +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -208,14 +208,18 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) + struct dma_fence *fence; + unsigned long irqflags; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ spin_lock_irqsave(&v3d->job_lock, irqflags); ++ v3d->queue[V3D_BIN].active_job = NULL; ++ spin_unlock_irqrestore(&v3d->job_lock, irqflags); + return NULL; ++ } + + /* Lock required around bin_job update vs + * v3d_overflow_mem_work(). + */ + spin_lock_irqsave(&v3d->job_lock, irqflags); +- v3d->bin_job = job; ++ v3d->queue[V3D_BIN].active_job = &job->base; + /* Clear out the overflow allocation, so we don't + * reuse the overflow attached to a previous job. + */ +@@ -263,10 +267,12 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_RENDER].active_job = NULL; + return NULL; ++ } + +- v3d->render_job = job; ++ v3d->queue[V3D_RENDER].active_job = &job->base; + + /* Can we avoid this flush? We need to be careful of + * scheduling, though -- imagine job0 rendering to texture and +@@ -309,10 +315,12 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job) + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_TFU].active_job = NULL; + return NULL; ++ } + +- v3d->tfu_job = job; ++ v3d->queue[V3D_TFU].active_job = &job->base; + + fence = v3d_fence_create(v3d, V3D_TFU); + if (IS_ERR(fence)) +@@ -355,10 +363,12 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) + struct dma_fence *fence; + int i, csd_cfg0_reg; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_CSD].active_job = NULL; + return NULL; ++ } + +- v3d->csd_job = job; ++ v3d->queue[V3D_CSD].active_job = &job->base; + + v3d_invalidate_caches(v3d); + +-- +2.53.0 + diff --git a/queue-6.12/drm-xe-display-fix-oops-in-suspend-shutdown-without-.patch b/queue-6.12/drm-xe-display-fix-oops-in-suspend-shutdown-without-.patch new file mode 100644 index 0000000000..87b89322c1 --- /dev/null +++ b/queue-6.12/drm-xe-display-fix-oops-in-suspend-shutdown-without-.patch @@ -0,0 +1,85 @@ +From 272ce010d7b652f900c9c62b3ce2acd7402a04f5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 19:09:20 +0300 +Subject: drm/xe/display: fix oops in suspend/shutdown without display + +From: Jani Nikula + +[ Upstream commit 68938cc08e23a94fd881e845837ff918de005ce7 ] + +The xe driver keeps track of whether to probe display, and whether +display hardware is there, using xe->info.probe_display. It gets set to +false if there's no display after intel_display_device_probe(). However, +the display may also be disabled via fuses, detected at a later time in +intel_display_device_info_runtime_init(). + +In this case, the xe driver does for_each_intel_crtc() on uninitialized +mode config in xe_display_flush_cleanup_work(), leading to a NULL +pointer dereference, and generally calls display code with display info +cleared. + +Check for intel_display_device_present() after +intel_display_device_info_runtime_init(), and reset +xe->info.probe_display as necessary. Also do unset_display_features() +for completeness, although display runtime init has already done +that. This will need to be unified across all cases later. + +Move intel_display_device_info_runtime_init() call slightly earlier, +similar to i915, to avoid a bunch of unnecessary setup for no display +cases. + +Note #1: The xe driver has no business doing low level display plumbing +like for_each_intel_crtc() to begin with. It all needs to happen in +display code. + +Note #2: The actual bug is present already in commit 44e694958b95 +("drm/xe/display: Implement display support"), but the oops was likely +introduced later at commit ddf6492e0e50 ("drm/xe/display: Make display +suspend/resume work on discrete"). + +Fixes: 44e694958b95 ("drm/xe/display: Implement display support") +Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7904 +Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/6150 +Cc: stable@vger.kernel.org # v6.8+ +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515160920.1082842-1-jani.nikula@intel.com +Signed-off-by: Jani Nikula +(cherry picked from commit 7c3eb9f47533220888a67266448185fd0775d4da) +Signed-off-by: Matthew Brost +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/display/xe_display.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c +index e164e2d71e1157..de1fd4dff0e869 100644 +--- a/drivers/gpu/drm/xe/display/xe_display.c ++++ b/drivers/gpu/drm/xe/display/xe_display.c +@@ -148,6 +148,15 @@ int xe_display_init_noirq(struct xe_device *xe) + + intel_display_driver_early_probe(xe); + ++ intel_display_device_info_runtime_init(xe); ++ ++ /* Display may have been disabled at runtime init */ ++ if (!has_display(xe)) { ++ xe->info.probe_display = false; ++ unset_display_features(xe); ++ return 0; ++ } ++ + /* Early display init.. */ + intel_opregion_setup(display); + +@@ -159,8 +168,6 @@ int xe_display_init_noirq(struct xe_device *xe) + + intel_bw_init_hw(xe); + +- intel_display_device_info_runtime_init(xe); +- + err = intel_display_driver_probe_noirq(xe); + if (err) { + intel_opregion_cleanup(display); +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 6e61e56e92..fd2301629c 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -6,3 +6,6 @@ gpiolib-extract-gpiochip_choose_fwnode-for-wider-use.patch gpiolib-remove-redundant-assignment-of-return-variab.patch gpio-fix-resource-leaks-on-errors-in-gpiochip_add_da.patch io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch +drm-xe-display-fix-oops-in-suspend-shutdown-without-.patch +drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch +drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch diff --git a/queue-6.18/net-stmmac-fix-stm32-and-potentially-others-resume-r.patch b/queue-6.18/net-stmmac-fix-stm32-and-potentially-others-resume-r.patch new file mode 100644 index 0000000000..136f6c872e --- /dev/null +++ b/queue-6.18/net-stmmac-fix-stm32-and-potentially-others-resume-r.patch @@ -0,0 +1,69 @@ +From 7d997cd44502aa7c30de86fa52726122fb75ae37 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 30 Jan 2026 20:04:57 +0000 +Subject: net: stmmac: fix stm32 (and potentially others) resume regression + +From: Russell King (Oracle) + +[ Upstream commit dbbec8c5a79f4c7aa8d07da8c0b5a34d76c50699 ] + +Marek reported that suspending stm32 causes the following errors when +the interface is administratively down: + + $ echo devices > /sys/power/pm_test + $ echo mem > /sys/power/state + ... + ck_ker_eth2stp already disabled + ... + ck_ker_eth2stp already unprepared + ... + +On suspend, stm32 starts the eth2stp clock in its suspend method, and +stops it in the resume method. This is because the blamed commit omits +the call to the platform glue ->suspend() method, but does make the +call to the platform glue ->resume() method. + +This problem affects all other converted drivers as well - e.g. looking +at the PCIe drivers, pci_save_state() will not be called, but +pci_restore_state() will be. Similar issues affect all other drivers. + +Fix this by always calling the ->suspend() method, even when the network +interface is down. This fixes all the conversions to the platform glue +->suspend() and ->resume() methods. + +Link: https://lore.kernel.org/r/20260114081809.12758-1-marex@nabladev.com +Fixes: 07bbbfe7addf ("net: stmmac: add suspend()/resume() platform ops") +Reported-by: Marek Vasut +Tested-by: Marek Vasut +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1vlujh-00000007Hkw-2p6r@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 41b270a486308a..1ceedd74e42908 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -7760,7 +7760,7 @@ int stmmac_suspend(struct device *dev) + u32 chan; + + if (!ndev || !netif_running(ndev)) +- return 0; ++ goto suspend_bsp; + + mutex_lock(&priv->lock); + +@@ -7803,6 +7803,7 @@ int stmmac_suspend(struct device *dev) + if (stmmac_fpe_supported(priv)) + ethtool_mmsv_stop(&priv->fpe_cfg.mmsv); + ++suspend_bsp: + if (priv->plat->suspend) + return priv->plat->suspend(dev, priv->plat->bsp_priv); + +-- +2.53.0 + diff --git a/queue-6.18/series b/queue-6.18/series index 7dd13f0fed..b06f471377 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -1 +1,2 @@ io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch +net-stmmac-fix-stm32-and-potentially-others-resume-r.patch diff --git a/queue-6.6/drm-amd-display-bound-vbios-record-chain-walk-loops.patch b/queue-6.6/drm-amd-display-bound-vbios-record-chain-walk-loops.patch new file mode 100644 index 0000000000..21a2c71725 --- /dev/null +++ b/queue-6.6/drm-amd-display-bound-vbios-record-chain-walk-loops.patch @@ -0,0 +1,300 @@ +From e734b5cafdb7b3374c44d42e04ca06574fcb621e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 15:24:22 -0400 +Subject: drm/amd/display: Bound VBIOS record-chain walk loops + +From: Harry Wentland + +[ Upstream commit ff287df16a1a58aca78b08d1f3ee09fc44da0351 ] + +[Why & How] +All record-chain walk loops in bios_parser.c and bios_parser2.c use +for(;;) and only terminate on a 0xFF record_type sentinel or zero +record_size. A malformed VBIOS image missing the terminator record +causes unbounded iteration at probe time, potentially hundreds of +thousands of iterations with record_size=1. In the final iterations +near the BIOS image boundary, struct casts beyond the 2-byte header +validated by GET_IMAGE can also read out of bounds. + +Cap all 14 record-chain walk loops to BIOS_MAX_NUM_RECORD (256) +iterations. The atombios.h defines up to 22 distinct record types +and atomfirmware.h has 13. Assuming an average of less than 10 +records per type (which is reasonable since most are connector- +based) 256 is a generous upper bound. + +Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") +Assisted-by: Copilot:claude-opus-4.6 Mythos +Reviewed-by: Alex Hung +Signed-off-by: Harry Wentland +Signed-off-by: Ray Wu +Tested-by: Daniel Wheeler +Signed-off-by: Alex Deucher +(cherry picked from commit 95700a3d660287ed657d6892f7be9ffc0e294a93) +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + .../gpu/drm/amd/display/dc/bios/bios_parser.c | 15 +++++++---- + .../drm/amd/display/dc/bios/bios_parser2.c | 27 ++++++++++++------- + .../amd/display/dc/bios/bios_parser_helper.h | 5 ++++ + 3 files changed, 33 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +index ac2a71e80723d1..fa8fcbcc421aba 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +@@ -225,6 +225,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + ATOM_COMMON_RECORD_HEADER *header; + ATOM_I2C_RECORD *record; + struct bios_parser *bp = BP_FROM_DCB(dcb); ++ int i; + + if (!info) + return BP_RESULT_BADINPUT; +@@ -237,7 +238,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -296,11 +297,12 @@ static enum bp_result bios_parser_get_device_tag_record( + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -873,6 +875,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -882,7 +885,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -1577,6 +1580,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + { + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -1586,7 +1590,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) +@@ -2675,6 +2679,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, + unsigned int record_offset) + { + unsigned int j; ++ unsigned int n; + struct bios_parser *bp; + ATOM_BRACKET_LAYOUT_RECORD *record; + ATOM_COMMON_RECORD_HEADER *record_header; +@@ -2684,7 +2689,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb, + record = NULL; + record_header = NULL; + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); + if (record_header == NULL) { +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +index ed8f669a9a3e15..33142f1c37462b 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +@@ -391,6 +391,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + struct atom_i2c_record *record; + struct atom_i2c_record dummy_record = {0}; + struct bios_parser *bp = BP_FROM_DCB(dcb); ++ int i; + + if (!info) + return BP_RESULT_BADINPUT; +@@ -424,7 +425,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + break; + } + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -529,6 +530,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -537,7 +539,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -607,6 +609,7 @@ static struct atom_hpd_int_record *get_hpd_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -616,7 +619,7 @@ static struct atom_hpd_int_record *get_hpd_record( + offset = le16_to_cpu(object->disp_recordoffset) + + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2125,6 +2128,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -2133,7 +2137,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( + + offset = object->encoder_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2162,6 +2166,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -2170,7 +2175,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2198,6 +2203,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_ + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -2206,7 +2212,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_ + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -2286,6 +2292,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct + { + struct atom_common_record_header *header; + uint32_t offset; ++ int i; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ +@@ -2294,7 +2301,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + +- for (;;) { ++ for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) +@@ -3154,6 +3161,7 @@ static enum bp_result update_slot_layout_info( + { + unsigned int record_offset; + unsigned int j; ++ unsigned int n; + struct atom_display_object_path_v2 *object; + struct atom_bracket_layout_record *record; + struct atom_common_record_header *record_header; +@@ -3175,7 +3183,7 @@ static enum bp_result update_slot_layout_info( + (object->disp_recordoffset) + + (unsigned int)(bp->object_info_tbl_offset); + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (struct atom_common_record_header *) + GET_IMAGE(struct atom_common_record_header, +@@ -3269,6 +3277,7 @@ static enum bp_result update_slot_layout_info_v2( + struct slot_layout_info *slot_layout_info) + { + unsigned int record_offset; ++ unsigned int n; + struct atom_display_object_path_v3 *object; + struct atom_bracket_layout_record_v2 *record; + struct atom_common_record_header *record_header; +@@ -3291,7 +3300,7 @@ static enum bp_result update_slot_layout_info_v2( + (object->disp_recordoffset) + + (unsigned int)(bp->object_info_tbl_offset); + +- for (;;) { ++ for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { + + record_header = (struct atom_common_record_header *) + GET_IMAGE(struct atom_common_record_header, +diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +index e1b4a40a353db1..da1e30de3c59a0 100644 +--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h ++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +@@ -38,4 +38,9 @@ uint32_t bios_get_vga_enabled_displays(struct dc_bios *bios); + + #define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) + ++/* Upper bound on the number of records in a VBIOS record chain. Prevents ++ * unbounded looping if the VBIOS image is malformed and lacks a terminator. ++ */ ++#define BIOS_MAX_NUM_RECORD 256 ++ + #endif +-- +2.53.0 + diff --git a/queue-6.6/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch b/queue-6.6/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch new file mode 100644 index 0000000000..97d57c3b88 --- /dev/null +++ b/queue-6.6/drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch @@ -0,0 +1,63 @@ +From 0e818f17826d30aa7afd0b445265f2016481ae3a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 14:50:15 -0300 +Subject: drm/v3d: Skip CSD when it has zeroed workgroups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 7f93fad5ea0affc9e1505dd0f7596c0fdb496213 ] + +A compute shader dispatch encodes its workgroup counts in the CFG0..CFG2 +registers. Kicking off a dispatch with a zero count in any of the three +dimensions is invalid. First, the hardware will process 0 as 65536, +while the user-space driver exposes a maximum of 65535. Over that, a +submission with a zeroed workgroup dimension should be a no-op. + +These zeroed counts can reach the dispatch path through an indirect CSD +job, whose workgroup counts are only known once the indirect buffer is +read and may legitimately be zero, but such scenario should only result in +a no-op. + +Overwrite the indirect CSD job workgroup counts with the indirect BO +ones, even if they are zeroed, and don't submit the job to the hardware +when any of the workgroup counts is zero, so the job completes immediately +instead of running the shader. + +Cc: stable@vger.kernel.org +Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.") +Suggested-by: Jose Maria Casanova Crespo +Reviewed-by: Iago Toral Quiroga +Link: https://patch.msgid.link/20260602-v3d-fix-indirect-csd-v4-2-654309e32bc0@igalia.com +Signed-off-by: Maíra Canal +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_sched.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c +index ff91cdb75bb912..ab872bc818004a 100644 +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -234,6 +234,16 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) + return NULL; + } + ++ /* The HW interprets a workgroup size of 0 as 65536; however, the ++ * user-space driver exposes a maximum of 65535. Therefore, a 0 in ++ * any dimension means that we have no workgroups and the compute ++ * shader should not be dispatched. ++ */ ++ if (!V3D_GET_FIELD(job->args.cfg[0], V3D_CSD_QUEUED_CFG0_NUM_WGS_X) || ++ !V3D_GET_FIELD(job->args.cfg[1], V3D_CSD_QUEUED_CFG1_NUM_WGS_Y) || ++ !V3D_GET_FIELD(job->args.cfg[2], V3D_CSD_QUEUED_CFG2_NUM_WGS_Z)) ++ return NULL; ++ + v3d->queue[V3D_CSD].active_job = &job->base; + + v3d_invalidate_caches(v3d); +-- +2.53.0 + diff --git a/queue-6.6/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch b/queue-6.6/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch new file mode 100644 index 0000000000..f6bacdddfd --- /dev/null +++ b/queue-6.6/drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch @@ -0,0 +1,237 @@ +From a1f8c052790ca504293f974ba90e731f223f4dfd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 Aug 2025 11:18:59 -0300 +Subject: drm/v3d: Store the active job inside the queue's state +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maíra Canal + +[ Upstream commit 0d3768826d38c0ac740f8b45cd13346630535f2b ] + +Instead of storing the queue's active job in four different variables, +store the active job inside the queue's state. This way, it's possible +to access all active jobs using an index based in `enum v3d_queue`. + +Reviewed-by: Iago Toral Quiroga +Reviewed-by: Melissa Wen +Link: https://lore.kernel.org/r/20250826-v3d-queue-lock-v3-2-979efc43e490@igalia.com +Signed-off-by: Maíra Canal +Stable-dep-of: 7f93fad5ea0a ("drm/v3d: Skip CSD when it has zeroed workgroups") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/v3d/v3d_drv.h | 8 +++----- + drivers/gpu/drm/v3d/v3d_gem.c | 5 +++-- + drivers/gpu/drm/v3d/v3d_irq.c | 24 ++++++++++++++---------- + drivers/gpu/drm/v3d/v3d_sched.c | 26 ++++++++++++++++++-------- + 4 files changed, 38 insertions(+), 25 deletions(-) + +diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h +index bcef978ba9c4ca..91ea0a80879a9b 100644 +--- a/drivers/gpu/drm/v3d/v3d_drv.h ++++ b/drivers/gpu/drm/v3d/v3d_drv.h +@@ -26,6 +26,9 @@ struct v3d_queue_state { + + u64 fence_context; + u64 emit_seqno; ++ ++ /* Currently active job for this queue */ ++ struct v3d_job *active_job; + }; + + /* Performance monitor object. The perform lifetime is controlled by userspace +@@ -110,11 +113,6 @@ struct v3d_dev { + + struct work_struct overflow_mem_work; + +- struct v3d_bin_job *bin_job; +- struct v3d_render_job *render_job; +- struct v3d_tfu_job *tfu_job; +- struct v3d_csd_job *csd_job; +- + struct v3d_queue_state queue[V3D_MAX_QUEUES]; + + /* Spinlock used to synchronize the overflow memory +diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c +index 057f9525a8a422..f14c9f7143355c 100644 +--- a/drivers/gpu/drm/v3d/v3d_gem.c ++++ b/drivers/gpu/drm/v3d/v3d_gem.c +@@ -1071,14 +1071,15 @@ void + v3d_gem_destroy(struct drm_device *dev) + { + struct v3d_dev *v3d = to_v3d_dev(dev); ++ enum v3d_queue q; + + v3d_sched_fini(v3d); + + /* Waiting for jobs to finish would need to be done before + * unregistering V3D. + */ +- WARN_ON(v3d->bin_job); +- WARN_ON(v3d->render_job); ++ for (q = 0; q < V3D_MAX_QUEUES; q++) ++ WARN_ON(v3d->queue[q].active_job); + + drm_mm_takedown(&v3d->mm); + +diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c +index 641315dbee8b29..1717504742a665 100644 +--- a/drivers/gpu/drm/v3d/v3d_irq.c ++++ b/drivers/gpu/drm/v3d/v3d_irq.c +@@ -40,6 +40,8 @@ v3d_overflow_mem_work(struct work_struct *work) + container_of(work, struct v3d_dev, overflow_mem_work); + struct drm_device *dev = &v3d->drm; + struct v3d_bo *bo = v3d_bo_create(dev, NULL /* XXX: GMP */, 256 * 1024); ++ struct v3d_queue_state *queue = &v3d->queue[V3D_BIN]; ++ struct v3d_bin_job *bin_job; + struct drm_gem_object *obj; + unsigned long irqflags; + +@@ -59,13 +61,15 @@ v3d_overflow_mem_work(struct work_struct *work) + * some binner pool anyway. + */ + spin_lock_irqsave(&v3d->job_lock, irqflags); +- if (!v3d->bin_job) { ++ bin_job = (struct v3d_bin_job *)queue->active_job; ++ ++ if (!bin_job) { + spin_unlock_irqrestore(&v3d->job_lock, irqflags); + goto out; + } + + drm_gem_object_get(obj); +- list_add_tail(&bo->unref_head, &v3d->bin_job->render->unref_list); ++ list_add_tail(&bo->unref_head, &bin_job->render->unref_list); + spin_unlock_irqrestore(&v3d->job_lock, irqflags); + + V3D_CORE_WRITE(0, V3D_PTB_BPOA, bo->node.start << PAGE_SHIFT); +@@ -99,11 +103,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_FLDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->bin_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_BIN].active_job->irq_fence); + + trace_v3d_bcl_irq(&v3d->drm, fence->seqno); + +- v3d->bin_job = NULL; ++ v3d->queue[V3D_BIN].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -111,11 +115,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_FRDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->render_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_RENDER].active_job->irq_fence); + + trace_v3d_rcl_irq(&v3d->drm, fence->seqno); + +- v3d->render_job = NULL; ++ v3d->queue[V3D_RENDER].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -123,11 +127,11 @@ v3d_irq(int irq, void *arg) + + if (intsts & V3D_INT_CSDDONE) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->csd_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_CSD].active_job->irq_fence); + + trace_v3d_csd_irq(&v3d->drm, fence->seqno); + +- v3d->csd_job = NULL; ++ v3d->queue[V3D_CSD].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +@@ -162,11 +166,11 @@ v3d_hub_irq(int irq, void *arg) + + if (intsts & V3D_HUB_INT_TFUC) { + struct v3d_fence *fence = +- to_v3d_fence(v3d->tfu_job->base.irq_fence); ++ to_v3d_fence(v3d->queue[V3D_TFU].active_job->irq_fence); + + trace_v3d_tfu_irq(&v3d->drm, fence->seqno); + +- v3d->tfu_job = NULL; ++ v3d->queue[V3D_TFU].active_job = NULL; + dma_fence_signal(&fence->base); + + status = IRQ_HANDLED; +diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c +index 41493cf3d03b81..ff91cdb75bb912 100644 +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -80,14 +80,18 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) + struct dma_fence *fence; + unsigned long irqflags; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ spin_lock_irqsave(&v3d->job_lock, irqflags); ++ v3d->queue[V3D_BIN].active_job = NULL; ++ spin_unlock_irqrestore(&v3d->job_lock, irqflags); + return NULL; ++ } + + /* Lock required around bin_job update vs + * v3d_overflow_mem_work(). + */ + spin_lock_irqsave(&v3d->job_lock, irqflags); +- v3d->bin_job = job; ++ v3d->queue[V3D_BIN].active_job = &job->base; + /* Clear out the overflow allocation, so we don't + * reuse the overflow attached to a previous job. + */ +@@ -134,10 +138,12 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_RENDER].active_job = NULL; + return NULL; ++ } + +- v3d->render_job = job; ++ v3d->queue[V3D_RENDER].active_job = &job->base; + + /* Can we avoid this flush? We need to be careful of + * scheduling, though -- imagine job0 rendering to texture and +@@ -179,10 +185,12 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job) + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_TFU].active_job = NULL; + return NULL; ++ } + +- v3d->tfu_job = job; ++ v3d->queue[V3D_TFU].active_job = &job->base; + + fence = v3d_fence_create(v3d, V3D_TFU); + if (IS_ERR(fence)) +@@ -221,10 +229,12 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) + struct dma_fence *fence; + int i; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->queue[V3D_CSD].active_job = NULL; + return NULL; ++ } + +- v3d->csd_job = job; ++ v3d->queue[V3D_CSD].active_job = &job->base; + + v3d_invalidate_caches(v3d); + +-- +2.53.0 + diff --git a/queue-6.6/ip6_vti-set-netns_immutable-on-the-fallback-device.patch b/queue-6.6/ip6_vti-set-netns_immutable-on-the-fallback-device.patch new file mode 100644 index 0000000000..9e938de35f --- /dev/null +++ b/queue-6.6/ip6_vti-set-netns_immutable-on-the-fallback-device.patch @@ -0,0 +1,49 @@ +From 0dbd97c588c0c85fd82d1a21dc6c2057f68926f6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Jun 2026 15:59:18 +0000 +Subject: ip6_vti: set netns_immutable on the fallback device. + +From: Eric Dumazet + +[ Upstream commit d289d5307762d1838aaece22c6b6fcad9e8865f9 ] + +john1988 and Noam Rathaus reported that vti6_init_net() does not set the +netns_immutable flag on the per-netns fallback tunnel device (ip6_vti0). + +Other similar tunnel drivers (like ip6_tunnel, sit, ip6_gre, and ip_tunnel) +correctly set this flag during their fallback device initialization to +prevent them from being moved to another network namespace. + +Fixes: 61220ab34948 ("vti6: Enable namespace changing") +Reported-by: Noam Rathaus +Signed-off-by: Eric Dumazet +Cc: Steffen Klassert +Reviewed-by: Nicolas Dichtel +Link: https://patch.msgid.link/20260608155918.787644-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +[Salvatore Bonaccorso: Backport for version without 0c493da86374 ("net: +rename netns_local to netns_immutable") in v6.15-rc1 and without +05c1280a2bcf ("netdev_features: convert NETIF_F_NETNS_LOCAL to +dev->netns_local") in v6.12-rc1 and use NETIF_F_NETNS_LOCAL device +feature.] +Signed-off-by: Salvatore Bonaccorso +Signed-off-by: Sasha Levin +--- + net/ipv6/ip6_vti.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c +index 67cf616c1499a4..95cf8c52953c2f 100644 +--- a/net/ipv6/ip6_vti.c ++++ b/net/ipv6/ip6_vti.c +@@ -1166,6 +1166,7 @@ static int __net_init vti6_init_net(struct net *net) + goto err_alloc_dev; + dev_net_set(ip6n->fb_tnl_dev, net); + ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops; ++ ip6n->fb_tnl_dev->features |= NETIF_F_NETNS_LOCAL; + + err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); + if (err < 0) +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index bef91ff4ac..a07ea87320 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -1,2 +1,6 @@ fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch +drm-amd-display-bound-vbios-record-chain-walk-loops.patch +ip6_vti-set-netns_immutable-on-the-fallback-device.patch +drm-v3d-store-the-active-job-inside-the-queue-s-stat.patch +drm-v3d-skip-csd-when-it-has-zeroed-workgroups.patch diff --git a/queue-7.0/arm64-entry-fix-arm64-specific-rseq-brokenness.patch b/queue-7.0/arm64-entry-fix-arm64-specific-rseq-brokenness.patch new file mode 100644 index 0000000000..00621c7b2a --- /dev/null +++ b/queue-7.0/arm64-entry-fix-arm64-specific-rseq-brokenness.patch @@ -0,0 +1,217 @@ +From 1ef7ce634a34929b43c99937f674cf9f02b41fb8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 16:14:26 +0100 +Subject: arm64/entry: Fix arm64-specific rseq brokenness + +From: Mark Rutland + +commit 411c1cf430392c905e39f12bc305dd994da0b426 upstream. + +Mathias Stearn reports that since v6.19, there are two big issues +affecting rseq: + +(1) On arm64 specifically, rseq critical sections aren't aborted when + they should be. + +(2) The 'cpu_id_start' field is no longer written by the kernel in all + cases it used to be, including some cases where TCMalloc depends on + the kernel clobbering the field. + +This patch fixes issue #1. This patch DOES NOT fix issue #2, which will +need to be addressed by other patches. + +The arm64-specific brokenness is a result of commits: + + 2fc0e4b4126c ("rseq: Record interrupt from user space") + 39a167560a61 ("rseq: Optimize event setting") + +The first commit failed to add a call to rseq_note_user_irq_entry() on +arm64. Thus arm64 never sets rseq_event::user_irq to record that it may +be necessary to abort an active rseq critical section upon return to +userspace. On its own, this commit had no functional impact as the value +of rseq_event::user_irq was not consumed. + +The second commit relied upon rseq_event::user_irq to determine whether +or not to bother to perform rseq work when returning to userspace. As +rseq_event::user_irq wasn't set on arm64, this work would be skipped, +and consequently an active rseq critical section would not be aborted. + +Fix this by giving arm64 syscall-specific entry/exit paths, and +performing the relevant logic in syscall and non-syscall paths, +including calling rseq_note_user_irq_entry() for non-syscall entry. + +Currently arm64 cannot use syscall_enter_from_user_mode(), +syscall_exit_to_user_mode(), and irqentry_exit_to_user_mode(), due to +ordering constraints with exception masking, and risk of ABI breakage +for syscall tracing/audit/etc. For the moment the entry/exit logic is +left as arm64-specific, directly using enter_from_user_mode() and +exit_to_user_mode(), but mirroring the generic code. + +I intend to follow up with refactoring/cleanup, as we did for kernel +mode entry paths in commit: + + 041aa7a85390 ("entry: Split preemption from irqentry_exit_to_kernel_mode()") + +... which will allow arm64 to use the GENERIC_IRQ_ENTRY functions directly. + +Fixes: 39a167560a61 ("rseq: Optimize event setting") +Reported-by: Mathias Stearn +Signed-off-by: Mark Rutland +Signed-off-by: Peter Zijlstra (Intel) +Acked-by: Catalin Marinas +Link: https://lore.kernel.org/regressions/CAHnCjA25b+nO2n5CeifknSKHssJpPrjnf+dtr7UgzRw4Zgu=oA@mail.gmail.com/ +Link: https://patch.msgid.link/20260508142023.3268622-1-mark.rutland@arm.com +[Mark: fix conflicts in entry-common.c & irq-entry-common.h] +Signed-off-by: Mark Rutland +Signed-off-by: Sasha Levin +--- + arch/arm64/kernel/entry-common.c | 29 ++++++++++++++++++++++------- + include/linux/irq-entry-common.h | 8 -------- + include/linux/rseq_entry.h | 19 ------------------- + 3 files changed, 22 insertions(+), 34 deletions(-) + +diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c +index 3625797e9ee8f9..e3614cedaf23e7 100644 +--- a/arch/arm64/kernel/entry-common.c ++++ b/arch/arm64/kernel/entry-common.c +@@ -58,6 +58,12 @@ static void noinstr exit_to_kernel_mode(struct pt_regs *regs, + irqentry_exit(regs, state); + } + ++static __always_inline void arm64_syscall_enter_from_user_mode(struct pt_regs *regs) ++{ ++ enter_from_user_mode(regs); ++ mte_disable_tco_entry(current); ++} ++ + /* + * Handle IRQ/context state management when entering from user mode. + * Before this function is called it is not safe to call regular kernel code, +@@ -66,19 +72,28 @@ static void noinstr exit_to_kernel_mode(struct pt_regs *regs, + static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs) + { + enter_from_user_mode(regs); ++ rseq_note_user_irq_entry(); + mte_disable_tco_entry(current); + } + ++static __always_inline void arm64_syscall_exit_to_user_mode(struct pt_regs *regs) ++{ ++ local_irq_disable(); ++ syscall_exit_to_user_mode_prepare(regs); ++ local_daif_mask(); ++ mte_check_tfsr_exit(); ++ exit_to_user_mode(); ++} ++ + /* + * Handle IRQ/context state management when exiting to user mode. + * After this function returns it is not safe to call regular kernel code, + * instrumentable code, or any code which may trigger an exception. + */ +- + static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs) + { + local_irq_disable(); +- exit_to_user_mode_prepare_legacy(regs); ++ irqentry_exit_to_user_mode_prepare(regs); + local_daif_mask(); + mte_check_tfsr_exit(); + exit_to_user_mode(); +@@ -86,7 +101,7 @@ static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs) + + asmlinkage void noinstr asm_exit_to_user_mode(struct pt_regs *regs) + { +- arm64_exit_to_user_mode(regs); ++ arm64_syscall_exit_to_user_mode(regs); + } + + /* +@@ -717,12 +732,12 @@ static void noinstr el0_brk64(struct pt_regs *regs, unsigned long esr) + + static void noinstr el0_svc(struct pt_regs *regs) + { +- arm64_enter_from_user_mode(regs); ++ arm64_syscall_enter_from_user_mode(regs); + cortex_a76_erratum_1463225_svc_handler(); + fpsimd_syscall_enter(); + local_daif_restore(DAIF_PROCCTX); + do_el0_svc(regs); +- arm64_exit_to_user_mode(regs); ++ arm64_syscall_exit_to_user_mode(regs); + fpsimd_syscall_exit(); + } + +@@ -869,11 +884,11 @@ static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr) + + static void noinstr el0_svc_compat(struct pt_regs *regs) + { +- arm64_enter_from_user_mode(regs); ++ arm64_syscall_enter_from_user_mode(regs); + cortex_a76_erratum_1463225_svc_handler(); + local_daif_restore(DAIF_PROCCTX); + do_el0_svc_compat(regs); +- arm64_exit_to_user_mode(regs); ++ arm64_syscall_exit_to_user_mode(regs); + } + + static void noinstr el0_bkpt32(struct pt_regs *regs, unsigned long esr) +diff --git a/include/linux/irq-entry-common.h b/include/linux/irq-entry-common.h +index d26d1b1bcbfb97..6519b4a30dc1dd 100644 +--- a/include/linux/irq-entry-common.h ++++ b/include/linux/irq-entry-common.h +@@ -236,14 +236,6 @@ static __always_inline void __exit_to_user_mode_validate(void) + lockdep_sys_exit(); + } + +-/* Temporary workaround to keep ARM64 alive */ +-static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs) +-{ +- __exit_to_user_mode_prepare(regs); +- rseq_exit_to_user_mode_legacy(); +- __exit_to_user_mode_validate(); +-} +- + /** + * syscall_exit_to_user_mode_prepare - call exit_to_user_mode_loop() if required + * @regs: Pointer to pt_regs on entry stack +diff --git a/include/linux/rseq_entry.h b/include/linux/rseq_entry.h +index 69bdb93951b904..bbe190269f79aa 100644 +--- a/include/linux/rseq_entry.h ++++ b/include/linux/rseq_entry.h +@@ -740,24 +740,6 @@ static __always_inline void rseq_irqentry_exit_to_user_mode(void) + ev->events = 0; + } + +-/* Required to keep ARM64 working */ +-static __always_inline void rseq_exit_to_user_mode_legacy(void) +-{ +- struct rseq_event *ev = ¤t->rseq.event; +- +- rseq_stat_inc(rseq_stats.exit); +- +- if (static_branch_unlikely(&rseq_debug_enabled)) +- WARN_ON_ONCE(ev->sched_switch); +- +- /* +- * Ensure that event (especially user_irq) is cleared when the +- * interrupt did not result in a schedule and therefore the +- * rseq processing did not clear it. +- */ +- ev->events = 0; +-} +- + void __rseq_debug_syscall_return(struct pt_regs *regs); + + static __always_inline void rseq_debug_syscall_return(struct pt_regs *regs) +@@ -773,7 +755,6 @@ static inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned + } + static inline void rseq_syscall_exit_to_user_mode(void) { } + static inline void rseq_irqentry_exit_to_user_mode(void) { } +-static inline void rseq_exit_to_user_mode_legacy(void) { } + static inline void rseq_debug_syscall_return(struct pt_regs *regs) { } + static inline bool rseq_grant_slice_extension(bool work_pending) { return false; } + #endif /* !CONFIG_RSEQ */ +-- +2.53.0 + diff --git a/queue-7.0/series b/queue-7.0/series index 7dd13f0fed..b09b947a96 100644 --- a/queue-7.0/series +++ b/queue-7.0/series @@ -1 +1,2 @@ io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch +arm64-entry-fix-arm64-specific-rseq-brokenness.patch