]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees master
authorSasha Levin <sashal@kernel.org>
Wed, 3 Jun 2026 15:14:13 +0000 (11:14 -0400)
committerSasha Levin <sashal@kernel.org>
Wed, 3 Jun 2026 15:14:13 +0000 (11:14 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
131 files changed:
queue-5.10/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch [new file with mode: 0644]
queue-5.10/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch [new file with mode: 0644]
queue-5.10/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-fix-race-condition-in-send-error.patch [new file with mode: 0644]
queue-5.10/batman-adv-tt-avoid-empty-vlan-responses.patch [new file with mode: 0644]
queue-5.10/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch [new file with mode: 0644]
queue-5.10/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch [new file with mode: 0644]
queue-5.10/batman-adv-tvlv-reject-oversized-tvlv-packets.patch [new file with mode: 0644]
queue-5.10/batman-adv-v-stop-ogmv2-on-disabled-interface.patch [new file with mode: 0644]
queue-5.10/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-26331 [new file with mode: 0644]
queue-5.10/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch [new file with mode: 0644]
queue-5.10/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch [new file with mode: 0644]
queue-5.15/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch [new file with mode: 0644]
queue-5.15/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch [new file with mode: 0644]
queue-5.15/batman-adv-tt-avoid-empty-vlan-responses.patch [new file with mode: 0644]
queue-5.15/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch [new file with mode: 0644]
queue-5.15/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch [new file with mode: 0644]
queue-5.15/batman-adv-tvlv-reject-oversized-tvlv-packets.patch [new file with mode: 0644]
queue-5.15/batman-adv-v-stop-ogmv2-on-disabled-interface.patch [new file with mode: 0644]
queue-5.15/drm-dp-add-edp-1.5-bit-definition.patch [new file with mode: 0644]
queue-5.15/drm-i915-psr-add-defininitions-for-intel_wa_register.patch [new file with mode: 0644]
queue-5.15/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch [new file with mode: 0644]
queue-5.15/drm-i915-psr-read-intel-dpcd-workaround-register.patch [new file with mode: 0644]
queue-5.15/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-82 [new file with mode: 0644]
queue-5.15/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch [new file with mode: 0644]
queue-5.15/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch [new file with mode: 0644]
queue-5.15/series
queue-5.15/wifi-brcmfmac-fix-use-after-free-when-rescheduling-b.patch [new file with mode: 0644]
queue-6.1/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch [new file with mode: 0644]
queue-6.1/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch [new file with mode: 0644]
queue-6.1/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch [new file with mode: 0644]
queue-6.1/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch [new file with mode: 0644]
queue-6.1/batman-adv-tt-avoid-empty-vlan-responses.patch [new file with mode: 0644]
queue-6.1/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch [new file with mode: 0644]
queue-6.1/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch [new file with mode: 0644]
queue-6.1/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch [new file with mode: 0644]
queue-6.1/batman-adv-tvlv-reject-oversized-tvlv-packets.patch [new file with mode: 0644]
queue-6.1/batman-adv-v-stop-ogmv2-on-disabled-interface.patch [new file with mode: 0644]
queue-6.1/bpf-fix-a-few-selftest-failures-due-to-llvm18-change.patch [new file with mode: 0644]
queue-6.1/drm-dp-add-edp-1.5-bit-definition.patch [new file with mode: 0644]
queue-6.1/drm-i915-psr-add-defininitions-for-intel_wa_register.patch [new file with mode: 0644]
queue-6.1/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch [new file with mode: 0644]
queue-6.1/drm-i915-psr-read-intel-dpcd-workaround-register.patch [new file with mode: 0644]
queue-6.1/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch [new file with mode: 0644]
queue-6.1/net-packet-convert-po-has_vnet_hdr-to-an-atomic-flag.patch [new file with mode: 0644]
queue-6.1/net-packet-convert-po-running-to-an-atomic-flag.patch [new file with mode: 0644]
queue-6.1/net-packet-convert-po-tp_loss-to-an-atomic-flag.patch [new file with mode: 0644]
queue-6.1/net-packet-convert-po-tp_tx_has_off-to-an-atomic-fla.patch [new file with mode: 0644]
queue-6.1/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpa.patch [new file with mode: 0644]
queue-6.1/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-25226 [new file with mode: 0644]
queue-6.1/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch [new file with mode: 0644]
queue-6.1/revert-selftests-bpf-add-a-cgroup-prog-bpf_get_ns_cu.patch [new file with mode: 0644]
queue-6.1/revert-selftests-bpf-add-tests-for-_opts-variants-of.patch [new file with mode: 0644]
queue-6.1/revert-selftests-bpf-workaround-strict-bpf_lsm-retur.patch [new file with mode: 0644]
queue-6.1/selftests-bpf-add-generic-bpf-program-tester-loader.patch [new file with mode: 0644]
queue-6.1/selftests-bpf-add-read_build_id-function.patch [new file with mode: 0644]
queue-6.1/selftests-bpf-convert-test_global_funcs-test-to-test.patch [new file with mode: 0644]
queue-6.1/selftests-bpf-enhance-align-selftest-s-expected-log-.patch [new file with mode: 0644]
queue-6.1/selftests-bpf-fix-arg_ptr_to_long-half-uninitialized.patch [new file with mode: 0644]
queue-6.1/selftests-bpf-s-iptables-iptables-legacy-in-the-bpf_.patch [new file with mode: 0644]
queue-6.1/selftests-bpf-update-bpf_clone_redirect-expected-ret.patch [new file with mode: 0644]
queue-6.1/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch [new file with mode: 0644]
queue-6.1/series
queue-6.12/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch [new file with mode: 0644]
queue-6.12/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch [new file with mode: 0644]
queue-6.12/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch [new file with mode: 0644]
queue-6.12/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch [new file with mode: 0644]
queue-6.12/batman-adv-tt-avoid-empty-vlan-responses.patch [new file with mode: 0644]
queue-6.12/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch [new file with mode: 0644]
queue-6.12/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch [new file with mode: 0644]
queue-6.12/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch [new file with mode: 0644]
queue-6.12/batman-adv-tvlv-reject-oversized-tvlv-packets.patch [new file with mode: 0644]
queue-6.12/batman-adv-v-stop-ogmv2-on-disabled-interface.patch [new file with mode: 0644]
queue-6.12/drm-dp-add-edp-1.5-bit-definition.patch [new file with mode: 0644]
queue-6.12/drm-i915-psr-add-defininitions-for-intel_wa_register.patch [new file with mode: 0644]
queue-6.12/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch [new file with mode: 0644]
queue-6.12/drm-i915-psr-read-intel-dpcd-workaround-register.patch [new file with mode: 0644]
queue-6.12/hid-core-add-printk_ratelimited-variants-to-hid_warn.patch [new file with mode: 0644]
queue-6.12/hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch [new file with mode: 0644]
queue-6.12/hid-core-introduce-hid_safe_input_report.patch [new file with mode: 0644]
queue-6.12/hid-pass-the-buffer-size-to-hid_report_raw_event.patch [new file with mode: 0644]
queue-6.12/inet-frags-add-inet_frag_queue_flush.patch [new file with mode: 0644]
queue-6.12/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch [new file with mode: 0644]
queue-6.12/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch [new file with mode: 0644]
queue-6.12/media-rc-ttusbir-fix-inverted-error-logic.patch [new file with mode: 0644]
queue-6.12/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch [new file with mode: 0644]
queue-6.12/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch [new file with mode: 0644]
queue-6.12/s390-cio-restore-gfp_dma-for-chsc-allocation.patch [new file with mode: 0644]
queue-6.12/series
queue-6.18/drm-i915-psr-add-defininitions-for-intel_wa_register.patch [new file with mode: 0644]
queue-6.18/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch [new file with mode: 0644]
queue-6.18/drm-i915-psr-read-intel-dpcd-workaround-register.patch [new file with mode: 0644]
queue-6.18/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch [new file with mode: 0644]
queue-6.18/s390-cio-restore-gfp_dma-for-chsc-allocation.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/smb-client-validate-the-whole-dacl-before-rewriting-.patch [new file with mode: 0644]
queue-6.6/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch [new file with mode: 0644]
queue-6.6/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch [new file with mode: 0644]
queue-6.6/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch [new file with mode: 0644]
queue-6.6/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch [new file with mode: 0644]
queue-6.6/batman-adv-tt-avoid-empty-vlan-responses.patch [new file with mode: 0644]
queue-6.6/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch [new file with mode: 0644]
queue-6.6/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch [new file with mode: 0644]
queue-6.6/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch [new file with mode: 0644]
queue-6.6/batman-adv-tvlv-reject-oversized-tvlv-packets.patch [new file with mode: 0644]
queue-6.6/batman-adv-v-stop-ogmv2-on-disabled-interface.patch [new file with mode: 0644]
queue-6.6/drm-dp-add-edp-1.5-bit-definition.patch [new file with mode: 0644]
queue-6.6/drm-fbcon-vga_switcheroo-avoid-race-condition-in-fbc.patch [new file with mode: 0644]
queue-6.6/drm-fbdev-helper-set-and-clear-vga-switcheroo-client.patch [new file with mode: 0644]
queue-6.6/drm-i915-psr-add-defininitions-for-intel_wa_register.patch [new file with mode: 0644]
queue-6.6/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch [new file with mode: 0644]
queue-6.6/drm-i915-psr-read-intel-dpcd-workaround-register.patch [new file with mode: 0644]
queue-6.6/inet-frags-add-inet_frag_queue_flush.patch [new file with mode: 0644]
queue-6.6/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch [new file with mode: 0644]
queue-6.6/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch [new file with mode: 0644]
queue-6.6/media-rc-ttusbir-fix-inverted-error-logic.patch [new file with mode: 0644]
queue-6.6/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch [new file with mode: 0644]
queue-6.6/net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch [new file with mode: 0644]
queue-6.6/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch [new file with mode: 0644]
queue-6.6/series
queue-7.0/drm-i915-psr-add-defininitions-for-intel_wa_register.patch [new file with mode: 0644]
queue-7.0/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch [new file with mode: 0644]
queue-7.0/drm-i915-psr-read-intel-dpcd-workaround-register.patch [new file with mode: 0644]
queue-7.0/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch [new file with mode: 0644]
queue-7.0/series

diff --git a/queue-5.10/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-5.10/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
new file mode 100644 (file)
index 0000000..68a769d
--- /dev/null
@@ -0,0 +1,232 @@
+From ff876916d00d800ff39013e984e4ac836fc253f5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:15:58 +0200
+Subject: batman-adv: bla: avoid double decrement of bla.num_requests
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 83ab69bd12b80f6ea169c8bea6977701b53a043d upstream.
+
+The bla.num_requests is increased when no request_sent was in progress. And
+it is decremented in various places (announcement was received, backbone is
+purged, periodic work). But the check if the request_sent is actually set
+to a specific state and the atomic_dec/_inc are not safe because they are
+not atomic (TOCTOU) and multiple such code portions can run concurrently.
+
+At the same time, it is necessary to modify request_sent (state) and
+bla.num_requests atomically. Otherwise batadv_bla_send_request() might set
+request_sent to 1 and is interrupted.  batadv_handle_announce() can then
+set request_sent back to 0 and decrement num_requests before
+batadv_bla_send_request() incremented it.
+
+The two operations must therefore be locked. And since state (request_sent)
+and wait_periods are only accessed inside this lock, they can be converted
+to simpler datatypes. And to avoid that the bla.num_requests is touched by
+a parallel running context with a valid backbone_gw reference after
+batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to
+correctly signal that a backbone_gw is in the state of being cleaned up.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++--------
+ net/batman-adv/soft-interface.c        |  1 +
+ net/batman-adv/types.h                 | 39 ++++++++++++++++----
+ 3 files changed, 67 insertions(+), 24 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index d01c636024ce15..8354e5473dc698 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -521,8 +521,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+-      atomic_set(&entry->request_sent, 0);
+-      atomic_set(&entry->wait_periods, 0);
++      entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
++      entry->wait_periods = 0;
+       ether_addr_copy(entry->orig, orig);
+       INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
+       kref_init(&entry->refcount);
+@@ -551,9 +551,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
+               batadv_bla_send_announce(bat_priv, entry);
+               /* this will be decreased in the worker thread */
+-              atomic_inc(&entry->request_sent);
+-              atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
+-              atomic_inc(&bat_priv->bla.num_requests);
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++                      entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
++                      entry->wait_periods = BATADV_BLA_WAIT_PERIODS;
++                      atomic_inc(&bat_priv->bla.num_requests);
++              }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       return entry;
+@@ -656,10 +660,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw)
+                             backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);
+       /* no local broadcasts should be sent or received, for now. */
+-      if (!atomic_read(&backbone_gw->request_sent)) {
++      spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
++      if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++              backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
+               atomic_inc(&backbone_gw->bat_priv->bla.num_requests);
+-              atomic_set(&backbone_gw->request_sent, 1);
+       }
++      spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
+ }
+ /**
+@@ -880,10 +886,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               /* if we have sent a request and the crc was OK,
+                * we can allow traffic again.
+                */
+-              if (atomic_read(&backbone_gw->request_sent)) {
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) {
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
+               }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       batadv_backbone_gw_put(backbone_gw);
+@@ -1262,9 +1270,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                               purged = true;
+                               /* don't wait for the pending request anymore */
+-                              if (atomic_read(&backbone_gw->request_sent))
++                              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED)
+                                       atomic_dec(&bat_priv->bla.num_requests);
++                              backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
++                              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
++
+                               batadv_bla_del_backbone_claims(backbone_gw);
+                               hlist_del_rcu(&backbone_gw->hash_entry);
+@@ -1515,7 +1527,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                               batadv_bla_send_loopdetect(bat_priv,
+                                                          backbone_gw);
+-                      /* request_sent is only set after creation to avoid
++                      /* state is only set to unsynced after creation to avoid
+                        * problems when we are not yet known as backbone gw
+                        * in the backbone.
+                        *
+@@ -1524,14 +1536,21 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                        * some grace time.
+                        */
+-                      if (atomic_read(&backbone_gw->request_sent) == 0)
+-                              continue;
++                      spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                      if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
++                              goto unlock_next;
+-                      if (!atomic_dec_and_test(&backbone_gw->wait_periods))
+-                              continue;
++                      if (backbone_gw->wait_periods > 0)
++                              backbone_gw->wait_periods--;
++
++                      if (backbone_gw->wait_periods > 0)
++                              goto unlock_next;
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
++
++unlock_next:
++                      spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+               }
+               rcu_read_unlock();
+       }
+diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
+index 0e6d0a5e684130..c8c62b0cd54c76 100644
+--- a/net/batman-adv/soft-interface.c
++++ b/net/batman-adv/soft-interface.c
+@@ -822,6 +822,7 @@ static int batadv_softif_init_late(struct net_device *dev)
+       atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
+ #ifdef CONFIG_BATMAN_ADV_BLA
+       atomic_set(&bat_priv->bla.num_requests, 0);
++      spin_lock_init(&bat_priv->bla.num_requests_lock);
+ #endif
+       atomic_set(&bat_priv->tp_num, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index aee90f613e13c5..86c149d5b52ae2 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1036,6 +1036,12 @@ struct batadv_priv_bla {
+       /** @num_requests: number of bla requests in flight */
+       atomic_t num_requests;
++      /**
++       * @num_requests_lock: locks update num_requests +
++       * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update
++       */
++      spinlock_t num_requests_lock;
++
+       /**
+        * @claim_hash: hash table containing mesh nodes this host has claimed
+        */
+@@ -1815,6 +1821,27 @@ struct batadv_socket_packet {
+ #ifdef CONFIG_BATMAN_ADV_BLA
++enum batadv_bla_backbone_gw_state {
++      /**
++       * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed
++       * and it must not longer work on requests
++       */
++      BATADV_BLA_BACKBONE_GW_STOPPED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out
++       * of sync and a request was send. No traffic is forwarded until the
++       * situation is resolved
++       */
++      BATADV_BLA_BACKBONE_GW_UNSYNCED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in
++       * sync. traffic can be forwarded
++       */
++      BATADV_BLA_BACKBONE_GW_SYNCED,
++};
++
+ /**
+  * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
+  */
+@@ -1840,16 +1867,12 @@ struct batadv_bla_backbone_gw {
+       /**
+        * @wait_periods: grace time for bridge forward delays and bla group
+        *  forming at bootup phase - no bcast traffic is formwared until it has
+-       *  elapsed
++       *  elapsed. Must only be access with num_requests_lock.
+        */
+-      atomic_t wait_periods;
++      u8 wait_periods;
+-      /**
+-       * @request_sent: if this bool is set to true we are out of sync with
+-       *  this backbone gateway - no bcast traffic is formwared until the
+-       *  situation was resolved
+-       */
+-      atomic_t request_sent;
++      /** @state: sync state. Must only be access with num_requests_lock. */
++      enum batadv_bla_backbone_gw_state state;
+       /** @crc: crc16 checksum over all claims */
+       u16 crc;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-5.10/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
new file mode 100644 (file)
index 0000000..0fb9db5
--- /dev/null
@@ -0,0 +1,53 @@
+From fd95b14c518fe2e2e38b20e176c79bd03bca87f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:16:21 +0200
+Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 upstream.
+
+Without rtnl_lock held, a hardif might be retrieved as primary interface of
+a meshif, but then (while operating on this interface) getting decoupled
+from the mesh interface. In this case, the meshif still exists but the
+pointer from the primary hardif to the meshif is set to NULL.
+
+The mesh_iface must be checked first to be non-NULL before continuing to
+send an ARP request using meshif.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Reported-by: Ido Schimmel <idosch@nvidia.com>
+Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index d8305961b59bd1..d01c636024ce15 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -357,12 +357,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac,
+              sizeof(local_claim_dest));
+       local_claim_dest.type = claimtype;
+-      soft_iface = primary_if->soft_iface;
++      soft_iface = READ_ONCE(primary_if->soft_iface);
++      if (!soft_iface)
++              goto out;
+       skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
+                        /* IP DST: 0.0.0.0 */
+                        zeroip,
+-                       primary_if->soft_iface,
++                       soft_iface,
+                        /* IP SRC: 0.0.0.0 */
+                        zeroip,
+                        /* Ethernet DST: Broadcast */
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-5.10/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
new file mode 100644 (file)
index 0000000..2ddd3b8
--- /dev/null
@@ -0,0 +1,231 @@
+From dd3ad9e8a19ef319d3cc55b36ac980390f6bbb41 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:15:29 +0200
+Subject: batman-adv: iv: recover OGM scheduling after forward packet error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit aa3153bd139a6c48667dcd02608d3b2c80bff02c upstream.
+
+When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward
+packet for OGM transmission, the work item that drives periodic OGM
+scheduling is never re-armed. This silently halts transmission of the
+node's own OGMs on the affected interface — only OGMs from other peers
+continue to be aggregated and forwarded.
+
+Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively
+batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet.
+When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing
+a dedicated recovery work item (reschedule_work) that fires after one
+originator interval and calls batadv_iv_ogm_schedule() again.
+
+Cc: stable@kernel.org
+Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++----------
+ net/batman-adv/types.h      |  3 ++
+ 2 files changed, 60 insertions(+), 19 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 9cd2fa751c21ef..93c2c5f6facc0d 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -225,6 +225,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_iv.ogm_buff = NULL;
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
++
++      cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
+ }
+ static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
+@@ -530,8 +532,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
+  * @if_incoming: interface where the packet was received
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
++static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                                       int packet_len, unsigned long send_time,
+                                       bool direct_link,
+                                       struct batadv_hard_iface *if_incoming,
+@@ -555,13 +559,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+       skb = netdev_alloc_skb_ip_align(NULL, skb_size);
+       if (!skb)
+-              return;
++              return false;
+       forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
+                                                   queue_left, bat_priv, skb);
+       if (!forw_packet_aggr) {
+               kfree_skb(skb);
+-              return;
++              return false;
+       }
+       forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
+@@ -583,6 +587,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                         batadv_iv_send_outstanding_bat_ogm_packet);
+       batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
++
++      return true;
+ }
+ /* aggregate a new packet into the existing ogm packet */
+@@ -612,8 +618,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
+  * @send_time: timestamp (jiffies) when the packet is to be sent
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
++static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+                                   unsigned char *packet_buff,
+                                   int packet_len,
+                                   struct batadv_hard_iface *if_incoming,
+@@ -665,14 +673,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+               if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
+                       send_time += max_aggregation_jiffies;
+-              batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
+-                                          send_time, direct_link,
+-                                          if_incoming, if_outgoing,
+-                                          own_packet);
++              return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
++                                                 send_time, direct_link,
++                                                 if_incoming, if_outgoing,
++                                                 own_packet);
+       } else {
+               batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
+                                       packet_len, direct_link);
+               spin_unlock_bh(&bat_priv->forw_bat_list_lock);
++
++              return true;
+       }
+ }
+@@ -784,6 +794,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      bool reschedule = false;
++      bool scheduled;
+       int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -812,11 +824,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                                                      ogm_buff_len,
+                                                      BATADV_OGM_HLEN);
+               if (ret < 0) {
+-                      /* OGMs must be queued even when the buffer allocation for
+-                       * TVLVs failed. just fall back to the non-TVLV version
+-                       */
+-                      ret = 0;
+-                      *ogm_buff_len = BATADV_OGM_HLEN;
++                      reschedule = true;
++                      goto out;
+               }
+               tvlv_len = ret;
+@@ -838,8 +847,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               /* OGMs from secondary interfaces are only scheduled on their
+                * respective interfaces.
+                */
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
+-                                      hard_iface, hard_iface, 1, send_time);
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
++                                                  hard_iface, hard_iface, 1, send_time);
++              if (!scheduled)
++                      reschedule = true;
++
+               goto out;
+       }
+@@ -854,15 +866,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
+                       continue;
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
+-                                      *ogm_buff_len, hard_iface,
+-                                      tmp_hard_iface, 1, send_time);
+-
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
++                                                  *ogm_buff_len, hard_iface,
++                                                  tmp_hard_iface, 1, send_time);
+               batadv_hardif_put(tmp_hard_iface);
++
++              if (!scheduled && tmp_hard_iface == hard_iface)
++                      reschedule = true;
+       }
+       rcu_read_unlock();
+ out:
++      if (reschedule) {
++              /* there was a failure scheduling the own forward packet.
++               * as result, the batadv_iv_send_outstanding_bat_ogm_packet()
++               * work item is no longer scheduled. it is therefore necessary
++               * to reschedule it manually
++               */
++              queue_delayed_work(batadv_event_workqueue,
++                                 &hard_iface->bat_iv.reschedule_work,
++                                 msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
++      }
++
+       if (primary_if)
+               batadv_hardif_put(primary_if);
+ }
+@@ -878,6 +903,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+ }
++static void batadv_iv_ogm_reschedule(struct work_struct *work)
++{
++      struct delayed_work *delayed_work = to_delayed_work(work);
++      struct batadv_hard_iface *hard_iface;
++
++      hard_iface = container_of(delayed_work,
++                                struct batadv_hard_iface,
++                                bat_iv.reschedule_work);
++      batadv_iv_ogm_schedule(hard_iface);
++}
++
+ /**
+  * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
+  * @orig_node: originator which reproadcasted the OGMs directly
+@@ -2443,6 +2479,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
+ static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
+ {
++      INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
++
+       /* begin scheduling originator messages on that interface */
+       batadv_iv_ogm_schedule(hard_iface);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index f7e5a8f7570a31..288fe330946fb4 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv {
+       /** @ogm_seqno: OGM sequence number - used to identify each OGM */
+       atomic_t ogm_seqno;
++      /** @reschedule_work: recover OGM schedule after schedule error */
++      struct delayed_work reschedule_work;
++
+       /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
+       struct mutex ogm_buff_mutex;
+ };
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch b/queue-5.10/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch
new file mode 100644 (file)
index 0000000..0380cf5
--- /dev/null
@@ -0,0 +1,196 @@
+From 94794c40fade445b66016a69112e2625ec7a7a40 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:14:56 +0200
+Subject: batman-adv: tp_meter: avoid role confusion in tp_list
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit ff24f2ecfd94c07a2b89bac497433e3b23271cac upstream.
+
+Session lookups in tp_list matched only on destination address (and
+optionally session ID), leaving role validation to the caller. If two
+sessions with the same other_end coexisted (one as sender, one as receiver)
+a lookup could silently return the wrong one, causing the caller's role to
+bail out early, potentially skipping necessary cleanup.
+
+Move the role check into the lookup functions themselves so the correct
+entry is always returned, or none at all. Since batadv_tp_start()
+legitimately needs to detect any active session to a destination regardless
+of role, introduce a dedicated helper for that case rather than bending the
+existing lookup semantics.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++---------------
+ 1 file changed, 36 insertions(+), 23 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index b93cb86129769c..ebefad5822eea0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -253,6 +253,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
+  * batadv_tp_list_find() - find a tp_vars object in the global list
+  * @bat_priv: the bat priv with all the soft interface information
+  * @dst: the other endpoint MAC address to look for
++ * @role: role of the session
+  *
+  * Look for a tp_vars object matching dst as end_point and return it after
+  * having increment the refcounter. Return NULL is not found
+@@ -260,7 +261,8 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
+  * Return: matching tp_vars or NULL when no tp_vars with @dst was found
+  */
+ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+-                                                const u8 *dst)
++                                                const u8 *dst,
++                                                enum batadv_tp_meter_role role)
+ {
+       struct batadv_tp_vars *pos, *tp_vars = NULL;
+@@ -269,6 +271,9 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(pos->other_end, dst))
+                       continue;
++              if (pos->role != role)
++                      continue;
++
+               /* most of the time this function is invoked during the normal
+                * process..it makes sens to pay more when the session is
+                * finished and to speed the process up during the measurement
+@@ -284,12 +289,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+       return tp_vars;
+ }
++/**
++ * batadv_tp_list_active() - check if session from/to destination is ongoing
++ * @bat_priv: the bat priv with all the mesh interface information
++ * @dst: the other endpoint MAC address to look for
++ *
++ * Return: if matching session with @dst was found
++ */
++static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
++      __must_hold(&bat_priv->tp_list_lock)
++{
++      struct batadv_tp_vars *tp_vars;
++
++      hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) {
++              if (batadv_compare_eth(tp_vars->other_end, dst))
++                      return true;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tp_list_find_session() - find tp_vars session object in the global
+  *  list
+  * @bat_priv: the bat priv with all the soft interface information
+  * @dst: the other endpoint MAC address to look for
+  * @session: session identifier
++ * @role: role of the session
+  *
+  * Look for a tp_vars object matching dst as end_point, session as tp meter
+  * session and return it after having increment the refcounter. Return NULL
+@@ -299,7 +325,7 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+  */
+ static struct batadv_tp_vars *
+ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
+-                          const u8 *session)
++                          const u8 *session, enum batadv_tp_meter_role role)
+ {
+       struct batadv_tp_vars *pos, *tp_vars = NULL;
+@@ -311,6 +337,9 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
+               if (memcmp(pos->session, session, sizeof(pos->session)) != 0)
+                       continue;
++              if (pos->role != role)
++                      continue;
++
+               /* most of the time this function is invoked during the normal
+                * process..it makes sense to pay more when the session is
+                * finished and to speed the process up during the measurement
+@@ -654,13 +683,10 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+       /* find the tp_vars */
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                            icmp->session);
++                                            icmp->session, BATADV_TP_SENDER);
+       if (unlikely(!tp_vars))
+               return;
+-      if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+-              goto out;
+-
+       if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+               goto out;
+@@ -972,10 +998,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+               return;
+       }
+-      tp_vars = batadv_tp_list_find(bat_priv, dst);
+-      if (tp_vars) {
++      if (batadv_tp_list_active(bat_priv, dst)) {
+               spin_unlock_bh(&bat_priv->tp_list_lock);
+-              batadv_tp_vars_put(tp_vars);
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                          "Meter: test to or from the same node already ongoing, aborting\n");
+               batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING,
+@@ -1094,18 +1118,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
+       if (!orig_node)
+               return;
+-      tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig);
++      tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER);
+       if (!tp_vars) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                          "Meter: trying to interrupt an already over connection\n");
+               goto out_put_orig_node;
+       }
+-      if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+-              goto out_put_tp_vars;
+-
+       batadv_tp_sender_shutdown(tp_vars, return_value);
+-out_put_tp_vars:
+       batadv_tp_vars_put(tp_vars);
+ out_put_orig_node:
+       batadv_orig_node_put(orig_node);
+@@ -1367,7 +1387,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+               goto out_unlock;
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                            icmp->session);
++                                            icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars)
+               goto out_unlock;
+@@ -1435,7 +1455,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               }
+       } else {
+               tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                                    icmp->session);
++                                                    icmp->session, BATADV_TP_RECEIVER);
+               if (!tp_vars) {
+                       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                                  "Unexpected packet from %pM!\n",
+@@ -1444,13 +1464,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               }
+       }
+-      if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) {
+-              batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+-                         "Meter: dropping packet: not expected (role=%u)\n",
+-                         tp_vars->role);
+-              goto out;
+-      }
+-
+       tp_vars->last_recv_time = jiffies;
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tp_meter-fix-race-condition-in-send-error.patch b/queue-5.10/batman-adv-tp_meter-fix-race-condition-in-send-error.patch
new file mode 100644 (file)
index 0000000..55f672d
--- /dev/null
@@ -0,0 +1,186 @@
+From 4fc9090dfc3ef3ab7277309e85bc2eea8197ce7e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:14:34 +0200
+Subject: batman-adv: tp_meter: fix race condition in send error reporting
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 71dce47f0758537fff78fddb5fb0d4632d29b29f upstream.
+
+batadv_tp_sender_shutdown() previously used two separate variables to track
+session state: sending (an atomic flag indicating whether the session was
+active) and reason (a plain enum storing the stop reason). This introduced
+a race window between the two writes: after sending was cleared to 0,
+batadv_tp_send() could observe the stopped state and call
+batadv_tp_sender_end() before reason was written, causing the wrong stop
+reason to be reported to the caller.
+
+Fix this by consolidating both variables into a single atomic send_result,
+which holds 0 while the session is running and the stop reason once it
+ends.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 40 ++++++++++++++++++++++++---------------
+ net/batman-adv/types.h    | 10 +++++-----
+ 2 files changed, 30 insertions(+), 20 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 578da029a7b3d8..b93cb86129769c 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -402,11 +402,14 @@ static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
+ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
+                                struct batadv_tp_vars *tp_vars)
+ {
++      enum batadv_tp_meter_reason reason;
+       u32 session_cookie;
++      reason = atomic_read(&tp_vars->send_result);
++
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Test towards %pM finished..shutting down (reason=%d)\n",
+-                 tp_vars->other_end, tp_vars->reason);
++                 tp_vars->other_end, reason);
+       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                  "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n",
+@@ -419,7 +422,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
+       session_cookie = batadv_tp_session_cookie(tp_vars->session,
+                                                 tp_vars->icmp_uid);
+-      batadv_tp_batctl_notify(tp_vars->reason,
++      batadv_tp_batctl_notify(reason,
+                               tp_vars->other_end,
+                               bat_priv,
+                               tp_vars->start_time,
+@@ -435,10 +438,18 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
+ static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars,
+                                     enum batadv_tp_meter_reason reason)
+ {
+-      if (atomic_xchg(&tp_vars->sending, 0) != 1)
+-              return;
++      atomic_cmpxchg(&tp_vars->send_result, 0, reason);
++}
+-      tp_vars->reason = reason;
++/**
++ * batadv_tp_sender_stopped() - check if tp session was stopped with reason
++ * @tp_vars: the private data of the current TP meter session
++ *
++ * Return: whether stop reason was found
++ */
++static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars)
++{
++      return atomic_read(&tp_vars->send_result) != 0;
+ }
+ /**
+@@ -468,7 +479,7 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars)
+       /* most of the time this function is invoked while normal packet
+        * reception...
+        */
+-      if (unlikely(atomic_read(&tp_vars->sending) == 0))
++      if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+               /* timer ref will be dropped in batadv_tp_sender_cleanup */
+               return;
+@@ -488,7 +499,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
+       struct batadv_tp_vars *tp_vars = from_timer(tp_vars, t, timer);
+       struct batadv_priv *bat_priv = tp_vars->bat_priv;
+-      if (atomic_read(&tp_vars->sending) == 0)
++      if (batadv_tp_sender_stopped(tp_vars))
+               return;
+       /* if the user waited long enough...shutdown the test */
+@@ -650,7 +661,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+       if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+               goto out;
+-      if (unlikely(atomic_read(&tp_vars->sending) == 0))
++      if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+               goto out;
+       /* old ACK? silently drop it.. */
+@@ -819,21 +830,21 @@ static int batadv_tp_send(void *arg)
+       if (unlikely(tp_vars->role != BATADV_TP_SENDER)) {
+               err = BATADV_TP_REASON_DST_UNREACHABLE;
+-              tp_vars->reason = err;
++              batadv_tp_sender_shutdown(tp_vars, err);
+               goto out;
+       }
+       orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end);
+       if (unlikely(!orig_node)) {
+               err = BATADV_TP_REASON_DST_UNREACHABLE;
+-              tp_vars->reason = err;
++              batadv_tp_sender_shutdown(tp_vars, err);
+               goto out;
+       }
+       primary_if = batadv_primary_if_get_selected(bat_priv);
+       if (unlikely(!primary_if)) {
+               err = BATADV_TP_REASON_DST_UNREACHABLE;
+-              tp_vars->reason = err;
++              batadv_tp_sender_shutdown(tp_vars, err);
+               goto out;
+       }
+@@ -852,7 +863,7 @@ static int batadv_tp_send(void *arg)
+       queue_delayed_work(batadv_event_workqueue, &tp_vars->finish_work,
+                          msecs_to_jiffies(tp_vars->test_length));
+-      while (atomic_read(&tp_vars->sending) != 0) {
++      while (!batadv_tp_sender_stopped(tp_vars)) {
+               if (unlikely(!batadv_tp_avail(tp_vars, payload_len))) {
+                       batadv_tp_wait_available(tp_vars, payload_len);
+                       continue;
+@@ -875,8 +886,7 @@ static int batadv_tp_send(void *arg)
+                                  "Meter: %s() cannot send packets (%d)\n",
+                                  __func__, err);
+                       /* ensure nobody else tries to stop the thread now */
+-                      if (atomic_xchg(&tp_vars->sending, 0) == 1)
+-                              tp_vars->reason = err;
++                      batadv_tp_sender_shutdown(tp_vars, err);
+                       break;
+               }
+@@ -997,7 +1007,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+       ether_addr_copy(tp_vars->other_end, dst);
+       kref_init(&tp_vars->refcount);
+       tp_vars->role = BATADV_TP_SENDER;
+-      atomic_set(&tp_vars->sending, 1);
++      atomic_set(&tp_vars->send_result, 0);
+       memcpy(tp_vars->session, session_id, sizeof(session_id));
+       tp_vars->icmp_uid = icmp_uid;
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 288fe330946fb4..aee90f613e13c5 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1407,11 +1407,11 @@ struct batadv_tp_vars {
+       /** @role: receiver/sender modi */
+       enum batadv_tp_meter_role role;
+-      /** @sending: sending binary semaphore: 1 if sending, 0 is not */
+-      atomic_t sending;
+-
+-      /** @reason: reason for a stopped session */
+-      enum batadv_tp_meter_reason reason;
++      /**
++       * @send_result: 0 when sending is ongoing and otherwise
++       * enum batadv_tp_meter_reason
++       */
++      atomic_t send_result;
+       /** @finish_work: work item for the finishing procedure */
+       struct delayed_work finish_work;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-5.10/batman-adv-tt-avoid-empty-vlan-responses.patch
new file mode 100644 (file)
index 0000000..1f80477
--- /dev/null
@@ -0,0 +1,91 @@
+From f8cdd1e62a3eb30fe9cef4199a1df26dc2003afd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:12:08 +0200
+Subject: batman-adv: tt: avoid empty VLAN responses
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 upstream.
+
+The commit 16116dac2339 ("batman-adv: prevent TT request storms by not
+sending inconsistent TT TLVLs") added checks to the local (direct) TT
+response code. But the response can also be done indirectly by another node
+using the global TT state. To avoid such inconsistency states reported in
+the original fix, also avoid sending empty VLANs for replies from the
+global TT state.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 18eeeb80378dca..483121a85cf3fc 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -848,17 +848,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+                                  s32 *tt_len)
+ {
+       u16 num_vlan = 0;
+-      u16 num_entries = 0;
+       u16 tvlv_len = 0;
+       unsigned int change_offset;
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_orig_node_vlan *vlan;
++      u16 total_entries = 0;
+       u8 *tt_change_ptr;
++      int vlan_entries;
+       spin_lock_bh(&orig_node->vlan_list_lock);
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              total_entries += vlan_entries;
+               num_vlan++;
+-              num_entries += atomic_read(&vlan->tt.num_entries);
+       }
+       change_offset = sizeof(**tt_data);
+@@ -866,7 +868,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       /* if tt_len is negative, allocate the space needed by the full table */
+       if (*tt_len < 0)
+-              *tt_len = batadv_tt_len(num_entries);
++              *tt_len = batadv_tt_len(total_entries);
+       if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) {
+               *tt_len = 0;
+@@ -887,14 +889,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              if (vlan_entries < 1)
++                      continue;
++
+               tt_vlan->vid = htons(vlan->vid);
+               tt_vlan->crc = htonl(vlan->tt.crc);
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-5.10/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
new file mode 100644 (file)
index 0000000..2f9518b
--- /dev/null
@@ -0,0 +1,78 @@
+From 68abc66755f098051bd9559c3d5b7b443c390d85 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 23:06:04 +0200
+Subject: batman-adv: tt: fix TOCTOU race for reported vlans
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 94d27005016be15ffc638b2ecbc4d58805ad7b48 upstream.
+
+The local TT based TVLV is generated by first checking the number of VLANs
+which have at least one TT entry. A new buffer with the correct size for
+the VLANs is then allocated. Only then, the list of VLANs s used to fill
+the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock
+is held. But the actual number of TT entries of each VLAN can still
+increase during this time - just not the number of VLANs in the list.
+
+But the prefilter used in the buffer size calculation might still cause an
+increase of the number of VLANs which need to be stored. Simply because a
+VLAN might now suddenly have at least one entry when it had none in the
+pre-alloc check - and then needs to occupy space which was not allocated.
+
+It is better to overestimate the buffer size at the beginning and then fill
+the buffer only with the VLANs which are not empty.
+
+Cc: stable@kernel.org
+Fixes: 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 73f1ab4f008c4f..18eeeb80378dca 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -939,11 +939,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+-              if (vlan_entries < 1)
+-                      continue;
+-
+-              num_vlan++;
+               total_entries += vlan_entries;
++              num_vlan++;
+       }
+       change_offset = sizeof(**tt_data);
+@@ -967,6 +964,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+               if (vlan_entries < 1)
+@@ -977,8 +975,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-5.10/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
new file mode 100644 (file)
index 0000000..88805b4
--- /dev/null
@@ -0,0 +1,196 @@
+From f6c337bd4cd1b2ec09241b059ddae725aeab7b2c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:56:05 +0200
+Subject: batman-adv: tvlv: abort OGM send on tvlv append failure
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 501368506563e151b322c8c3f228b796e615b90d upstream.
+
+batadv_tvlv_container_ogm_append() could fail in two ways: a memory
+allocation failure when resizing the packet buffer, or the tvlv data
+exceeding U16_MAX bytes. In both cases the function previously returned the
+old (now stale) tvlv_value_len rather than signalling an error, causing the
+OGM/OGM2 send path to transmit a packet whose TVLV length field no longer
+matched the actual buffer contents. And because it also didn't fill in the
+new TVLV data, sending either uninitialized or corrupted data on the wire.
+
+All errors in batadv_tvlv_container_ogm_append() must be forwarded to the
+caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N.
+IV, it is currently not allowed to abort the send. The non-TVLV part of the
+OGM must be queued up instead.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 16 +++++++++++++---
+ net/batman-adv/bat_v_ogm.c  | 26 ++++++++++++++------------
+ net/batman-adv/tvlv.c       | 17 ++++++++++++-----
+ net/batman-adv/tvlv.h       |  2 +-
+ 4 files changed, 40 insertions(+), 21 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index f35665a4045123..9cd2fa751c21ef 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -784,6 +784,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -807,9 +808,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                * appended as it may alter the tt tvlv container
+                */
+               batadv_tt_local_commit_changes(bat_priv);
+-              tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+-                                                          ogm_buff_len,
+-                                                          BATADV_OGM_HLEN);
++              ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                                     ogm_buff_len,
++                                                     BATADV_OGM_HLEN);
++              if (ret < 0) {
++                      /* OGMs must be queued even when the buffer allocation for
++                       * TVLVs failed. just fall back to the non-TVLV version
++                       */
++                      ret = 0;
++                      *ogm_buff_len = BATADV_OGM_HLEN;
++              }
++
++              tvlv_len = ret;
+       }
+       batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 5f75bcf64ae56c..939aa4b303ad98 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -272,9 +272,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       struct batadv_hard_iface *hard_iface;
+       struct batadv_ogm2_packet *ogm_packet;
+       struct sk_buff *skb, *skb_tmp;
+-      unsigned char *ogm_buff;
+-      int ogm_buff_len;
+-      u16 tvlv_len = 0;
++      unsigned char **ogm_buff;
++      int *ogm_buff_len;
++      u16 tvlv_len;
+       int ret;
+       lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
+@@ -282,25 +282,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
+               goto out;
+-      ogm_buff = bat_priv->bat_v.ogm_buff;
+-      ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
++      ogm_buff = &bat_priv->bat_v.ogm_buff;
++      ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
++
+       /* tt changes have to be committed before the tvlv data is
+        * appended as it may alter the tt tvlv container
+        */
+       batadv_tt_local_commit_changes(bat_priv);
+-      tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
+-                                                  &ogm_buff_len,
+-                                                  BATADV_OGM2_HLEN);
++      ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                             ogm_buff_len,
++                                             BATADV_OGM2_HLEN);
++      if (ret < 0)
++              goto reschedule;
+-      bat_priv->bat_v.ogm_buff = ogm_buff;
+-      bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
++      tvlv_len = ret;
+-      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
++      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len);
+       if (!skb)
+               goto reschedule;
+       skb_reserve(skb, ETH_HLEN);
+-      skb_put_data(skb, ogm_buff, ogm_buff_len);
++      skb_put_data(skb, *ogm_buff, *ogm_buff_len);
+       ogm_packet = (struct batadv_ogm2_packet *)skb->data;
+       ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 99fc48efde5431..75f7ea827ed9dd 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -7,6 +7,7 @@
+ #include "main.h"
+ #include <linux/byteorder/generic.h>
++#include <linux/errno.h>
+ #include <linux/etherdevice.h>
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
+  * The ogm packet might be enlarged or shrunk depending on the current size
+  * and the size of the to-be-appended tvlv containers.
+  *
+- * Return: size of all appended tvlv containers in bytes.
++ * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
++ *  if operation failed
+  */
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len)
+ {
+@@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_len;
+       void *tvlv_value;
++      int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+@@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-
+-      if (!ret)
++      if (!ret) {
++              tvlv_len_ret = -ENOMEM;
+               goto end;
++      }
++
++      tvlv_len_ret = tvlv_value_len;
+       if (!tvlv_value_len)
+               goto end;
+@@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ end:
+       spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+-      return tvlv_value_len;
++
++      return tvlv_len_ret;
+ }
+ /**
+diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h
+index d509d00c7a23e6..4823f4963df5bd 100644
+--- a/net/batman-adv/tvlv.h
++++ b/net/batman-adv/tvlv.h
+@@ -15,7 +15,7 @@
+ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+                                   u8 type, u8 version,
+                                   void *tvlv_value, u16 tvlv_value_len);
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len);
+ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-5.10/batman-adv-tvlv-reject-oversized-tvlv-packets.patch
new file mode 100644 (file)
index 0000000..986b346
--- /dev/null
@@ -0,0 +1,85 @@
+From 1eb789040cee7f7b55911ba95c226511331006b3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:13:55 +0200
+Subject: batman-adv: tvlv: reject oversized TVLV packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f50487e3566358b2b982b7801945e858c78ad9ab upstream.
+
+batadv_tvlv_container_ogm_append() builds a TVLV packet section from
+the tvlv.container_list. The total size of this section is computed by
+batadv_tvlv_container_list_size(), which sums the sizes of all registered
+containers.
+
+The return type and accumulator in batadv_tvlv_container_list_size() were
+u16. If the accumulated size exceeds U16_MAX, the value wraps around,
+causing the subsequent allocation in batadv_tvlv_container_ogm_append()
+to be undersized. The memcpy-style copy that follows would then write
+beyond the end of the allocated buffer, corrupting kernel memory.
+
+Fix this by widening the return type of batadv_tvlv_container_list_size()
+to size_t. In batadv_tvlv_container_ogm_append(), check the computed length
+against U16_MAX before proceeding, and bail out as if the allocation had
+failed when the limit is exceeded.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 75f7ea827ed9dd..8da8184a2ebdfc 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -13,6 +13,7 @@
+ #include <linux/if_ether.h>
+ #include <linux/kernel.h>
+ #include <linux/kref.h>
++#include <linux/limits.h>
+ #include <linux/list.h>
+ #include <linux/lockdep.h>
+ #include <linux/netdevice.h>
+@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
+  *
+  * Return: size of all currently registered tvlv containers in bytes.
+  */
+-static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
++static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+ {
+       struct batadv_tvlv_container *tvlv;
+-      u16 tvlv_len = 0;
++      size_t tvlv_len = 0;
+       lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
+@@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ {
+       struct batadv_tvlv_container *tvlv;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+-      u16 tvlv_value_len;
++      size_t tvlv_value_len;
+       void *tvlv_value;
+       int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+       tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
++      if (tvlv_value_len > U16_MAX) {
++              tvlv_len_ret = -E2BIG;
++              goto end;
++      }
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-- 
+2.53.0
+
diff --git a/queue-5.10/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-5.10/batman-adv-v-stop-ogmv2-on-disabled-interface.patch
new file mode 100644 (file)
index 0000000..c878255
--- /dev/null
@@ -0,0 +1,149 @@
+From a128c818ba38abae4cd7923931eb42c718b37b55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:36:39 +0200
+Subject: batman-adv: v: stop OGMv2 on disabled interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f8ce8b8331a1bc44ad4905886a482214d428b253 upstream.
+
+When a batadv_hard_iface is disabled, its mesh_iface pointer is set to
+NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via
+batadv_v_ogm_queue_on_if() for interfaces that have since lost their
+mesh_iface association. This results in a NULL pointer dereference when
+batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the
+now NULL hard_iface->mesh_iface to retrieve the batadv_priv.
+
+It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that
+it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was
+called.
+
+Cc: stable@kernel.org
+Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index d43fc72af9a994..5f75bcf64ae56c 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -116,14 +116,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
+ /**
+  * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to send
+  * @hard_iface: the interface to use to send the OGM
+  */
+-static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
++static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
++                                  struct sk_buff *skb,
+                                   struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+-
+       if (hard_iface->if_status != BATADV_IF_ACTIVE) {
+               kfree_skb(skb);
+               return;
+@@ -190,6 +190,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+ /**
+  * batadv_v_ogm_aggr_send() - flush & send aggregation queue
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @hard_iface: the interface with the aggregation queue to flush
+  *
+  * Aggregates all OGMv2 packets currently in the aggregation queue into a
+@@ -199,7 +200,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+  *
+  * Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
+  */
+-static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
++static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
++                                 struct batadv_hard_iface *hard_iface)
+ {
+       unsigned int aggr_len = hard_iface->bat_v.aggr_len;
+       struct sk_buff *skb_aggr;
+@@ -229,27 +231,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
+               consume_skb(skb);
+       }
+-      batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
++      batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ }
+ /**
+  * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to queue
+  * @hard_iface: the interface to queue the OGM on
+  */
+-static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
++static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
++                                   struct sk_buff *skb,
+                                    struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      if (hard_iface->soft_iface != bat_priv->soft_iface) {
++              kfree_skb(skb);
++              return;
++      }
+       if (!atomic_read(&bat_priv->aggregated_ogms)) {
+-              batadv_v_ogm_send_to_if(skb, hard_iface);
++              batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+               return;
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+-              batadv_v_ogm_aggr_send(hard_iface);
++              batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
+@@ -348,7 +355,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+                       break;
+               }
+-              batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
++              batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+               batadv_hardif_put(hard_iface);
+       }
+       rcu_read_unlock();
+@@ -388,12 +395,14 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
+ {
+       struct batadv_hard_iface_bat_v *batv;
+       struct batadv_hard_iface *hard_iface;
++      struct batadv_priv *bat_priv;
+       batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
+       hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
++      bat_priv = netdev_priv(hard_iface->soft_iface);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+-      batadv_v_ogm_aggr_send(hard_iface);
++      batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+       batadv_v_ogm_start_queue_timer(hard_iface);
+@@ -583,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
+                  if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
+                  ogm_forward->ttl, if_incoming->net_dev->name);
+-      batadv_v_ogm_queue_on_if(skb, if_outgoing);
++      batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+ out:
+       if (orig_ifinfo)
+-- 
+2.53.0
+
diff --git a/queue-5.10/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-26331 b/queue-5.10/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-26331
new file mode 100644 (file)
index 0000000..453fd5b
--- /dev/null
@@ -0,0 +1,68 @@
+From 0836321027771a29176a7821e079363ff849be0d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:11:37 +0200
+Subject: RDMA/rxe: Fix double free in rxe_srq_from_init
+
+From: Jiasheng Jiang <jiashengjiangcool@gmail.com>
+
+commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream.
+
+In rxe_srq_from_init(), the queue pointer 'q' is assigned to
+'srq->rq.queue' before copying the SRQ number to user space.
+If copy_to_user() fails, the function calls rxe_queue_cleanup()
+to free the queue, but leaves the now-invalid pointer in
+'srq->rq.queue'.
+
+The caller of rxe_srq_from_init() (rxe_create_srq) eventually
+calls rxe_srq_cleanup() upon receiving the error, which triggers
+a second rxe_queue_cleanup() on the same memory, leading to a
+double free.
+
+The call trace looks like this:
+   kmem_cache_free+0x.../0x...
+   rxe_queue_cleanup+0x1a/0x30 [rdma_rxe]
+   rxe_srq_cleanup+0x42/0x60 [rdma_rxe]
+   rxe_elem_release+0x31/0x70 [rdma_rxe]
+   rxe_create_srq+0x12b/0x1a0 [rdma_rxe]
+   ib_create_srq_user+0x9a/0x150 [ib_core]
+
+Fix this by moving 'srq->rq.queue = q' after copy_to_user.
+
+Fixes: aae0484e15f0 ("IB/rxe: avoid srq memory leak")
+Signed-off-by: Jiasheng Jiang <jiashengjiangcool@gmail.com>
+Link: https://patch.msgid.link/20260112015412.29458-1-jiashengjiangcool@gmail.com
+Reviewed-by: Zhu Yanjun <yanjun.Zhu@linux.dev>
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+[bwh: Backported to 5.10: There was no assignment to init->attr.max_wr
+ here; don't add it]
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/infiniband/sw/rxe/rxe_srq.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
+index 41b0d1e11bafdb..4e523d91e7dcb1 100644
+--- a/drivers/infiniband/sw/rxe/rxe_srq.c
++++ b/drivers/infiniband/sw/rxe/rxe_srq.c
+@@ -98,8 +98,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               return -ENOMEM;
+       }
+-      srq->rq.queue = q;
+-
+       err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf,
+                          q->buf_size, &q->ip);
+       if (err) {
+@@ -116,6 +114,8 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               }
+       }
++      srq->rq.queue = q;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.10/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch b/queue-5.10/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch
new file mode 100644 (file)
index 0000000..de2c12d
--- /dev/null
@@ -0,0 +1,35 @@
+From 2652c19f60f717d8cda243411cb78fb177179e77 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:06:43 +0200
+Subject: Revert "RDMA/rxe: Fix double free in rxe_srq_from_init"
+
+From: Ben Hutchings <benh@debian.org>
+
+This reverts commit 22b8c23a3b92d023614bb00896fe364b2c1a31d3, which
+was commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream.  The
+backported version did not move but duplicated the problematic
+assignment, so it did not fix the bug.  A proper backport will follow.
+
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/infiniband/sw/rxe/rxe_srq.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
+index 9d9baca2694999..41b0d1e11bafdb 100644
+--- a/drivers/infiniband/sw/rxe/rxe_srq.c
++++ b/drivers/infiniband/sw/rxe/rxe_srq.c
+@@ -116,9 +116,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               }
+       }
+-      srq->rq.queue = q;
+-      init->attr.max_wr = srq->rq.max_wr;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.10/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch b/queue-5.10/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch
new file mode 100644 (file)
index 0000000..8bd6cd3
--- /dev/null
@@ -0,0 +1,108 @@
+From e25b37a2f6297fe641e3e33f9ab115732c71603a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:29:47 +0200
+Subject: selftests: forwarding: lib: Add helpers for checksum handling
+
+From: Petr Machata <petrm@nvidia.com>
+
+commit 952e0ee38c7215c45192d8c899acd1830873f28b upstream.
+
+In order to generate IGMPv3 and MLDv2 packets on the fly, we will need
+helpers to calculate the packet checksum.
+
+The approach presented in this patch revolves around payload templates
+for mausezahn. These are mausezahn-like payload strings (01:23:45:...)
+with possibly one 2-byte sequence replaced with the word PAYLOAD. The
+main function is payload_template_calc_checksum(), which calculates
+RFC 1071 checksum of the message. There are further helpers to then
+convert the checksum to the payload format, and to expand it.
+
+For IPv6, MLDv2 message checksum is computed using a pseudoheader that
+differs from the header used in the payload itself. The fact that the
+two messages are different means that the checksum needs to be
+returned as a separate quantity, instead of being expanded in-place in
+the payload itself. Furthermore, the pseudoheader includes a length of
+the message. Much like the checksum, this needs to be expanded in
+mausezahn format. And likewise for number of addresses for (S,G)
+entries. Thus we have several places where a computed quantity needs
+to be presented in the payload format. Add a helper u16_to_bytes(),
+which will be used in all these cases.
+
+Signed-off-by: Petr Machata <petrm@nvidia.com>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 02cb2e6bacbb ("selftests: forwarding: vxlan_bridge_1d: fix test failure with br_netfilter enabled")
+[bwh: Backported to 5.10: adjust context]
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/net/forwarding/lib.sh | 56 +++++++++++++++++++
+ 1 file changed, 56 insertions(+)
+
+diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
+index dfb41db7fbe481..2825c779ef30e0 100644
+--- a/tools/testing/selftests/net/forwarding/lib.sh
++++ b/tools/testing/selftests/net/forwarding/lib.sh
+@@ -1298,3 +1298,59 @@ tcpdump_show()
+ {
+       tcpdump -e -n -r $capfile 2>&1
+ }
++
++u16_to_bytes()
++{
++      local u16=$1; shift
++
++      printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'
++}
++
++# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),
++# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,
++# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)
++# stands for 00:00.
++payload_template_calc_checksum()
++{
++      local payload=$1; shift
++
++      (
++          # Set input radix.
++          echo "16i"
++          # Push zero for the initial checksum.
++          echo 0
++
++          # Pad the payload with a terminating 00: in case we get an odd
++          # number of bytes.
++          echo "${payload%:}:00:" |
++              sed 's/CHECKSUM/00:00/g' |
++              tr '[:lower:]' '[:upper:]' |
++              # Add the word to the checksum.
++              sed 's/\(..\):\(..\):/\1\2+\n/g' |
++              # Strip the extra odd byte we pushed if left unconverted.
++              sed 's/\(..\):$//'
++
++          echo "10000 ~ +"    # Calculate and add carry.
++          echo "FFFF r - p"   # Bit-flip and print.
++      ) |
++          dc |
++          tr '[:upper:]' '[:lower:]'
++}
++
++payload_template_expand_checksum()
++{
++      local payload=$1; shift
++      local checksum=$1; shift
++
++      local ckbytes=$(u16_to_bytes $checksum)
++
++      echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"
++}
++
++payload_template_nbytes()
++{
++      local payload=$1; shift
++
++      payload_template_expand_checksum "${payload%:}" 0 |
++              sed 's/:/\n/g' | wc -l
++}
+-- 
+2.53.0
+
index c4aa23cf6edf9ef09adf2b6ee1e6909049b275df..5b8948c9f107ebbac66b270a692d306b0b23175c 100644 (file)
@@ -27,3 +27,16 @@ net-sched-revert-net-sched-restrict-conditions-for-a.patch
 bluetooth-l2cap-clear-chan-ident-on-ecred-reconfigur.patch
 bluetooth-l2cap-fix-possible-crash-on-l2cap_ecred_co.patch
 sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch
+batman-adv-v-stop-ogmv2-on-disabled-interface.patch
+batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
+batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
+batman-adv-tvlv-reject-oversized-tvlv-packets.patch
+batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
+batman-adv-tp_meter-fix-race-condition-in-send-error.patch
+batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch
+selftests-forwarding-lib-add-helpers-for-checksum-ha.patch
+batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
+batman-adv-tt-avoid-empty-vlan-responses.patch
+batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
+revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch
+rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-26331
diff --git a/queue-5.15/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-5.15/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
new file mode 100644 (file)
index 0000000..a402723
--- /dev/null
@@ -0,0 +1,232 @@
+From daa64c8a65b7eece74a1a8c4c33018b4a7ffb742 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:08:08 +0200
+Subject: batman-adv: bla: avoid double decrement of bla.num_requests
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 83ab69bd12b80f6ea169c8bea6977701b53a043d upstream.
+
+The bla.num_requests is increased when no request_sent was in progress. And
+it is decremented in various places (announcement was received, backbone is
+purged, periodic work). But the check if the request_sent is actually set
+to a specific state and the atomic_dec/_inc are not safe because they are
+not atomic (TOCTOU) and multiple such code portions can run concurrently.
+
+At the same time, it is necessary to modify request_sent (state) and
+bla.num_requests atomically. Otherwise batadv_bla_send_request() might set
+request_sent to 1 and is interrupted.  batadv_handle_announce() can then
+set request_sent back to 0 and decrement num_requests before
+batadv_bla_send_request() incremented it.
+
+The two operations must therefore be locked. And since state (request_sent)
+and wait_periods are only accessed inside this lock, they can be converted
+to simpler datatypes. And to avoid that the bla.num_requests is touched by
+a parallel running context with a valid backbone_gw reference after
+batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to
+correctly signal that a backbone_gw is in the state of being cleaned up.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++--------
+ net/batman-adv/soft-interface.c        |  1 +
+ net/batman-adv/types.h                 | 39 ++++++++++++++++----
+ 3 files changed, 67 insertions(+), 24 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index d8ead0eb71da06..452e78fd70c039 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -515,8 +515,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+-      atomic_set(&entry->request_sent, 0);
+-      atomic_set(&entry->wait_periods, 0);
++      entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
++      entry->wait_periods = 0;
+       ether_addr_copy(entry->orig, orig);
+       INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
+       kref_init(&entry->refcount);
+@@ -545,9 +545,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
+               batadv_bla_send_announce(bat_priv, entry);
+               /* this will be decreased in the worker thread */
+-              atomic_inc(&entry->request_sent);
+-              atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
+-              atomic_inc(&bat_priv->bla.num_requests);
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++                      entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
++                      entry->wait_periods = BATADV_BLA_WAIT_PERIODS;
++                      atomic_inc(&bat_priv->bla.num_requests);
++              }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       return entry;
+@@ -650,10 +654,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw)
+                             backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);
+       /* no local broadcasts should be sent or received, for now. */
+-      if (!atomic_read(&backbone_gw->request_sent)) {
++      spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
++      if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++              backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
+               atomic_inc(&backbone_gw->bat_priv->bla.num_requests);
+-              atomic_set(&backbone_gw->request_sent, 1);
+       }
++      spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
+ }
+ /**
+@@ -874,10 +880,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               /* if we have sent a request and the crc was OK,
+                * we can allow traffic again.
+                */
+-              if (atomic_read(&backbone_gw->request_sent)) {
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) {
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
+               }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       batadv_backbone_gw_put(backbone_gw);
+@@ -1256,9 +1264,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                               purged = true;
+                               /* don't wait for the pending request anymore */
+-                              if (atomic_read(&backbone_gw->request_sent))
++                              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED)
+                                       atomic_dec(&bat_priv->bla.num_requests);
++                              backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
++                              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
++
+                               batadv_bla_del_backbone_claims(backbone_gw);
+                               hlist_del_rcu(&backbone_gw->hash_entry);
+@@ -1509,7 +1521,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                               batadv_bla_send_loopdetect(bat_priv,
+                                                          backbone_gw);
+-                      /* request_sent is only set after creation to avoid
++                      /* state is only set to unsynced after creation to avoid
+                        * problems when we are not yet known as backbone gw
+                        * in the backbone.
+                        *
+@@ -1518,14 +1530,21 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                        * some grace time.
+                        */
+-                      if (atomic_read(&backbone_gw->request_sent) == 0)
+-                              continue;
++                      spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                      if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
++                              goto unlock_next;
+-                      if (!atomic_dec_and_test(&backbone_gw->wait_periods))
+-                              continue;
++                      if (backbone_gw->wait_periods > 0)
++                              backbone_gw->wait_periods--;
++
++                      if (backbone_gw->wait_periods > 0)
++                              goto unlock_next;
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
++
++unlock_next:
++                      spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+               }
+               rcu_read_unlock();
+       }
+diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
+index bff86ccadc2cc2..b46480ec7bd1d0 100644
+--- a/net/batman-adv/soft-interface.c
++++ b/net/batman-adv/soft-interface.c
+@@ -785,6 +785,7 @@ static int batadv_softif_init_late(struct net_device *dev)
+       atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
+ #ifdef CONFIG_BATMAN_ADV_BLA
+       atomic_set(&bat_priv->bla.num_requests, 0);
++      spin_lock_init(&bat_priv->bla.num_requests_lock);
+ #endif
+       atomic_set(&bat_priv->tp_num, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index ff40fc9e36f66c..6b64e6b7bf80ca 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1026,6 +1026,12 @@ struct batadv_priv_bla {
+       /** @num_requests: number of bla requests in flight */
+       atomic_t num_requests;
++      /**
++       * @num_requests_lock: locks update num_requests +
++       * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update
++       */
++      spinlock_t num_requests_lock;
++
+       /**
+        * @claim_hash: hash table containing mesh nodes this host has claimed
+        */
+@@ -1787,6 +1793,27 @@ struct batadv_socket_packet {
+ #ifdef CONFIG_BATMAN_ADV_BLA
++enum batadv_bla_backbone_gw_state {
++      /**
++       * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed
++       * and it must not longer work on requests
++       */
++      BATADV_BLA_BACKBONE_GW_STOPPED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out
++       * of sync and a request was send. No traffic is forwarded until the
++       * situation is resolved
++       */
++      BATADV_BLA_BACKBONE_GW_UNSYNCED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in
++       * sync. traffic can be forwarded
++       */
++      BATADV_BLA_BACKBONE_GW_SYNCED,
++};
++
+ /**
+  * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
+  */
+@@ -1812,16 +1839,12 @@ struct batadv_bla_backbone_gw {
+       /**
+        * @wait_periods: grace time for bridge forward delays and bla group
+        *  forming at bootup phase - no bcast traffic is formwared until it has
+-       *  elapsed
++       *  elapsed. Must only be access with num_requests_lock.
+        */
+-      atomic_t wait_periods;
++      u8 wait_periods;
+-      /**
+-       * @request_sent: if this bool is set to true we are out of sync with
+-       *  this backbone gateway - no bcast traffic is formwared until the
+-       *  situation was resolved
+-       */
+-      atomic_t request_sent;
++      /** @state: sync state. Must only be access with num_requests_lock. */
++      enum batadv_bla_backbone_gw_state state;
+       /** @crc: crc16 checksum over all claims */
+       u16 crc;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-5.15/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
new file mode 100644 (file)
index 0000000..ebe20c6
--- /dev/null
@@ -0,0 +1,53 @@
+From a8197f02cd3ecacbf8778f9e5051db51d2227e6e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:08:49 +0200
+Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 upstream.
+
+Without rtnl_lock held, a hardif might be retrieved as primary interface of
+a meshif, but then (while operating on this interface) getting decoupled
+from the mesh interface. In this case, the meshif still exists but the
+pointer from the primary hardif to the meshif is set to NULL.
+
+The mesh_iface must be checked first to be non-NULL before continuing to
+send an ARP request using meshif.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Reported-by: Ido Schimmel <idosch@nvidia.com>
+Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index f768a4e0bc0f49..d8ead0eb71da06 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -355,12 +355,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac,
+              sizeof(local_claim_dest));
+       local_claim_dest.type = claimtype;
+-      soft_iface = primary_if->soft_iface;
++      soft_iface = READ_ONCE(primary_if->soft_iface);
++      if (!soft_iface)
++              goto out;
+       skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
+                        /* IP DST: 0.0.0.0 */
+                        zeroip,
+-                       primary_if->soft_iface,
++                       soft_iface,
+                        /* IP SRC: 0.0.0.0 */
+                        zeroip,
+                        /* Ethernet DST: Broadcast */
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-5.15/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
new file mode 100644 (file)
index 0000000..cb38671
--- /dev/null
@@ -0,0 +1,230 @@
+From 66d82dbc14c35035b0c1b0e98619cf9ba3c01510 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:07:24 +0200
+Subject: batman-adv: iv: recover OGM scheduling after forward packet error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit aa3153bd139a6c48667dcd02608d3b2c80bff02c upstream.
+
+When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward
+packet for OGM transmission, the work item that drives periodic OGM
+scheduling is never re-armed. This silently halts transmission of the
+node's own OGMs on the affected interface — only OGMs from other peers
+continue to be aggregated and forwarded.
+
+Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively
+batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet.
+When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing
+a dedicated recovery work item (reschedule_work) that fires after one
+originator interval and calls batadv_iv_ogm_schedule() again.
+
+Cc: stable@kernel.org
+Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++----------
+ net/batman-adv/types.h      |  3 ++
+ 2 files changed, 60 insertions(+), 19 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index bc8685ffcb7100..669f77eed073a0 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -224,6 +224,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_iv.ogm_buff = NULL;
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
++
++      cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
+ }
+ static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
+@@ -528,8 +530,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
+  * @if_incoming: interface where the packet was received
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
++static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                                       int packet_len, unsigned long send_time,
+                                       bool direct_link,
+                                       struct batadv_hard_iface *if_incoming,
+@@ -553,13 +557,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+       skb = netdev_alloc_skb_ip_align(NULL, skb_size);
+       if (!skb)
+-              return;
++              return false;
+       forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
+                                                   queue_left, bat_priv, skb);
+       if (!forw_packet_aggr) {
+               kfree_skb(skb);
+-              return;
++              return false;
+       }
+       forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
+@@ -581,6 +585,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                         batadv_iv_send_outstanding_bat_ogm_packet);
+       batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
++
++      return true;
+ }
+ /* aggregate a new packet into the existing ogm packet */
+@@ -610,8 +616,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
+  * @send_time: timestamp (jiffies) when the packet is to be sent
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
++static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+                                   unsigned char *packet_buff,
+                                   int packet_len,
+                                   struct batadv_hard_iface *if_incoming,
+@@ -663,14 +671,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+               if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
+                       send_time += max_aggregation_jiffies;
+-              batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
+-                                          send_time, direct_link,
+-                                          if_incoming, if_outgoing,
+-                                          own_packet);
++              return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
++                                                 send_time, direct_link,
++                                                 if_incoming, if_outgoing,
++                                                 own_packet);
+       } else {
+               batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
+                                       packet_len, direct_link);
+               spin_unlock_bh(&bat_priv->forw_bat_list_lock);
++
++              return true;
+       }
+ }
+@@ -782,6 +792,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      bool reschedule = false;
++      bool scheduled;
+       int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -810,11 +822,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                                                      ogm_buff_len,
+                                                      BATADV_OGM_HLEN);
+               if (ret < 0) {
+-                      /* OGMs must be queued even when the buffer allocation for
+-                       * TVLVs failed. just fall back to the non-TVLV version
+-                       */
+-                      ret = 0;
+-                      *ogm_buff_len = BATADV_OGM_HLEN;
++                      reschedule = true;
++                      goto out;
+               }
+               tvlv_len = ret;
+@@ -836,8 +845,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               /* OGMs from secondary interfaces are only scheduled on their
+                * respective interfaces.
+                */
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
+-                                      hard_iface, hard_iface, 1, send_time);
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
++                                                  hard_iface, hard_iface, 1, send_time);
++              if (!scheduled)
++                      reschedule = true;
++
+               goto out;
+       }
+@@ -852,15 +864,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
+                       continue;
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
+-                                      *ogm_buff_len, hard_iface,
+-                                      tmp_hard_iface, 1, send_time);
+-
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
++                                                  *ogm_buff_len, hard_iface,
++                                                  tmp_hard_iface, 1, send_time);
+               batadv_hardif_put(tmp_hard_iface);
++
++              if (!scheduled && tmp_hard_iface == hard_iface)
++                      reschedule = true;
+       }
+       rcu_read_unlock();
+ out:
++      if (reschedule) {
++              /* there was a failure scheduling the own forward packet.
++               * as result, the batadv_iv_send_outstanding_bat_ogm_packet()
++               * work item is no longer scheduled. it is therefore necessary
++               * to reschedule it manually
++               */
++              queue_delayed_work(batadv_event_workqueue,
++                                 &hard_iface->bat_iv.reschedule_work,
++                                 msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
++      }
++
+       batadv_hardif_put(primary_if);
+ }
+@@ -875,6 +900,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+ }
++static void batadv_iv_ogm_reschedule(struct work_struct *work)
++{
++      struct delayed_work *delayed_work = to_delayed_work(work);
++      struct batadv_hard_iface *hard_iface;
++
++      hard_iface = container_of(delayed_work,
++                                struct batadv_hard_iface,
++                                bat_iv.reschedule_work);
++      batadv_iv_ogm_schedule(hard_iface);
++}
++
+ /**
+  * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
+  * @orig_node: originator which reproadcasted the OGMs directly
+@@ -2278,6 +2314,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
+ static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
+ {
++      INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
++
+       /* begin scheduling originator messages on that interface */
+       batadv_iv_ogm_schedule(hard_iface);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 19fd1c9adf4931..ff40fc9e36f66c 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -82,6 +82,9 @@ struct batadv_hard_iface_bat_iv {
+       /** @ogm_seqno: OGM sequence number - used to identify each OGM */
+       atomic_t ogm_seqno;
++      /** @reschedule_work: recover OGM schedule after schedule error */
++      struct delayed_work reschedule_work;
++
+       /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
+       struct mutex ogm_buff_mutex;
+ };
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/queue-5.15/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch
new file mode 100644 (file)
index 0000000..b74a7fa
--- /dev/null
@@ -0,0 +1,60 @@
+From 7b75765536dbc4a9a0778f10897cc9f9c8e21a5e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 22:27:23 +0200
+Subject: batman-adv: tp_meter: directly shut down timer on cleanup
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d5487249a81ea658717614009c8f46acc5b7101a upstream.
+
+batadv_tp_sender_cleanup() was calling timer_delete_sync() followed by
+timer_delete() to guard against the timer handler re-arming itself between
+the two calls. This double-deletion hack relied on the sending status being
+set to 0 to suppress re-arming.
+
+Replace both calls with a single timer_shutdown_sync(). This function both
+waits for any running timer callback to complete (like timer_delete_sync())
+and permanently disarms the timer so it cannot be re-armed afterwards,
+making re-arming prevention unconditional and self-documenting.
+
+The re-arming property is also required because otherwise:
+
+1. context 0 (batadv_tp_recv_ack()) checks in
+   batadv_tp_reset_sender_timer() if sending is still 1 -> it is
+2. context 1 changes in batadv_tp_sender_shutdown() sending to 0 and in
+   this process forces the kthread to stop timer in
+   batadv_tp_sender_cleanup()
+3. context 0 continues in batadv_tp_reset_sender_timer() and rearms the
+   timer -> but the reference for it is already gone
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ adapt pre-hunk to old del_timer* names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 464fca0ca8ac28..0eaeffba4b4ffe 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -384,13 +384,7 @@ static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
+       atomic_dec(&tp_vars->bat_priv->tp_num);
+       /* kill the timer and remove its reference */
+-      del_timer_sync(&tp_vars->timer);
+-      /* the worker might have rearmed itself therefore we kill it again. Note
+-       * that if the worker should run again before invoking the following
+-       * del_timer(), it would not re-arm itself once again because the status
+-       * is OFF now
+-       */
+-      del_timer(&tp_vars->timer);
++      timer_shutdown_sync(&tp_vars->timer);
+       batadv_tp_vars_put(tp_vars);
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-5.15/batman-adv-tt-avoid-empty-vlan-responses.patch
new file mode 100644 (file)
index 0000000..1c1a6c7
--- /dev/null
@@ -0,0 +1,91 @@
+From 1efdcd4a97c096e0812055453c0d9182de3090ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:02:51 +0200
+Subject: batman-adv: tt: avoid empty VLAN responses
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 upstream.
+
+The commit 16116dac2339 ("batman-adv: prevent TT request storms by not
+sending inconsistent TT TLVLs") added checks to the local (direct) TT
+response code. But the response can also be done indirectly by another node
+using the global TT state. To avoid such inconsistency states reported in
+the original fix, also avoid sending empty VLANs for replies from the
+global TT state.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 11c39a9ab90e46..e4d55b27f2551b 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+                                  s32 *tt_len)
+ {
+       u16 num_vlan = 0;
+-      u16 num_entries = 0;
+       u16 tvlv_len = 0;
+       unsigned int change_offset;
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_orig_node_vlan *vlan;
++      u16 total_entries = 0;
+       u8 *tt_change_ptr;
++      int vlan_entries;
+       spin_lock_bh(&orig_node->vlan_list_lock);
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              total_entries += vlan_entries;
+               num_vlan++;
+-              num_entries += atomic_read(&vlan->tt.num_entries);
+       }
+       change_offset = sizeof(**tt_data);
+@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       /* if tt_len is negative, allocate the space needed by the full table */
+       if (*tt_len < 0)
+-              *tt_len = batadv_tt_len(num_entries);
++              *tt_len = batadv_tt_len(total_entries);
+       if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) {
+               *tt_len = 0;
+@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              if (vlan_entries < 1)
++                      continue;
++
+               tt_vlan->vid = htons(vlan->vid);
+               tt_vlan->crc = htonl(vlan->tt.crc);
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-5.15/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
new file mode 100644 (file)
index 0000000..23d07de
--- /dev/null
@@ -0,0 +1,78 @@
+From 3aae4099e4f746a570e865fa8bb96f34889a2f00 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 23:04:23 +0200
+Subject: batman-adv: tt: fix TOCTOU race for reported vlans
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 94d27005016be15ffc638b2ecbc4d58805ad7b48 upstream.
+
+The local TT based TVLV is generated by first checking the number of VLANs
+which have at least one TT entry. A new buffer with the correct size for
+the VLANs is then allocated. Only then, the list of VLANs s used to fill
+the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock
+is held. But the actual number of TT entries of each VLAN can still
+increase during this time - just not the number of VLANs in the list.
+
+But the prefilter used in the buffer size calculation might still cause an
+increase of the number of VLANs which need to be stored. Simply because a
+VLAN might now suddenly have at least one entry when it had none in the
+pre-alloc check - and then needs to occupy space which was not allocated.
+
+It is better to overestimate the buffer size at the beginning and then fill
+the buffer only with the VLANs which are not empty.
+
+Cc: stable@kernel.org
+Fixes: 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 76cf40d859908a..11c39a9ab90e46 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+-              if (vlan_entries < 1)
+-                      continue;
+-
+-              num_vlan++;
+               total_entries += vlan_entries;
++              num_vlan++;
+       }
+       change_offset = sizeof(**tt_data);
+@@ -962,6 +959,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+               if (vlan_entries < 1)
+@@ -972,8 +970,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-5.15/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
new file mode 100644 (file)
index 0000000..324eb95
--- /dev/null
@@ -0,0 +1,196 @@
+From f500a77ad17a1fac80aea37f4ab5f19476ccf530 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:53:56 +0200
+Subject: batman-adv: tvlv: abort OGM send on tvlv append failure
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 501368506563e151b322c8c3f228b796e615b90d upstream.
+
+batadv_tvlv_container_ogm_append() could fail in two ways: a memory
+allocation failure when resizing the packet buffer, or the tvlv data
+exceeding U16_MAX bytes. In both cases the function previously returned the
+old (now stale) tvlv_value_len rather than signalling an error, causing the
+OGM/OGM2 send path to transmit a packet whose TVLV length field no longer
+matched the actual buffer contents. And because it also didn't fill in the
+new TVLV data, sending either uninitialized or corrupted data on the wire.
+
+All errors in batadv_tvlv_container_ogm_append() must be forwarded to the
+caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N.
+IV, it is currently not allowed to abort the send. The non-TVLV part of the
+OGM must be queued up instead.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 16 +++++++++++++---
+ net/batman-adv/bat_v_ogm.c  | 26 ++++++++++++++------------
+ net/batman-adv/tvlv.c       | 17 ++++++++++++-----
+ net/batman-adv/tvlv.h       |  2 +-
+ 4 files changed, 40 insertions(+), 21 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 58c18e22603d12..bc8685ffcb7100 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -782,6 +782,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -805,9 +806,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                * appended as it may alter the tt tvlv container
+                */
+               batadv_tt_local_commit_changes(bat_priv);
+-              tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+-                                                          ogm_buff_len,
+-                                                          BATADV_OGM_HLEN);
++              ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                                     ogm_buff_len,
++                                                     BATADV_OGM_HLEN);
++              if (ret < 0) {
++                      /* OGMs must be queued even when the buffer allocation for
++                       * TVLVs failed. just fall back to the non-TVLV version
++                       */
++                      ret = 0;
++                      *ogm_buff_len = BATADV_OGM_HLEN;
++              }
++
++              tvlv_len = ret;
+       }
+       batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 74295915653336..63337e02cf2f11 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -272,9 +272,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       struct batadv_hard_iface *hard_iface;
+       struct batadv_ogm2_packet *ogm_packet;
+       struct sk_buff *skb, *skb_tmp;
+-      unsigned char *ogm_buff;
+-      int ogm_buff_len;
+-      u16 tvlv_len = 0;
++      unsigned char **ogm_buff;
++      int *ogm_buff_len;
++      u16 tvlv_len;
+       int ret;
+       lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
+@@ -282,25 +282,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
+               goto out;
+-      ogm_buff = bat_priv->bat_v.ogm_buff;
+-      ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
++      ogm_buff = &bat_priv->bat_v.ogm_buff;
++      ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
++
+       /* tt changes have to be committed before the tvlv data is
+        * appended as it may alter the tt tvlv container
+        */
+       batadv_tt_local_commit_changes(bat_priv);
+-      tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
+-                                                  &ogm_buff_len,
+-                                                  BATADV_OGM2_HLEN);
++      ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                             ogm_buff_len,
++                                             BATADV_OGM2_HLEN);
++      if (ret < 0)
++              goto reschedule;
+-      bat_priv->bat_v.ogm_buff = ogm_buff;
+-      bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
++      tvlv_len = ret;
+-      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
++      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len);
+       if (!skb)
+               goto reschedule;
+       skb_reserve(skb, ETH_HLEN);
+-      skb_put_data(skb, ogm_buff, ogm_buff_len);
++      skb_put_data(skb, *ogm_buff, *ogm_buff_len);
+       ogm_packet = (struct batadv_ogm2_packet *)skb->data;
+       ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 992773376e51d4..4812137439708f 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -7,6 +7,7 @@
+ #include "main.h"
+ #include <linux/byteorder/generic.h>
++#include <linux/errno.h>
+ #include <linux/etherdevice.h>
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
+  * The ogm packet might be enlarged or shrunk depending on the current size
+  * and the size of the to-be-appended tvlv containers.
+  *
+- * Return: size of all appended tvlv containers in bytes.
++ * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
++ *  if operation failed
+  */
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len)
+ {
+@@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_len;
+       void *tvlv_value;
++      int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+@@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-
+-      if (!ret)
++      if (!ret) {
++              tvlv_len_ret = -ENOMEM;
+               goto end;
++      }
++
++      tvlv_len_ret = tvlv_value_len;
+       if (!tvlv_value_len)
+               goto end;
+@@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ end:
+       spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+-      return tvlv_value_len;
++
++      return tvlv_len_ret;
+ }
+ /**
+diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h
+index 54f2a35653d0f1..ffbc93f78688e5 100644
+--- a/net/batman-adv/tvlv.h
++++ b/net/batman-adv/tvlv.h
+@@ -15,7 +15,7 @@
+ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+                                   u8 type, u8 version,
+                                   void *tvlv_value, u16 tvlv_value_len);
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len);
+ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-5.15/batman-adv-tvlv-reject-oversized-tvlv-packets.patch
new file mode 100644 (file)
index 0000000..559646e
--- /dev/null
@@ -0,0 +1,85 @@
+From 85c934e513f72a0b78cd05213c98097a8436ee01 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 22:04:47 +0200
+Subject: batman-adv: tvlv: reject oversized TVLV packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f50487e3566358b2b982b7801945e858c78ad9ab upstream.
+
+batadv_tvlv_container_ogm_append() builds a TVLV packet section from
+the tvlv.container_list. The total size of this section is computed by
+batadv_tvlv_container_list_size(), which sums the sizes of all registered
+containers.
+
+The return type and accumulator in batadv_tvlv_container_list_size() were
+u16. If the accumulated size exceeds U16_MAX, the value wraps around,
+causing the subsequent allocation in batadv_tvlv_container_ogm_append()
+to be undersized. The memcpy-style copy that follows would then write
+beyond the end of the allocated buffer, corrupting kernel memory.
+
+Fix this by widening the return type of batadv_tvlv_container_list_size()
+to size_t. In batadv_tvlv_container_ogm_append(), check the computed length
+against U16_MAX before proceeding, and bail out as if the allocation had
+failed when the limit is exceeded.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 4812137439708f..de0c139426839c 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -13,6 +13,7 @@
+ #include <linux/if_ether.h>
+ #include <linux/kernel.h>
+ #include <linux/kref.h>
++#include <linux/limits.h>
+ #include <linux/list.h>
+ #include <linux/lockdep.h>
+ #include <linux/netdevice.h>
+@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
+  *
+  * Return: size of all currently registered tvlv containers in bytes.
+  */
+-static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
++static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+ {
+       struct batadv_tvlv_container *tvlv;
+-      u16 tvlv_len = 0;
++      size_t tvlv_len = 0;
+       lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
+@@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ {
+       struct batadv_tvlv_container *tvlv;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+-      u16 tvlv_value_len;
++      size_t tvlv_value_len;
+       void *tvlv_value;
+       int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+       tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
++      if (tvlv_value_len > U16_MAX) {
++              tvlv_len_ret = -E2BIG;
++              goto end;
++      }
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-- 
+2.53.0
+
diff --git a/queue-5.15/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-5.15/batman-adv-v-stop-ogmv2-on-disabled-interface.patch
new file mode 100644 (file)
index 0000000..96773ed
--- /dev/null
@@ -0,0 +1,149 @@
+From 8d7c7294774177317c2d949c9be471421273f9e1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:34:43 +0200
+Subject: batman-adv: v: stop OGMv2 on disabled interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f8ce8b8331a1bc44ad4905886a482214d428b253 upstream.
+
+When a batadv_hard_iface is disabled, its mesh_iface pointer is set to
+NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via
+batadv_v_ogm_queue_on_if() for interfaces that have since lost their
+mesh_iface association. This results in a NULL pointer dereference when
+batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the
+now NULL hard_iface->mesh_iface to retrieve the batadv_priv.
+
+It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that
+it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was
+called.
+
+Cc: stable@kernel.org
+Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index c357cf72396ebe..74295915653336 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -116,14 +116,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
+ /**
+  * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to send
+  * @hard_iface: the interface to use to send the OGM
+  */
+-static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
++static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
++                                  struct sk_buff *skb,
+                                   struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+-
+       if (hard_iface->if_status != BATADV_IF_ACTIVE) {
+               kfree_skb(skb);
+               return;
+@@ -190,6 +190,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+ /**
+  * batadv_v_ogm_aggr_send() - flush & send aggregation queue
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @hard_iface: the interface with the aggregation queue to flush
+  *
+  * Aggregates all OGMv2 packets currently in the aggregation queue into a
+@@ -199,7 +200,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+  *
+  * Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
+  */
+-static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
++static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
++                                 struct batadv_hard_iface *hard_iface)
+ {
+       unsigned int aggr_len = hard_iface->bat_v.aggr_len;
+       struct sk_buff *skb_aggr;
+@@ -229,27 +231,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
+               consume_skb(skb);
+       }
+-      batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
++      batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ }
+ /**
+  * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to queue
+  * @hard_iface: the interface to queue the OGM on
+  */
+-static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
++static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
++                                   struct sk_buff *skb,
+                                    struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      if (hard_iface->soft_iface != bat_priv->soft_iface) {
++              kfree_skb(skb);
++              return;
++      }
+       if (!atomic_read(&bat_priv->aggregated_ogms)) {
+-              batadv_v_ogm_send_to_if(skb, hard_iface);
++              batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+               return;
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+-              batadv_v_ogm_aggr_send(hard_iface);
++              batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
+@@ -348,7 +355,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+                       break;
+               }
+-              batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
++              batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+               batadv_hardif_put(hard_iface);
+       }
+       rcu_read_unlock();
+@@ -388,12 +395,14 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
+ {
+       struct batadv_hard_iface_bat_v *batv;
+       struct batadv_hard_iface *hard_iface;
++      struct batadv_priv *bat_priv;
+       batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
+       hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
++      bat_priv = netdev_priv(hard_iface->soft_iface);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+-      batadv_v_ogm_aggr_send(hard_iface);
++      batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+       batadv_v_ogm_start_queue_timer(hard_iface);
+@@ -583,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
+                  if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
+                  ogm_forward->ttl, if_incoming->net_dev->name);
+-      batadv_v_ogm_queue_on_if(skb, if_outgoing);
++      batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+ out:
+       batadv_orig_ifinfo_put(orig_ifinfo);
+-- 
+2.53.0
+
diff --git a/queue-5.15/drm-dp-add-edp-1.5-bit-definition.patch b/queue-5.15/drm-dp-add-edp-1.5-bit-definition.patch
new file mode 100644 (file)
index 0000000..ebb6d98
--- /dev/null
@@ -0,0 +1,41 @@
+From 19f0eb99335b3e44794620982a3312ab002e0f0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:03:16 +0300
+Subject: drm/dp: Add eDP 1.5 bit definition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Suraj Kandpal <suraj.kandpal@intel.com>
+
+commit 5dfc37a6b77bf6beedbd30d70184b54e1a08ccac upstream.
+
+Add the eDP revision bit value for 1.5.
+
+Spec: eDPv1.5 Table 16-5
+Signed-off-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Reviewed-by: Arun R Murthy <arun.r.murthy@intel.com>
+Tested-by: Ben Kao <ben.kao@intel.com>
+Acked-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250206063253.2827017-2-suraj.kandpal@intel.com
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/drm/drm_dp_helper.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
+index 9c7949ebc159eb..01422c2078f7db 100644
+--- a/include/drm/drm_dp_helper.h
++++ b/include/drm/drm_dp_helper.h
+@@ -942,6 +942,7 @@ struct drm_panel;
+ # define DP_EDP_14                        0x03
+ # define DP_EDP_14a                         0x04    /* eDP 1.4a */
+ # define DP_EDP_14b                         0x05    /* eDP 1.4b */
++# define DP_EDP_15                        0x06    /* eDP 1.5 */
+ #define DP_EDP_GENERAL_CAP_1              0x701
+ # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP         (1 << 0)
+-- 
+2.53.0
+
diff --git a/queue-5.15/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-5.15/drm-i915-psr-add-defininitions-for-intel_wa_register.patch
new file mode 100644 (file)
index 0000000..203c319
--- /dev/null
@@ -0,0 +1,73 @@
+From f639114e72baf50818faad11165ad9a1e2c437c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:03:14 +0300
+Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD
+ register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream.
+
+EDP specification says:
+
+"If either VSC SDP is unable to be transmitted 100 ns before the SU region,
+the Source device may optionally transmit the VSC SDP during the prior
+video scan line’s HBlank period There is a Intel specific drm dp register
+currently containing bits related how TCON can support PSR2 with SDP on
+prior line."
+
+Unfortunately many panels are having problems in implementing this. So
+there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to
+figure out if this is properly implemented on a panel or if panel doesn't
+require that 100 ns delay before the SU region. Here are the definitions in
+this custom DPCD address:
+
+0 = Panel doesn't support SDP on prior line
+1 = Panel supports SDP on prior line
+2 = Panel doesn't have 100ns requirement
+3 = Reserved
+
+Add definitions for this new register and it's values into new header
+intel_dpcd.h.
+
+v2: add INTEL_DPCD_ prefix to definitions
+
+Bspec: 74741
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com
+(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+ create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h
+
+diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h
+new file mode 100644
+index 00000000000000..4aea5326f2ed48
+--- /dev/null
++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: MIT */
++/*
++ * Copyright © 2026 Intel Corporation
++ */
++
++#ifndef __INTEL_DPCD_H__
++#define __INTEL_DPCD_H__
++
++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS                                     0x3f0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK        REG_GENMASK(1, 0)
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1                  0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE           1
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE                2
++
++#endif /* __INTEL_DPCD_H__ */
+-- 
+2.53.0
+
diff --git a/queue-5.15/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-5.15/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
new file mode 100644 (file)
index 0000000..75f3e47
--- /dev/null
@@ -0,0 +1,77 @@
+From 47d33a851e1598adf99cbb626e0f5fc46c990871 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:03:17 +0300
+Subject: drm/i915/psr: Apply Intel DPCD workaround when SDP on prior line used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit 4703049f768fc1c1caac754134118bee1a3af189 upstream.
+
+There is Intel specific workaround DPCD address containing workaround for
+case where SDP is on prior line. Apply this workaround according to values
+in the offset.
+
+Fixes: 61e887329e33 ("drm/i915/xelpd: Handle PSR2 SDP indication in the prior scanline")
+Cc: <stable@vger.kernel.org> # v5.15+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com
+(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_psr.c | 27 +++++++++++++++++++++++-
+ 1 file changed, 26 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index aa00e062a2a6aa..fca55ae3ffe5bd 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -824,6 +824,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp,
+       return true;
+ }
++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp,
++                                       struct intel_crtc_state *crtc_state)
++{
++      u8 early_scanline_support = intel_dp->intel_wa_dpcd &
++              INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK;
++
++      if (intel_dp->edp_dpcd[0] >= DP_EDP_15)
++              return true;
++
++      switch (early_scanline_support) {
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return false;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE:
++              return true;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return true;
++      default:
++              MISSING_CASE(early_scanline_support);
++              return false;
++      }
++}
++
+ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp,
+                                                       struct intel_crtc_state *crtc_state)
+ {
+@@ -844,7 +868,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d
+               return false;
+       crtc_state->req_psr2_sdp_prior_scanline = true;
+-      return true;
++
++      return apply_scanline_indication_wa(intel_dp, crtc_state);
+ }
+ static bool _compute_psr2_wake_times(struct intel_dp *intel_dp,
+-- 
+2.53.0
+
diff --git a/queue-5.15/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-5.15/drm-i915-psr-read-intel-dpcd-workaround-register.patch
new file mode 100644 (file)
index 0000000..fd43d73
--- /dev/null
@@ -0,0 +1,67 @@
+From 5799d01879ad52437f710dfeb3d0029965551a9d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:03:15 +0300
+Subject: drm/i915/psr: Read Intel DPCD workaround register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream.
+
+Read Intel DPCD workaround register and store it into
+intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains
+only PSR workaround for PSR2 SDP on prior scanline implementation.
+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com
+(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_display_types.h | 1 +
+ drivers/gpu/drm/i915/display/intel_psr.c           | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
+index 90e055f0569940..ef4c280d2cb630 100644
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -1554,6 +1554,7 @@ struct intel_dp {
+       u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE];
+       u8 fec_capable;
+       u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE];
++      u8 intel_wa_dpcd;
+       /* source rates */
+       int num_source_rates;
+       const int *source_rates;
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 93d2fd4cd16b7c..aa00e062a2a6aa 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -30,6 +30,7 @@
+ #include "intel_atomic.h"
+ #include "intel_de.h"
+ #include "intel_display_types.h"
++#include "intel_dpcd.h"
+ #include "intel_dp_aux.h"
+ #include "intel_hdmi.h"
+ #include "intel_psr.h"
+@@ -363,6 +364,12 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp)
+                       intel_dp_get_su_granularity(intel_dp);
+               }
+       }
++
++      if (intel_dp->psr.sink_psr2_support)
++              drm_dp_dpcd_read(&intel_dp->aux,
++                               INTEL_DPCD_INTEL_WA_REGISTER_CAPS,
++                               &intel_dp->intel_wa_dpcd,
++                               sizeof(intel_dp->intel_wa_dpcd));
+ }
+ static void hsw_psr_setup_aux(struct intel_dp *intel_dp)
+-- 
+2.53.0
+
diff --git a/queue-5.15/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-82 b/queue-5.15/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-82
new file mode 100644 (file)
index 0000000..20ca0ca
--- /dev/null
@@ -0,0 +1,68 @@
+From 664eabc5fb730c778018eea31e50440c07787ed6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:12:21 +0200
+Subject: RDMA/rxe: Fix double free in rxe_srq_from_init
+
+From: Jiasheng Jiang <jiashengjiangcool@gmail.com>
+
+commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream.
+
+In rxe_srq_from_init(), the queue pointer 'q' is assigned to
+'srq->rq.queue' before copying the SRQ number to user space.
+If copy_to_user() fails, the function calls rxe_queue_cleanup()
+to free the queue, but leaves the now-invalid pointer in
+'srq->rq.queue'.
+
+The caller of rxe_srq_from_init() (rxe_create_srq) eventually
+calls rxe_srq_cleanup() upon receiving the error, which triggers
+a second rxe_queue_cleanup() on the same memory, leading to a
+double free.
+
+The call trace looks like this:
+   kmem_cache_free+0x.../0x...
+   rxe_queue_cleanup+0x1a/0x30 [rdma_rxe]
+   rxe_srq_cleanup+0x42/0x60 [rdma_rxe]
+   rxe_elem_release+0x31/0x70 [rdma_rxe]
+   rxe_create_srq+0x12b/0x1a0 [rdma_rxe]
+   ib_create_srq_user+0x9a/0x150 [ib_core]
+
+Fix this by moving 'srq->rq.queue = q' after copy_to_user.
+
+Fixes: aae0484e15f0 ("IB/rxe: avoid srq memory leak")
+Signed-off-by: Jiasheng Jiang <jiashengjiangcool@gmail.com>
+Link: https://patch.msgid.link/20260112015412.29458-1-jiashengjiangcool@gmail.com
+Reviewed-by: Zhu Yanjun <yanjun.Zhu@linux.dev>
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+[bwh: Backported to 5.15: There was no assignment to init->attr.max_wr
+ here; don't add it]
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/infiniband/sw/rxe/rxe_srq.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
+index eb1c4c3b3a7865..595d4e7b91d0b8 100644
+--- a/drivers/infiniband/sw/rxe/rxe_srq.c
++++ b/drivers/infiniband/sw/rxe/rxe_srq.c
+@@ -100,8 +100,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               return -ENOMEM;
+       }
+-      srq->rq.queue = q;
+-
+       err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf,
+                          q->buf_size, &q->ip);
+       if (err) {
+@@ -118,6 +116,8 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               }
+       }
++      srq->rq.queue = q;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch b/queue-5.15/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch
new file mode 100644 (file)
index 0000000..5fad77a
--- /dev/null
@@ -0,0 +1,35 @@
+From 67f2ca027d973a97363336e1534dda681a13ccc6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:07:48 +0200
+Subject: Revert "RDMA/rxe: Fix double free in rxe_srq_from_init"
+
+From: Ben Hutchings <benh@debian.org>
+
+This reverts commit af5956243018918130d52c9f671efdb40bab3366, which
+was commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream.  The
+backported version did not move but duplicated the problematic
+assignment, so it did not fix the bug.  A proper backport will follow.
+
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/infiniband/sw/rxe/rxe_srq.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
+index 05ae3d183b21d1..eb1c4c3b3a7865 100644
+--- a/drivers/infiniband/sw/rxe/rxe_srq.c
++++ b/drivers/infiniband/sw/rxe/rxe_srq.c
+@@ -118,9 +118,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               }
+       }
+-      srq->rq.queue = q;
+-      init->attr.max_wr = srq->rq.max_wr;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-5.15/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch b/queue-5.15/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch
new file mode 100644 (file)
index 0000000..523e5ce
--- /dev/null
@@ -0,0 +1,108 @@
+From 7b3084888360b002704e9af39ce89f70c519fd7c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:33:11 +0200
+Subject: selftests: forwarding: lib: Add helpers for checksum handling
+
+From: Petr Machata <petrm@nvidia.com>
+
+commit 952e0ee38c7215c45192d8c899acd1830873f28b upstream.
+
+In order to generate IGMPv3 and MLDv2 packets on the fly, we will need
+helpers to calculate the packet checksum.
+
+The approach presented in this patch revolves around payload templates
+for mausezahn. These are mausezahn-like payload strings (01:23:45:...)
+with possibly one 2-byte sequence replaced with the word PAYLOAD. The
+main function is payload_template_calc_checksum(), which calculates
+RFC 1071 checksum of the message. There are further helpers to then
+convert the checksum to the payload format, and to expand it.
+
+For IPv6, MLDv2 message checksum is computed using a pseudoheader that
+differs from the header used in the payload itself. The fact that the
+two messages are different means that the checksum needs to be
+returned as a separate quantity, instead of being expanded in-place in
+the payload itself. Furthermore, the pseudoheader includes a length of
+the message. Much like the checksum, this needs to be expanded in
+mausezahn format. And likewise for number of addresses for (S,G)
+entries. Thus we have several places where a computed quantity needs
+to be presented in the payload format. Add a helper u16_to_bytes(),
+which will be used in all these cases.
+
+Signed-off-by: Petr Machata <petrm@nvidia.com>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 02cb2e6bacbb ("selftests: forwarding: vxlan_bridge_1d: fix test failure with br_netfilter enabled")
+[bwh: Backported to 5.15: adjust context]
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/net/forwarding/lib.sh | 56 +++++++++++++++++++
+ 1 file changed, 56 insertions(+)
+
+diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
+index 83e8f9466d6273..c570d8f65a0cec 100644
+--- a/tools/testing/selftests/net/forwarding/lib.sh
++++ b/tools/testing/selftests/net/forwarding/lib.sh
+@@ -1491,3 +1491,59 @@ brmcast_check_sg_state()
+               check_err_fail $should_fail $? "Entry $src has blocked flag"
+       done
+ }
++
++u16_to_bytes()
++{
++      local u16=$1; shift
++
++      printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'
++}
++
++# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),
++# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,
++# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)
++# stands for 00:00.
++payload_template_calc_checksum()
++{
++      local payload=$1; shift
++
++      (
++          # Set input radix.
++          echo "16i"
++          # Push zero for the initial checksum.
++          echo 0
++
++          # Pad the payload with a terminating 00: in case we get an odd
++          # number of bytes.
++          echo "${payload%:}:00:" |
++              sed 's/CHECKSUM/00:00/g' |
++              tr '[:lower:]' '[:upper:]' |
++              # Add the word to the checksum.
++              sed 's/\(..\):\(..\):/\1\2+\n/g' |
++              # Strip the extra odd byte we pushed if left unconverted.
++              sed 's/\(..\):$//'
++
++          echo "10000 ~ +"    # Calculate and add carry.
++          echo "FFFF r - p"   # Bit-flip and print.
++      ) |
++          dc |
++          tr '[:upper:]' '[:lower:]'
++}
++
++payload_template_expand_checksum()
++{
++      local payload=$1; shift
++      local checksum=$1; shift
++
++      local ckbytes=$(u16_to_bytes $checksum)
++
++      echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"
++}
++
++payload_template_nbytes()
++{
++      local payload=$1; shift
++
++      payload_template_expand_checksum "${payload%:}" 0 |
++              sed 's/:/\n/g' | wc -l
++}
+-- 
+2.53.0
+
index 1b2dcdd1466dfaa3de4b9d0ba313f2d642215178..aa34ceab69148dfc7fd4a803b05a782e2d4b5cc1 100644 (file)
@@ -32,3 +32,20 @@ bluetooth-l2cap-clear-chan-ident-on-ecred-reconfigur.patch
 bluetooth-l2cap-fix-possible-crash-on-l2cap_ecred_co.patch
 gpio-rockchip-convert-bank-clk-to-devm_clk_get_enabl.patch
 sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch
+batman-adv-v-stop-ogmv2-on-disabled-interface.patch
+batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
+batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
+batman-adv-tvlv-reject-oversized-tvlv-packets.patch
+batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
+selftests-forwarding-lib-add-helpers-for-checksum-ha.patch
+batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch
+batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
+batman-adv-tt-avoid-empty-vlan-responses.patch
+batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
+wifi-brcmfmac-fix-use-after-free-when-rescheduling-b.patch
+drm-i915-psr-add-defininitions-for-intel_wa_register.patch
+drm-i915-psr-read-intel-dpcd-workaround-register.patch
+drm-dp-add-edp-1.5-bit-definition.patch
+drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
+revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch
+rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-82
diff --git a/queue-5.15/wifi-brcmfmac-fix-use-after-free-when-rescheduling-b.patch b/queue-5.15/wifi-brcmfmac-fix-use-after-free-when-rescheduling-b.patch
new file mode 100644 (file)
index 0000000..7f397b6
--- /dev/null
@@ -0,0 +1,107 @@
+From 4e6cc40ea75661a3abf4b56f56c1d7c9a57a5ff9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:54:31 +0800
+Subject: wifi: brcmfmac: fix use-after-free when rescheduling
+ brcmf_btcoex_info work
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Duoming Zhou <duoming@zju.edu.cn>
+
+[ Upstream commit 9cb83d4be0b9b697eae93d321e0da999f9cdfcfc ]
+
+The brcmf_btcoex_detach() only shuts down the btcoex timer, if the
+flag timer_on is false. However, the brcmf_btcoex_timerfunc(), which
+runs as timer handler, sets timer_on to false. This creates critical
+race conditions:
+
+1.If brcmf_btcoex_detach() is called while brcmf_btcoex_timerfunc()
+is executing, it may observe timer_on as false and skip the call to
+timer_shutdown_sync().
+
+2.The brcmf_btcoex_timerfunc() may then reschedule the brcmf_btcoex_info
+worker after the cancel_work_sync() has been executed, resulting in
+use-after-free bugs.
+
+The use-after-free bugs occur in two distinct scenarios, depending on
+the timing of when the brcmf_btcoex_info struct is freed relative to
+the execution of its worker thread.
+
+Scenario 1: Freed before the worker is scheduled
+
+The brcmf_btcoex_info is deallocated before the worker is scheduled.
+A race condition can occur when schedule_work(&bt_local->work) is
+called after the target memory has been freed. The sequence of events
+is detailed below:
+
+CPU0                           | CPU1
+brcmf_btcoex_detach            | brcmf_btcoex_timerfunc
+                               |   bt_local->timer_on = false;
+  if (cfg->btcoex->timer_on)   |
+    ...                        |
+  cancel_work_sync();          |
+  ...                          |
+  kfree(cfg->btcoex); // FREE  |
+                               |   schedule_work(&bt_local->work); // USE
+
+Scenario 2: Freed after the worker is scheduled
+
+The brcmf_btcoex_info is freed after the worker has been scheduled
+but before or during its execution. In this case, statements within
+the brcmf_btcoex_handler() — such as the container_of macro and
+subsequent dereferences of the brcmf_btcoex_info object will cause
+a use-after-free access. The following timeline illustrates this
+scenario:
+
+CPU0                            | CPU1
+brcmf_btcoex_detach             | brcmf_btcoex_timerfunc
+                                |   bt_local->timer_on = false;
+  if (cfg->btcoex->timer_on)    |
+    ...                         |
+  cancel_work_sync();           |
+  ...                           |   schedule_work(); // Reschedule
+                                |
+  kfree(cfg->btcoex); // FREE   |   brcmf_btcoex_handler() // Worker
+  /*                            |     btci = container_of(....); // USE
+   The kfree() above could      |     ...
+   also occur at any point      |     btci-> // USE
+   during the worker's execution|
+   */                           |
+
+To resolve the race conditions, drop the conditional check and call
+timer_shutdown_sync() directly. It can deactivate the timer reliably,
+regardless of its current state. Once stopped, the timer_on state is
+then set to false.
+
+Fixes: 61730d4dfffc ("brcmfmac: support critical protocol API for DHCP")
+Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
+Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
+Link: https://patch.msgid.link/20250822050839.4413-1-duoming@zju.edu.cn
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Robert Garcia <rob_garcia@163.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
+index f9f18ff451ea7c..f46e4090021777 100644
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
+@@ -392,10 +392,8 @@ void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg)
+       if (!cfg->btcoex)
+               return;
+-      if (cfg->btcoex->timer_on) {
+-              cfg->btcoex->timer_on = false;
+-              del_timer_sync(&cfg->btcoex->timer);
+-      }
++      del_timer_sync(&cfg->btcoex->timer);
++      cfg->btcoex->timer_on = false;
+       cancel_work_sync(&cfg->btcoex->work);
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-6.1/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
new file mode 100644 (file)
index 0000000..0b0d3f2
--- /dev/null
@@ -0,0 +1,232 @@
+From 68a8ce4e3c644d0a83009441df3fa637ed068bac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 21:57:07 +0200
+Subject: batman-adv: bla: avoid double decrement of bla.num_requests
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 83ab69bd12b80f6ea169c8bea6977701b53a043d upstream.
+
+The bla.num_requests is increased when no request_sent was in progress. And
+it is decremented in various places (announcement was received, backbone is
+purged, periodic work). But the check if the request_sent is actually set
+to a specific state and the atomic_dec/_inc are not safe because they are
+not atomic (TOCTOU) and multiple such code portions can run concurrently.
+
+At the same time, it is necessary to modify request_sent (state) and
+bla.num_requests atomically. Otherwise batadv_bla_send_request() might set
+request_sent to 1 and is interrupted.  batadv_handle_announce() can then
+set request_sent back to 0 and decrement num_requests before
+batadv_bla_send_request() incremented it.
+
+The two operations must therefore be locked. And since state (request_sent)
+and wait_periods are only accessed inside this lock, they can be converted
+to simpler datatypes. And to avoid that the bla.num_requests is touched by
+a parallel running context with a valid backbone_gw reference after
+batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to
+correctly signal that a backbone_gw is in the state of being cleaned up.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++--------
+ net/batman-adv/soft-interface.c        |  1 +
+ net/batman-adv/types.h                 | 39 ++++++++++++++++----
+ 3 files changed, 67 insertions(+), 24 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index 76d8c91c156a3b..cfb1eb25c6ac4d 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -516,8 +516,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+-      atomic_set(&entry->request_sent, 0);
+-      atomic_set(&entry->wait_periods, 0);
++      entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
++      entry->wait_periods = 0;
+       ether_addr_copy(entry->orig, orig);
+       INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
+       kref_init(&entry->refcount);
+@@ -546,9 +546,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+               batadv_bla_send_announce(bat_priv, entry);
+               /* this will be decreased in the worker thread */
+-              atomic_inc(&entry->request_sent);
+-              atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
+-              atomic_inc(&bat_priv->bla.num_requests);
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++                      entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
++                      entry->wait_periods = BATADV_BLA_WAIT_PERIODS;
++                      atomic_inc(&bat_priv->bla.num_requests);
++              }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       return entry;
+@@ -651,10 +655,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw)
+                             backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);
+       /* no local broadcasts should be sent or received, for now. */
+-      if (!atomic_read(&backbone_gw->request_sent)) {
++      spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
++      if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++              backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
+               atomic_inc(&backbone_gw->bat_priv->bla.num_requests);
+-              atomic_set(&backbone_gw->request_sent, 1);
+       }
++      spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
+ }
+ /**
+@@ -875,10 +881,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               /* if we have sent a request and the crc was OK,
+                * we can allow traffic again.
+                */
+-              if (atomic_read(&backbone_gw->request_sent)) {
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) {
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
+               }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       batadv_backbone_gw_put(backbone_gw);
+@@ -1257,9 +1265,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                               purged = true;
+                               /* don't wait for the pending request anymore */
+-                              if (atomic_read(&backbone_gw->request_sent))
++                              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED)
+                                       atomic_dec(&bat_priv->bla.num_requests);
++                              backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
++                              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
++
+                               batadv_bla_del_backbone_claims(backbone_gw);
+                               hlist_del_rcu(&backbone_gw->hash_entry);
+@@ -1510,7 +1522,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                               batadv_bla_send_loopdetect(bat_priv,
+                                                          backbone_gw);
+-                      /* request_sent is only set after creation to avoid
++                      /* state is only set to unsynced after creation to avoid
+                        * problems when we are not yet known as backbone gw
+                        * in the backbone.
+                        *
+@@ -1519,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                        * some grace time.
+                        */
+-                      if (atomic_read(&backbone_gw->request_sent) == 0)
+-                              continue;
++                      spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                      if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
++                              goto unlock_next;
+-                      if (!atomic_dec_and_test(&backbone_gw->wait_periods))
+-                              continue;
++                      if (backbone_gw->wait_periods > 0)
++                              backbone_gw->wait_periods--;
++
++                      if (backbone_gw->wait_periods > 0)
++                              goto unlock_next;
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
++
++unlock_next:
++                      spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+               }
+               rcu_read_unlock();
+       }
+diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
+index d7b525a495e451..c0549abcae7a64 100644
+--- a/net/batman-adv/soft-interface.c
++++ b/net/batman-adv/soft-interface.c
+@@ -785,6 +785,7 @@ static int batadv_softif_init_late(struct net_device *dev)
+       atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
+ #ifdef CONFIG_BATMAN_ADV_BLA
+       atomic_set(&bat_priv->bla.num_requests, 0);
++      spin_lock_init(&bat_priv->bla.num_requests_lock);
+ #endif
+       atomic_set(&bat_priv->tp_num, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 41959a4eea7d3f..12fbcc89581adf 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1026,6 +1026,12 @@ struct batadv_priv_bla {
+       /** @num_requests: number of bla requests in flight */
+       atomic_t num_requests;
++      /**
++       * @num_requests_lock: locks update num_requests +
++       * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update
++       */
++      spinlock_t num_requests_lock;
++
+       /**
+        * @claim_hash: hash table containing mesh nodes this host has claimed
+        */
+@@ -1748,6 +1754,27 @@ struct batadv_priv {
+ #ifdef CONFIG_BATMAN_ADV_BLA
++enum batadv_bla_backbone_gw_state {
++      /**
++       * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed
++       * and it must not longer work on requests
++       */
++      BATADV_BLA_BACKBONE_GW_STOPPED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out
++       * of sync and a request was send. No traffic is forwarded until the
++       * situation is resolved
++       */
++      BATADV_BLA_BACKBONE_GW_UNSYNCED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in
++       * sync. traffic can be forwarded
++       */
++      BATADV_BLA_BACKBONE_GW_SYNCED,
++};
++
+ /**
+  * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
+  */
+@@ -1773,16 +1800,12 @@ struct batadv_bla_backbone_gw {
+       /**
+        * @wait_periods: grace time for bridge forward delays and bla group
+        *  forming at bootup phase - no bcast traffic is formwared until it has
+-       *  elapsed
++       *  elapsed. Must only be access with num_requests_lock.
+        */
+-      atomic_t wait_periods;
++      u8 wait_periods;
+-      /**
+-       * @request_sent: if this bool is set to true we are out of sync with
+-       *  this backbone gateway - no bcast traffic is formwared until the
+-       *  situation was resolved
+-       */
+-      atomic_t request_sent;
++      /** @state: sync state. Must only be access with num_requests_lock. */
++      enum batadv_bla_backbone_gw_state state;
+       /** @crc: crc16 checksum over all claims */
+       u16 crc;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-6.1/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
new file mode 100644 (file)
index 0000000..d0edac1
--- /dev/null
@@ -0,0 +1,53 @@
+From a1ca0972a60493b83816b12870b9ea3cfea52191 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 21:57:36 +0200
+Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 upstream.
+
+Without rtnl_lock held, a hardif might be retrieved as primary interface of
+a meshif, but then (while operating on this interface) getting decoupled
+from the mesh interface. In this case, the meshif still exists but the
+pointer from the primary hardif to the meshif is set to NULL.
+
+The mesh_iface must be checked first to be non-NULL before continuing to
+send an ARP request using meshif.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Reported-by: Ido Schimmel <idosch@nvidia.com>
+Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index f614709e6cda74..76d8c91c156a3b 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
+              sizeof(local_claim_dest));
+       local_claim_dest.type = claimtype;
+-      soft_iface = primary_if->soft_iface;
++      soft_iface = READ_ONCE(primary_if->soft_iface);
++      if (!soft_iface)
++              goto out;
+       skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
+                        /* IP DST: 0.0.0.0 */
+                        zeroip,
+-                       primary_if->soft_iface,
++                       soft_iface,
+                        /* IP SRC: 0.0.0.0 */
+                        zeroip,
+                        /* Ethernet DST: Broadcast */
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-6.1/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
new file mode 100644 (file)
index 0000000..7b29f61
--- /dev/null
@@ -0,0 +1,230 @@
+From f7dc6d9db4eaf933b61e16c80e22cde0030c4be7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 21:56:18 +0200
+Subject: batman-adv: iv: recover OGM scheduling after forward packet error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit aa3153bd139a6c48667dcd02608d3b2c80bff02c upstream.
+
+When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward
+packet for OGM transmission, the work item that drives periodic OGM
+scheduling is never re-armed. This silently halts transmission of the
+node's own OGMs on the affected interface — only OGMs from other peers
+continue to be aggregated and forwarded.
+
+Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively
+batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet.
+When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing
+a dedicated recovery work item (reschedule_work) that fires after one
+originator interval and calls batadv_iv_ogm_schedule() again.
+
+Cc: stable@kernel.org
+Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++----------
+ net/batman-adv/types.h      |  3 ++
+ 2 files changed, 60 insertions(+), 19 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index c93d30462054ce..c52e3b82889868 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -224,6 +224,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_iv.ogm_buff = NULL;
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
++
++      cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
+ }
+ static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
+@@ -528,8 +530,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
+  * @if_incoming: interface where the packet was received
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
++static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                                       int packet_len, unsigned long send_time,
+                                       bool direct_link,
+                                       struct batadv_hard_iface *if_incoming,
+@@ -553,13 +557,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+       skb = netdev_alloc_skb_ip_align(NULL, skb_size);
+       if (!skb)
+-              return;
++              return false;
+       forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
+                                                   queue_left, bat_priv, skb);
+       if (!forw_packet_aggr) {
+               kfree_skb(skb);
+-              return;
++              return false;
+       }
+       forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
+@@ -581,6 +585,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                         batadv_iv_send_outstanding_bat_ogm_packet);
+       batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
++
++      return true;
+ }
+ /* aggregate a new packet into the existing ogm packet */
+@@ -610,8 +616,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
+  * @send_time: timestamp (jiffies) when the packet is to be sent
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
++static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+                                   unsigned char *packet_buff,
+                                   int packet_len,
+                                   struct batadv_hard_iface *if_incoming,
+@@ -663,14 +671,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+               if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
+                       send_time += max_aggregation_jiffies;
+-              batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
+-                                          send_time, direct_link,
+-                                          if_incoming, if_outgoing,
+-                                          own_packet);
++              return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
++                                                 send_time, direct_link,
++                                                 if_incoming, if_outgoing,
++                                                 own_packet);
+       } else {
+               batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
+                                       packet_len, direct_link);
+               spin_unlock_bh(&bat_priv->forw_bat_list_lock);
++
++              return true;
+       }
+ }
+@@ -782,6 +792,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      bool reschedule = false;
++      bool scheduled;
+       int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -810,11 +822,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                                                      ogm_buff_len,
+                                                      BATADV_OGM_HLEN);
+               if (ret < 0) {
+-                      /* OGMs must be queued even when the buffer allocation for
+-                       * TVLVs failed. just fall back to the non-TVLV version
+-                       */
+-                      ret = 0;
+-                      *ogm_buff_len = BATADV_OGM_HLEN;
++                      reschedule = true;
++                      goto out;
+               }
+               tvlv_len = ret;
+@@ -836,8 +845,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               /* OGMs from secondary interfaces are only scheduled on their
+                * respective interfaces.
+                */
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
+-                                      hard_iface, hard_iface, 1, send_time);
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
++                                                  hard_iface, hard_iface, 1, send_time);
++              if (!scheduled)
++                      reschedule = true;
++
+               goto out;
+       }
+@@ -852,15 +864,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
+                       continue;
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
+-                                      *ogm_buff_len, hard_iface,
+-                                      tmp_hard_iface, 1, send_time);
+-
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
++                                                  *ogm_buff_len, hard_iface,
++                                                  tmp_hard_iface, 1, send_time);
+               batadv_hardif_put(tmp_hard_iface);
++
++              if (!scheduled && tmp_hard_iface == hard_iface)
++                      reschedule = true;
+       }
+       rcu_read_unlock();
+ out:
++      if (reschedule) {
++              /* there was a failure scheduling the own forward packet.
++               * as result, the batadv_iv_send_outstanding_bat_ogm_packet()
++               * work item is no longer scheduled. it is therefore necessary
++               * to reschedule it manually
++               */
++              queue_delayed_work(batadv_event_workqueue,
++                                 &hard_iface->bat_iv.reschedule_work,
++                                 msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
++      }
++
+       batadv_hardif_put(primary_if);
+ }
+@@ -875,6 +900,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+ }
++static void batadv_iv_ogm_reschedule(struct work_struct *work)
++{
++      struct delayed_work *delayed_work = to_delayed_work(work);
++      struct batadv_hard_iface *hard_iface;
++
++      hard_iface = container_of(delayed_work,
++                                struct batadv_hard_iface,
++                                bat_iv.reschedule_work);
++      batadv_iv_ogm_schedule(hard_iface);
++}
++
+ /**
+  * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
+  * @orig_node: originator which reproadcasted the OGMs directly
+@@ -2278,6 +2314,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
+ static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
+ {
++      INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
++
+       /* begin scheduling originator messages on that interface */
+       batadv_iv_ogm_schedule(hard_iface);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index afd71f061c409a..41959a4eea7d3f 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -82,6 +82,9 @@ struct batadv_hard_iface_bat_iv {
+       /** @ogm_seqno: OGM sequence number - used to identify each OGM */
+       atomic_t ogm_seqno;
++      /** @reschedule_work: recover OGM schedule after schedule error */
++      struct delayed_work reschedule_work;
++
+       /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
+       struct mutex ogm_buff_mutex;
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/queue-6.1/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch
new file mode 100644 (file)
index 0000000..6cc9cda
--- /dev/null
@@ -0,0 +1,60 @@
+From 19af649dbcd2e1b375b1041cb1d79e1133664527 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 22:25:20 +0200
+Subject: batman-adv: tp_meter: directly shut down timer on cleanup
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d5487249a81ea658717614009c8f46acc5b7101a upstream.
+
+batadv_tp_sender_cleanup() was calling timer_delete_sync() followed by
+timer_delete() to guard against the timer handler re-arming itself between
+the two calls. This double-deletion hack relied on the sending status being
+set to 0 to suppress re-arming.
+
+Replace both calls with a single timer_shutdown_sync(). This function both
+waits for any running timer callback to complete (like timer_delete_sync())
+and permanently disarms the timer so it cannot be re-armed afterwards,
+making re-arming prevention unconditional and self-documenting.
+
+The re-arming property is also required because otherwise:
+
+1. context 0 (batadv_tp_recv_ack()) checks in
+   batadv_tp_reset_sender_timer() if sending is still 1 -> it is
+2. context 1 changes in batadv_tp_sender_shutdown() sending to 0 and in
+   this process forces the kthread to stop timer in
+   batadv_tp_sender_cleanup()
+3. context 0 continues in batadv_tp_reset_sender_timer() and rearms the
+   timer -> but the reference for it is already gone
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ adapt pre-hunk to old del_timer* names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 61e6cb5bce8ec5..707f05aa14791f 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -384,13 +384,7 @@ static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
+       atomic_dec(&tp_vars->bat_priv->tp_num);
+       /* kill the timer and remove its reference */
+-      del_timer_sync(&tp_vars->timer);
+-      /* the worker might have rearmed itself therefore we kill it again. Note
+-       * that if the worker should run again before invoking the following
+-       * del_timer(), it would not re-arm itself once again because the status
+-       * is OFF now
+-       */
+-      del_timer(&tp_vars->timer);
++      timer_shutdown_sync(&tp_vars->timer);
+       batadv_tp_vars_put(tp_vars);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-6.1/batman-adv-tt-avoid-empty-vlan-responses.patch
new file mode 100644 (file)
index 0000000..83cf982
--- /dev/null
@@ -0,0 +1,91 @@
+From 9d0b6f2b04a199e7db832b5a18664c2445697373 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 21:44:38 +0200
+Subject: batman-adv: tt: avoid empty VLAN responses
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 upstream.
+
+The commit 16116dac2339 ("batman-adv: prevent TT request storms by not
+sending inconsistent TT TLVLs") added checks to the local (direct) TT
+response code. But the response can also be done indirectly by another node
+using the global TT state. To avoid such inconsistency states reported in
+the original fix, also avoid sending empty VLANs for replies from the
+global TT state.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index ac6c05971348d8..e0503c8f24c353 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+                                  s32 *tt_len)
+ {
+       u16 num_vlan = 0;
+-      u16 num_entries = 0;
+       u16 tvlv_len = 0;
+       unsigned int change_offset;
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_orig_node_vlan *vlan;
++      u16 total_entries = 0;
+       u8 *tt_change_ptr;
++      int vlan_entries;
+       spin_lock_bh(&orig_node->vlan_list_lock);
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              total_entries += vlan_entries;
+               num_vlan++;
+-              num_entries += atomic_read(&vlan->tt.num_entries);
+       }
+       change_offset = sizeof(**tt_data);
+@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       /* if tt_len is negative, allocate the space needed by the full table */
+       if (*tt_len < 0)
+-              *tt_len = batadv_tt_len(num_entries);
++              *tt_len = batadv_tt_len(total_entries);
+       if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) {
+               *tt_len = 0;
+@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              if (vlan_entries < 1)
++                      continue;
++
+               tt_vlan->vid = htons(vlan->vid);
+               tt_vlan->crc = htonl(vlan->tt.crc);
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-6.1/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
new file mode 100644 (file)
index 0000000..9eeb573
--- /dev/null
@@ -0,0 +1,78 @@
+From b79a409c07b9c294e6342ce42bdf10431a60cdc3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 23:02:13 +0200
+Subject: batman-adv: tt: fix TOCTOU race for reported vlans
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 94d27005016be15ffc638b2ecbc4d58805ad7b48 upstream.
+
+The local TT based TVLV is generated by first checking the number of VLANs
+which have at least one TT entry. A new buffer with the correct size for
+the VLANs is then allocated. Only then, the list of VLANs s used to fill
+the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock
+is held. But the actual number of TT entries of each VLAN can still
+increase during this time - just not the number of VLANs in the list.
+
+But the prefilter used in the buffer size calculation might still cause an
+increase of the number of VLANs which need to be stored. Simply because a
+VLAN might now suddenly have at least one entry when it had none in the
+pre-alloc check - and then needs to occupy space which was not allocated.
+
+It is better to overestimate the buffer size at the beginning and then fill
+the buffer only with the VLANs which are not empty.
+
+Cc: stable@kernel.org
+Fixes: 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 6c40d690ac6c3a..ac6c05971348d8 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+-              if (vlan_entries < 1)
+-                      continue;
+-
+-              num_vlan++;
+               total_entries += vlan_entries;
++              num_vlan++;
+       }
+       change_offset = sizeof(**tt_data);
+@@ -964,6 +961,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+               if (vlan_entries < 1)
+@@ -974,8 +972,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch b/queue-6.1/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch
new file mode 100644 (file)
index 0000000..e13eead
--- /dev/null
@@ -0,0 +1,62 @@
+From aaa2b73ec93daa8afe658d29061aa9a3da688234 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 21:43:08 +0200
+Subject: batman-adv: tt: reject oversized local TVLV buffers
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 1e9fab756f8395096d5bba7be0c373c4c8f5d165 upstream.
+
+The commit 3a359bf5c61d ("batman-adv: reject oversized global TT response
+buffers") added a check to ensure that a global return buffer size can be
+stored in an u16. The same buffer handling also exists for the local data
+buffer but was not touched.
+
+A similar check should be also be in place for the local TVLV buffer. It
+doesn't have the similar attack surface because it is only generated from
+locally discovered MAC addresses but the dynamic nature could still cause
+temporarily to large buffers.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index f1e93a60482038..6c40d690ac6c3a 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -924,12 +924,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+ {
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_softif_vlan *vlan;
++      size_t change_offset;
+       u16 num_vlan = 0;
+       u16 vlan_entries = 0;
+       u16 total_entries = 0;
+       u16 tvlv_len;
+       u8 *tt_change_ptr;
+-      int change_offset;
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+@@ -948,8 +948,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       if (*tt_len < 0)
+               *tt_len = batadv_tt_len(total_entries);
+-      tvlv_len = *tt_len;
+-      tvlv_len += change_offset;
++      if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) {
++              tvlv_len = 0;
++              goto out;
++      }
+       *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
+       if (!*tt_data) {
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-6.1/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
new file mode 100644 (file)
index 0000000..f3960f5
--- /dev/null
@@ -0,0 +1,196 @@
+From 904f40570779f3c8e51cfcf7c6ca46ab95084ea4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:51:13 +0200
+Subject: batman-adv: tvlv: abort OGM send on tvlv append failure
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 501368506563e151b322c8c3f228b796e615b90d upstream.
+
+batadv_tvlv_container_ogm_append() could fail in two ways: a memory
+allocation failure when resizing the packet buffer, or the tvlv data
+exceeding U16_MAX bytes. In both cases the function previously returned the
+old (now stale) tvlv_value_len rather than signalling an error, causing the
+OGM/OGM2 send path to transmit a packet whose TVLV length field no longer
+matched the actual buffer contents. And because it also didn't fill in the
+new TVLV data, sending either uninitialized or corrupted data on the wire.
+
+All errors in batadv_tvlv_container_ogm_append() must be forwarded to the
+caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N.
+IV, it is currently not allowed to abort the send. The non-TVLV part of the
+OGM must be queued up instead.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 16 +++++++++++++---
+ net/batman-adv/bat_v_ogm.c  | 26 ++++++++++++++------------
+ net/batman-adv/tvlv.c       | 17 ++++++++++++-----
+ net/batman-adv/tvlv.h       |  2 +-
+ 4 files changed, 40 insertions(+), 21 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 495196b741040e..c93d30462054ce 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -782,6 +782,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -805,9 +806,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                * appended as it may alter the tt tvlv container
+                */
+               batadv_tt_local_commit_changes(bat_priv);
+-              tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+-                                                          ogm_buff_len,
+-                                                          BATADV_OGM_HLEN);
++              ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                                     ogm_buff_len,
++                                                     BATADV_OGM_HLEN);
++              if (ret < 0) {
++                      /* OGMs must be queued even when the buffer allocation for
++                       * TVLVs failed. just fall back to the non-TVLV version
++                       */
++                      ret = 0;
++                      *ogm_buff_len = BATADV_OGM_HLEN;
++              }
++
++              tvlv_len = ret;
+       }
+       batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 24beb06f7c332a..57d0c6862f449c 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -272,9 +272,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       struct batadv_hard_iface *hard_iface;
+       struct batadv_ogm2_packet *ogm_packet;
+       struct sk_buff *skb, *skb_tmp;
+-      unsigned char *ogm_buff;
+-      int ogm_buff_len;
+-      u16 tvlv_len = 0;
++      unsigned char **ogm_buff;
++      int *ogm_buff_len;
++      u16 tvlv_len;
+       int ret;
+       lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
+@@ -282,25 +282,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
+               goto out;
+-      ogm_buff = bat_priv->bat_v.ogm_buff;
+-      ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
++      ogm_buff = &bat_priv->bat_v.ogm_buff;
++      ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
++
+       /* tt changes have to be committed before the tvlv data is
+        * appended as it may alter the tt tvlv container
+        */
+       batadv_tt_local_commit_changes(bat_priv);
+-      tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
+-                                                  &ogm_buff_len,
+-                                                  BATADV_OGM2_HLEN);
++      ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                             ogm_buff_len,
++                                             BATADV_OGM2_HLEN);
++      if (ret < 0)
++              goto reschedule;
+-      bat_priv->bat_v.ogm_buff = ogm_buff;
+-      bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
++      tvlv_len = ret;
+-      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
++      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len);
+       if (!skb)
+               goto reschedule;
+       skb_reserve(skb, ETH_HLEN);
+-      skb_put_data(skb, ogm_buff, ogm_buff_len);
++      skb_put_data(skb, *ogm_buff, *ogm_buff_len);
+       ogm_packet = (struct batadv_ogm2_packet *)skb->data;
+       ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 7ec2e234388454..83e20b0be747b6 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -8,6 +8,7 @@
+ #include <linux/byteorder/generic.h>
+ #include <linux/container_of.h>
++#include <linux/errno.h>
+ #include <linux/etherdevice.h>
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
+  * The ogm packet might be enlarged or shrunk depending on the current size
+  * and the size of the to-be-appended tvlv containers.
+  *
+- * Return: size of all appended tvlv containers in bytes.
++ * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
++ *  if operation failed
+  */
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len)
+ {
+@@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_len;
+       void *tvlv_value;
++      int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+@@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-
+-      if (!ret)
++      if (!ret) {
++              tvlv_len_ret = -ENOMEM;
+               goto end;
++      }
++
++      tvlv_len_ret = tvlv_value_len;
+       if (!tvlv_value_len)
+               goto end;
+@@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ end:
+       spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+-      return tvlv_value_len;
++
++      return tvlv_len_ret;
+ }
+ /**
+diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h
+index 4cf8af00fc11a0..485b2a6070994b 100644
+--- a/net/batman-adv/tvlv.h
++++ b/net/batman-adv/tvlv.h
+@@ -15,7 +15,7 @@
+ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+                                   u8 type, u8 version,
+                                   void *tvlv_value, u16 tvlv_value_len);
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len);
+ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-6.1/batman-adv-tvlv-reject-oversized-tvlv-packets.patch
new file mode 100644 (file)
index 0000000..b39a750
--- /dev/null
@@ -0,0 +1,85 @@
+From 6d2cb253dc1b123413640e284d211f8c1fb70d2e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 21:47:23 +0200
+Subject: batman-adv: tvlv: reject oversized TVLV packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f50487e3566358b2b982b7801945e858c78ad9ab upstream.
+
+batadv_tvlv_container_ogm_append() builds a TVLV packet section from
+the tvlv.container_list. The total size of this section is computed by
+batadv_tvlv_container_list_size(), which sums the sizes of all registered
+containers.
+
+The return type and accumulator in batadv_tvlv_container_list_size() were
+u16. If the accumulated size exceeds U16_MAX, the value wraps around,
+causing the subsequent allocation in batadv_tvlv_container_ogm_append()
+to be undersized. The memcpy-style copy that follows would then write
+beyond the end of the allocated buffer, corrupting kernel memory.
+
+Fix this by widening the return type of batadv_tvlv_container_list_size()
+to size_t. In batadv_tvlv_container_ogm_append(), check the computed length
+against U16_MAX before proceeding, and bail out as if the allocation had
+failed when the limit is exceeded.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 83e20b0be747b6..99e5e8518dcc9a 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -13,6 +13,7 @@
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+ #include <linux/kref.h>
++#include <linux/limits.h>
+ #include <linux/list.h>
+ #include <linux/lockdep.h>
+ #include <linux/netdevice.h>
+@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
+  *
+  * Return: size of all currently registered tvlv containers in bytes.
+  */
+-static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
++static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+ {
+       struct batadv_tvlv_container *tvlv;
+-      u16 tvlv_len = 0;
++      size_t tvlv_len = 0;
+       lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
+@@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ {
+       struct batadv_tvlv_container *tvlv;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+-      u16 tvlv_value_len;
++      size_t tvlv_value_len;
+       void *tvlv_value;
+       int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+       tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
++      if (tvlv_value_len > U16_MAX) {
++              tvlv_len_ret = -E2BIG;
++              goto end;
++      }
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-- 
+2.53.0
+
diff --git a/queue-6.1/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-6.1/batman-adv-v-stop-ogmv2-on-disabled-interface.patch
new file mode 100644 (file)
index 0000000..1e7b9cd
--- /dev/null
@@ -0,0 +1,149 @@
+From 3d84d70d95aa09f24375c45f8fbfc7f0b503ae44 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:32:39 +0200
+Subject: batman-adv: v: stop OGMv2 on disabled interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f8ce8b8331a1bc44ad4905886a482214d428b253 upstream.
+
+When a batadv_hard_iface is disabled, its mesh_iface pointer is set to
+NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via
+batadv_v_ogm_queue_on_if() for interfaces that have since lost their
+mesh_iface association. This results in a NULL pointer dereference when
+batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the
+now NULL hard_iface->mesh_iface to retrieve the batadv_priv.
+
+It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that
+it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was
+called.
+
+Cc: stable@kernel.org
+Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index deef817b28f0ae..24beb06f7c332a 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -116,14 +116,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
+ /**
+  * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to send
+  * @hard_iface: the interface to use to send the OGM
+  */
+-static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
++static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
++                                  struct sk_buff *skb,
+                                   struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+-
+       if (hard_iface->if_status != BATADV_IF_ACTIVE) {
+               kfree_skb(skb);
+               return;
+@@ -190,6 +190,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+ /**
+  * batadv_v_ogm_aggr_send() - flush & send aggregation queue
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @hard_iface: the interface with the aggregation queue to flush
+  *
+  * Aggregates all OGMv2 packets currently in the aggregation queue into a
+@@ -199,7 +200,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+  *
+  * Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
+  */
+-static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
++static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
++                                 struct batadv_hard_iface *hard_iface)
+ {
+       unsigned int aggr_len = hard_iface->bat_v.aggr_len;
+       struct sk_buff *skb_aggr;
+@@ -229,27 +231,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
+               consume_skb(skb);
+       }
+-      batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
++      batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ }
+ /**
+  * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to queue
+  * @hard_iface: the interface to queue the OGM on
+  */
+-static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
++static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
++                                   struct sk_buff *skb,
+                                    struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      if (hard_iface->soft_iface != bat_priv->soft_iface) {
++              kfree_skb(skb);
++              return;
++      }
+       if (!atomic_read(&bat_priv->aggregated_ogms)) {
+-              batadv_v_ogm_send_to_if(skb, hard_iface);
++              batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+               return;
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+-              batadv_v_ogm_aggr_send(hard_iface);
++              batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
+@@ -348,7 +355,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+                       break;
+               }
+-              batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
++              batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+               batadv_hardif_put(hard_iface);
+       }
+       rcu_read_unlock();
+@@ -388,12 +395,14 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
+ {
+       struct batadv_hard_iface_bat_v *batv;
+       struct batadv_hard_iface *hard_iface;
++      struct batadv_priv *bat_priv;
+       batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
+       hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
++      bat_priv = netdev_priv(hard_iface->soft_iface);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+-      batadv_v_ogm_aggr_send(hard_iface);
++      batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+       batadv_v_ogm_start_queue_timer(hard_iface);
+@@ -583,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
+                  if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
+                  ogm_forward->ttl, if_incoming->net_dev->name);
+-      batadv_v_ogm_queue_on_if(skb, if_outgoing);
++      batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+ out:
+       batadv_orig_ifinfo_put(orig_ifinfo);
+-- 
+2.53.0
+
diff --git a/queue-6.1/bpf-fix-a-few-selftest-failures-due-to-llvm18-change.patch b/queue-6.1/bpf-fix-a-few-selftest-failures-due-to-llvm18-change.patch
new file mode 100644 (file)
index 0000000..d8525fb
--- /dev/null
@@ -0,0 +1,89 @@
+From 1f78df8b5f9c9ee8cbd8fb72f79a3630a0938941 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:41:17 +0200
+Subject: bpf: Fix a few selftest failures due to llvm18 change
+
+From: Yonghong Song <yonghong.song@linux.dev>
+
+[ Upstream commit b16904fd9f01b580db357ef2b1cc9e86d89576c2 ]
+
+With latest upstream llvm18, the following test cases failed:
+
+  $ ./test_progs -j
+  #13/2    bpf_cookie/multi_kprobe_link_api:FAIL
+  #13/3    bpf_cookie/multi_kprobe_attach_api:FAIL
+  #13      bpf_cookie:FAIL
+  #77      fentry_fexit:FAIL
+  #78/1    fentry_test/fentry:FAIL
+  #78      fentry_test:FAIL
+  #82/1    fexit_test/fexit:FAIL
+  #82      fexit_test:FAIL
+  #112/1   kprobe_multi_test/skel_api:FAIL
+  #112/2   kprobe_multi_test/link_api_addrs:FAIL
+  [...]
+  #112     kprobe_multi_test:FAIL
+  #356/17  test_global_funcs/global_func17:FAIL
+  #356     test_global_funcs:FAIL
+
+Further analysis shows llvm upstream patch [1] is responsible for the above
+failures. For example, for function bpf_fentry_test7() in net/bpf/test_run.c,
+without [1], the asm code is:
+
+  0000000000000400 <bpf_fentry_test7>:
+     400: f3 0f 1e fa                   endbr64
+     404: e8 00 00 00 00                callq   0x409 <bpf_fentry_test7+0x9>
+     409: 48 89 f8                      movq    %rdi, %rax
+     40c: c3                            retq
+     40d: 0f 1f 00                      nopl    (%rax)
+
+... and with [1], the asm code is:
+
+  0000000000005d20 <bpf_fentry_test7.specialized.1>:
+    5d20: e8 00 00 00 00                callq   0x5d25 <bpf_fentry_test7.specialized.1+0x5>
+    5d25: c3                            retq
+
+... and <bpf_fentry_test7.specialized.1> is called instead of <bpf_fentry_test7>
+and this caused test failures for #13/#77 etc. except #356.
+
+For test case #356/17, with [1] (progs/test_global_func17.c)), the main prog
+looks like:
+
+  0000000000000000 <global_func17>:
+       0:       b4 00 00 00 2a 00 00 00 w0 = 0x2a
+       1:       95 00 00 00 00 00 00 00 exit
+
+... which passed verification while the test itself expects a verification
+failure.
+
+Let us add 'barrier_var' style asm code in both places to prevent function
+specialization which caused selftests failure.
+
+  [1] https://github.com/llvm/llvm-project/pull/72903
+
+Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Link: https://lore.kernel.org/bpf/20231127050342.1945270-1-yonghong.song@linux.dev
+[ Note: The change to test_run.c conflicted and was dropped. The related
+  tests are not failing anyway. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/bpf/progs/test_global_func17.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func17.c b/tools/testing/selftests/bpf/progs/test_global_func17.c
+index a32e11c7d933ee..5de44b09e8ec17 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func17.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func17.c
+@@ -5,6 +5,7 @@
+ __noinline int foo(int *p)
+ {
++      barrier_var(p);
+       return p ? (*p = 42) : 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/drm-dp-add-edp-1.5-bit-definition.patch b/queue-6.1/drm-dp-add-edp-1.5-bit-definition.patch
new file mode 100644 (file)
index 0000000..1a6f8fe
--- /dev/null
@@ -0,0 +1,41 @@
+From 4fc18faf30e836829a43e90153d8dd7ee4d5a561 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:41:56 +0300
+Subject: drm/dp: Add eDP 1.5 bit definition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Suraj Kandpal <suraj.kandpal@intel.com>
+
+commit 5dfc37a6b77bf6beedbd30d70184b54e1a08ccac upstream.
+
+Add the eDP revision bit value for 1.5.
+
+Spec: eDPv1.5 Table 16-5
+Signed-off-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Reviewed-by: Arun R Murthy <arun.r.murthy@intel.com>
+Tested-by: Ben Kao <ben.kao@intel.com>
+Acked-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250206063253.2827017-2-suraj.kandpal@intel.com
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/drm/display/drm_dp.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h
+index b235d6833e27d9..7d3700490b68f0 100644
+--- a/include/drm/display/drm_dp.h
++++ b/include/drm/display/drm_dp.h
+@@ -955,6 +955,7 @@
+ # define DP_EDP_14                        0x03
+ # define DP_EDP_14a                         0x04    /* eDP 1.4a */
+ # define DP_EDP_14b                         0x05    /* eDP 1.4b */
++# define DP_EDP_15                        0x06    /* eDP 1.5 */
+ #define DP_EDP_GENERAL_CAP_1              0x701
+ # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP         (1 << 0)
+-- 
+2.53.0
+
diff --git a/queue-6.1/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-6.1/drm-i915-psr-add-defininitions-for-intel_wa_register.patch
new file mode 100644 (file)
index 0000000..989e6a6
--- /dev/null
@@ -0,0 +1,73 @@
+From de285df3961bfcee727a1db556a0809c1eda0703 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:41:58 +0300
+Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD
+ register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream.
+
+EDP specification says:
+
+"If either VSC SDP is unable to be transmitted 100 ns before the SU region,
+the Source device may optionally transmit the VSC SDP during the prior
+video scan line’s HBlank period There is a Intel specific drm dp register
+currently containing bits related how TCON can support PSR2 with SDP on
+prior line."
+
+Unfortunately many panels are having problems in implementing this. So
+there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to
+figure out if this is properly implemented on a panel or if panel doesn't
+require that 100 ns delay before the SU region. Here are the definitions in
+this custom DPCD address:
+
+0 = Panel doesn't support SDP on prior line
+1 = Panel supports SDP on prior line
+2 = Panel doesn't have 100ns requirement
+3 = Reserved
+
+Add definitions for this new register and it's values into new header
+intel_dpcd.h.
+
+v2: add INTEL_DPCD_ prefix to definitions
+
+Bspec: 74741
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com
+(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+ create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h
+
+diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h
+new file mode 100644
+index 00000000000000..4aea5326f2ed48
+--- /dev/null
++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: MIT */
++/*
++ * Copyright © 2026 Intel Corporation
++ */
++
++#ifndef __INTEL_DPCD_H__
++#define __INTEL_DPCD_H__
++
++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS                                     0x3f0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK        REG_GENMASK(1, 0)
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1                  0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE           1
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE                2
++
++#endif /* __INTEL_DPCD_H__ */
+-- 
+2.53.0
+
diff --git a/queue-6.1/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-6.1/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
new file mode 100644 (file)
index 0000000..e05b9c7
--- /dev/null
@@ -0,0 +1,77 @@
+From 58a274e4fbb6d4e252a1ae8d2778587dcf3df8f5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:41:55 +0300
+Subject: drm/i915/psr: Apply Intel DPCD workaround when SDP on prior line used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit 4703049f768fc1c1caac754134118bee1a3af189 upstream.
+
+There is Intel specific workaround DPCD address containing workaround for
+case where SDP is on prior line. Apply this workaround according to values
+in the offset.
+
+Fixes: 61e887329e33 ("drm/i915/xelpd: Handle PSR2 SDP indication in the prior scanline")
+Cc: <stable@vger.kernel.org> # v5.15+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com
+(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_psr.c | 27 +++++++++++++++++++++++-
+ 1 file changed, 26 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 01fadf300ff679..3c5553b21fa894 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -818,6 +818,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp,
+       return true;
+ }
++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp,
++                                       struct intel_crtc_state *crtc_state)
++{
++      u8 early_scanline_support = intel_dp->intel_wa_dpcd &
++              INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK;
++
++      if (intel_dp->edp_dpcd[0] >= DP_EDP_15)
++              return true;
++
++      switch (early_scanline_support) {
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return false;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE:
++              return true;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return true;
++      default:
++              MISSING_CASE(early_scanline_support);
++              return false;
++      }
++}
++
+ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp,
+                                                       struct intel_crtc_state *crtc_state)
+ {
+@@ -839,7 +863,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d
+               return false;
+       crtc_state->req_psr2_sdp_prior_scanline = true;
+-      return true;
++
++      return apply_scanline_indication_wa(intel_dp, crtc_state);
+ }
+ static bool _compute_psr2_wake_times(struct intel_dp *intel_dp,
+-- 
+2.53.0
+
diff --git a/queue-6.1/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-6.1/drm-i915-psr-read-intel-dpcd-workaround-register.patch
new file mode 100644 (file)
index 0000000..f1ac52c
--- /dev/null
@@ -0,0 +1,67 @@
+From 67a94aeaa3005fbf324321fe756ad27611534263 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 11:41:57 +0300
+Subject: drm/i915/psr: Read Intel DPCD workaround register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream.
+
+Read Intel DPCD workaround register and store it into
+intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains
+only PSR workaround for PSR2 SDP on prior scanline implementation.
+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com
+(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_display_types.h | 1 +
+ drivers/gpu/drm/i915/display/intel_psr.c           | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
+index a8bf91a21cb246..a26c082bdc3289 100644
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -1637,6 +1637,7 @@ struct intel_dp {
+       u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE];
+       u8 fec_capable;
+       u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE];
++      u8 intel_wa_dpcd;
+       /* source rates */
+       int num_source_rates;
+       const int *source_rates;
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index a465b192931085..01fadf300ff679 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -31,6 +31,7 @@
+ #include "intel_crtc.h"
+ #include "intel_de.h"
+ #include "intel_display_types.h"
++#include "intel_dpcd.h"
+ #include "intel_dp_aux.h"
+ #include "intel_hdmi.h"
+ #include "intel_psr.h"
+@@ -388,6 +389,12 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp)
+                       intel_dp_get_su_granularity(intel_dp);
+               }
+       }
++
++      if (intel_dp->psr.sink_psr2_support)
++              drm_dp_dpcd_read(&intel_dp->aux,
++                               INTEL_DPCD_INTEL_WA_REGISTER_CAPS,
++                               &intel_dp->intel_wa_dpcd,
++                               sizeof(intel_dp->intel_wa_dpcd));
+ }
+ static void intel_psr_enable_sink(struct intel_dp *intel_dp)
+-- 
+2.53.0
+
diff --git a/queue-6.1/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch b/queue-6.1/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch
new file mode 100644 (file)
index 0000000..79b52ae
--- /dev/null
@@ -0,0 +1,67 @@
+From 0492d9ae159d6151c3e09cd2d80ebf2f33eaa129 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:02:30 +0800
+Subject: mm/page_alloc: clear page->private in free_pages_prepare()
+
+From: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
+
+[ Upstream commit ac1ea219590c09572ed5992dc233bbf7bb70fef9 ]
+
+Several subsystems (slub, shmem, ttm, etc.) use page->private but don't
+clear it before freeing pages.  When these pages are later allocated as
+high-order pages and split via split_page(), tail pages retain stale
+page->private values.
+
+This causes a use-after-free in the swap subsystem.  The swap code uses
+page->private to track swap count continuations, assuming freshly
+allocated pages have page->private == 0.  When stale values are present,
+swap_count_continued() incorrectly assumes the continuation list is valid
+and iterates over uninitialized page->lru containing LIST_POISON values,
+causing a crash:
+
+  KASAN: maybe wild-memory-access in range [0xdead000000000100-0xdead000000000107]
+  RIP: 0010:__do_sys_swapoff+0x1151/0x1860
+
+Fix this by clearing page->private in free_pages_prepare(), ensuring all
+freed pages have clean state regardless of previous use.
+
+Link: https://lkml.kernel.org/r/20260207173615.146159-1-mikhail.v.gavrilov@gmail.com
+Fixes: 3b8000ae185c ("mm/vmalloc: huge vmalloc backing pages should be split rather than compound")
+Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
+Suggested-by: Zi Yan <ziy@nvidia.com>
+Acked-by: Zi Yan <ziy@nvidia.com>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: Brendan Jackman <jackmanb@google.com>
+Cc: Chris Li <chrisl@kernel.org>
+Cc: Hugh Dickins <hughd@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Kairui Song <ryncsn@gmail.com>
+Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Nicholas Piggin <npiggin@gmail.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+[backport: context only]
+Signed-off-by: Li Wang <li.wang@windriver.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/page_alloc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/mm/page_alloc.c b/mm/page_alloc.c
+index 4c17b9ceff8099..93c63b5fad2889 100644
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -1483,6 +1483,7 @@ static __always_inline bool free_pages_prepare(struct page *page,
+       page_cpupid_reset_last(page);
+       page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP;
++      page->private = 0;
+       reset_page_owner(page, order);
+       page_table_check_free(page, order);
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-packet-convert-po-has_vnet_hdr-to-an-atomic-flag.patch b/queue-6.1/net-packet-convert-po-has_vnet_hdr-to-an-atomic-flag.patch
new file mode 100644 (file)
index 0000000..c9cc328
--- /dev/null
@@ -0,0 +1,143 @@
+From 180d118cc965bf89a41abdc5925f9a749af9d486 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Mar 2023 01:10:12 +0000
+Subject: net/packet: convert po->has_vnet_hdr to an atomic flag
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 50d935eafee292fc432d5ac8c8715a6492961abc ]
+
+po->has_vnet_hdr can be read locklessly.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 2c054e17d9d4 ("net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/packet/af_packet.c | 19 ++++++++++---------
+ net/packet/diag.c      |  2 +-
+ net/packet/internal.h  |  2 +-
+ 3 files changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
+index 490bfec158035e..50d9618d85f3c7 100644
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -2357,7 +2357,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
+               netoff = TPACKET_ALIGN(po->tp_hdrlen +
+                                      (maclen < 16 ? 16 : maclen)) +
+                                      po->tp_reserve;
+-              if (po->has_vnet_hdr) {
++              if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) {
+                       netoff += sizeof(struct virtio_net_hdr);
+                       do_vnet = true;
+               }
+@@ -2831,7 +2831,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
+       size_max = po->tx_ring.frame_size
+               - (po->tp_hdrlen - sizeof(struct sockaddr_ll));
+-      if ((size_max > dev->mtu + reserve + VLAN_HLEN) && !po->has_vnet_hdr)
++      if ((size_max > dev->mtu + reserve + VLAN_HLEN) &&
++          !packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR))
+               size_max = dev->mtu + reserve + VLAN_HLEN;
+       timeo = sock_sndtimeo(&po->sk, msg->msg_flags & MSG_DONTWAIT);
+@@ -2866,7 +2867,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
+               status = TP_STATUS_SEND_REQUEST;
+               hlen = LL_RESERVED_SPACE(dev);
+               tlen = dev->needed_tailroom;
+-              if (po->has_vnet_hdr) {
++              if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) {
+                       vnet_hdr = data;
+                       data += sizeof(*vnet_hdr);
+                       tp_len -= sizeof(*vnet_hdr);
+@@ -2894,7 +2895,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
+                                         addr, hlen, copylen, &sockc);
+               if (likely(tp_len >= 0) &&
+                   tp_len > dev->mtu + reserve &&
+-                  !po->has_vnet_hdr &&
++                  !packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR) &&
+                   !packet_extra_vlan_len_allowed(dev, skb))
+                       tp_len = -EMSGSIZE;
+@@ -2913,7 +2914,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
+                       }
+               }
+-              if (po->has_vnet_hdr) {
++              if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) {
+                       if (virtio_net_hdr_to_skb(skb, vnet_hdr, vio_le())) {
+                               tp_len = -EINVAL;
+                               goto tpacket_error;
+@@ -3041,7 +3042,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
+       if (sock->type == SOCK_RAW)
+               reserve = dev->hard_header_len;
+-      if (po->has_vnet_hdr) {
++      if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) {
+               err = packet_snd_vnet_parse(msg, &len, &vnet_hdr);
+               if (err)
+                       goto out_unlock;
+@@ -3506,7 +3507,7 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+       packet_rcv_try_clear_pressure(pkt_sk(sk));
+-      if (pkt_sk(sk)->has_vnet_hdr) {
++      if (packet_sock_flag(pkt_sk(sk), PACKET_SOCK_HAS_VNET_HDR)) {
+               err = packet_rcv_vnet(msg, skb, &len);
+               if (err)
+                       goto out_free;
+@@ -4002,7 +4003,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval,
+               if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
+                       ret = -EBUSY;
+               } else {
+-                      po->has_vnet_hdr = !!val;
++                      packet_sock_flag_set(po, PACKET_SOCK_HAS_VNET_HDR, val);
+                       ret = 0;
+               }
+               release_sock(sk);
+@@ -4136,7 +4137,7 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
+               val = packet_sock_flag(po, PACKET_SOCK_ORIGDEV);
+               break;
+       case PACKET_VNET_HDR:
+-              val = po->has_vnet_hdr;
++              val = packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR);
+               break;
+       case PACKET_VERSION:
+               val = po->tp_version;
+diff --git a/net/packet/diag.c b/net/packet/diag.c
+index 677d442cd930fe..a3bd91dba43945 100644
+--- a/net/packet/diag.c
++++ b/net/packet/diag.c
+@@ -27,7 +27,7 @@ static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb)
+               pinfo.pdi_flags |= PDI_AUXDATA;
+       if (packet_sock_flag(po, PACKET_SOCK_ORIGDEV))
+               pinfo.pdi_flags |= PDI_ORIGDEV;
+-      if (po->has_vnet_hdr)
++      if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR))
+               pinfo.pdi_flags |= PDI_VNETHDR;
+       if (packet_sock_flag(po, PACKET_SOCK_TP_LOSS))
+               pinfo.pdi_flags |= PDI_LOSS;
+diff --git a/net/packet/internal.h b/net/packet/internal.h
+index 82a997824e5733..0956e4a934492d 100644
+--- a/net/packet/internal.h
++++ b/net/packet/internal.h
+@@ -118,7 +118,6 @@ struct packet_sock {
+       struct mutex            pg_vec_lock;
+       unsigned long           flags;
+       unsigned int            running;        /* bind_lock must be held */
+-      unsigned int            has_vnet_hdr:1; /* writer must hold sock lock */
+       int                     pressure;
+       int                     ifindex;        /* bound device         */
+       __be16                  num;
+@@ -146,6 +145,7 @@ enum packet_sock_flags {
+       PACKET_SOCK_AUXDATA,
+       PACKET_SOCK_TX_HAS_OFF,
+       PACKET_SOCK_TP_LOSS,
++      PACKET_SOCK_HAS_VNET_HDR,
+ };
+ static inline void packet_sock_flag_set(struct packet_sock *po,
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-packet-convert-po-running-to-an-atomic-flag.patch b/queue-6.1/net-packet-convert-po-running-to-an-atomic-flag.patch
new file mode 100644 (file)
index 0000000..fef9779
--- /dev/null
@@ -0,0 +1,151 @@
+From 13efc9420cf09d14d84d4fa00f016fbc8fd92933 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Mar 2023 01:10:13 +0000
+Subject: net/packet: convert po->running to an atomic flag
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 61edf479818e63978cabd243b82ca80f8948a313 ]
+
+Instead of consuming 32 bits for po->running, use
+one available bit in po->flags.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 2c054e17d9d4 ("net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/packet/af_packet.c | 20 ++++++++++----------
+ net/packet/diag.c      |  2 +-
+ net/packet/internal.h  |  2 +-
+ 3 files changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
+index 50d9618d85f3c7..36347814ec7ceb 100644
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -340,14 +340,14 @@ static void __register_prot_hook(struct sock *sk)
+ {
+       struct packet_sock *po = pkt_sk(sk);
+-      if (!po->running) {
++      if (!packet_sock_flag(po, PACKET_SOCK_RUNNING)) {
+               if (po->fanout)
+                       __fanout_link(sk, po);
+               else
+                       dev_add_pack(&po->prot_hook);
+               sock_hold(sk);
+-              po->running = 1;
++              packet_sock_flag_set(po, PACKET_SOCK_RUNNING, 1);
+       }
+ }
+@@ -369,7 +369,7 @@ static void __unregister_prot_hook(struct sock *sk, bool sync)
+       lockdep_assert_held_once(&po->bind_lock);
+-      po->running = 0;
++      packet_sock_flag_set(po, PACKET_SOCK_RUNNING, 0);
+       if (po->fanout)
+               __fanout_unlink(sk, po);
+@@ -389,7 +389,7 @@ static void unregister_prot_hook(struct sock *sk, bool sync)
+ {
+       struct packet_sock *po = pkt_sk(sk);
+-      if (po->running)
++      if (packet_sock_flag(po, PACKET_SOCK_RUNNING))
+               __unregister_prot_hook(sk, sync);
+ }
+@@ -1834,7 +1834,7 @@ static int fanout_add(struct sock *sk, struct fanout_args *args)
+       err = -EINVAL;
+       spin_lock(&po->bind_lock);
+-      if (po->running &&
++      if (packet_sock_flag(po, PACKET_SOCK_RUNNING) &&
+           match->type == type &&
+           match->prot_hook.type == po->prot_hook.type &&
+           match->prot_hook.dev == po->prot_hook.dev) {
+@@ -3277,7 +3277,7 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex,
+       if (need_rehook) {
+               dev_hold(dev);
+-              if (po->running) {
++              if (packet_sock_flag(po, PACKET_SOCK_RUNNING)) {
+                       rcu_read_unlock();
+                       /* prevents packet_notifier() from calling
+                        * register_prot_hook()
+@@ -3290,7 +3290,7 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex,
+                                                                dev->ifindex);
+               }
+-              BUG_ON(po->running);
++              BUG_ON(packet_sock_flag(po, PACKET_SOCK_RUNNING));
+               WRITE_ONCE(po->num, proto);
+               po->prot_hook.type = proto;
+@@ -4230,7 +4230,7 @@ static int packet_notifier(struct notifier_block *this,
+               case NETDEV_DOWN:
+                       if (dev->ifindex == po->ifindex) {
+                               spin_lock(&po->bind_lock);
+-                              if (po->running) {
++                              if (packet_sock_flag(po, PACKET_SOCK_RUNNING)) {
+                                       __unregister_prot_hook(sk, false);
+                                       sk->sk_err = ENETDOWN;
+                                       if (!sock_flag(sk, SOCK_DEAD))
+@@ -4541,7 +4541,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
+       /* Detach socket from network */
+       spin_lock(&po->bind_lock);
+-      was_running = po->running;
++      was_running = packet_sock_flag(po, PACKET_SOCK_RUNNING);
+       num = po->num;
+       WRITE_ONCE(po->num, 0);
+       if (was_running)
+@@ -4752,7 +4752,7 @@ static int packet_seq_show(struct seq_file *seq, void *v)
+                          s->sk_type,
+                          ntohs(READ_ONCE(po->num)),
+                          READ_ONCE(po->ifindex),
+-                         po->running,
++                         packet_sock_flag(po, PACKET_SOCK_RUNNING),
+                          atomic_read(&s->sk_rmem_alloc),
+                          from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)),
+                          sock_i_ino(s));
+diff --git a/net/packet/diag.c b/net/packet/diag.c
+index a3bd91dba43945..cd30cc619c6b45 100644
+--- a/net/packet/diag.c
++++ b/net/packet/diag.c
+@@ -21,7 +21,7 @@ static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb)
+       pinfo.pdi_tstamp = po->tp_tstamp;
+       pinfo.pdi_flags = 0;
+-      if (po->running)
++      if (packet_sock_flag(po, PACKET_SOCK_RUNNING))
+               pinfo.pdi_flags |= PDI_RUNNING;
+       if (packet_sock_flag(po, PACKET_SOCK_AUXDATA))
+               pinfo.pdi_flags |= PDI_AUXDATA;
+diff --git a/net/packet/internal.h b/net/packet/internal.h
+index 0956e4a934492d..9e50bf06131f29 100644
+--- a/net/packet/internal.h
++++ b/net/packet/internal.h
+@@ -117,7 +117,6 @@ struct packet_sock {
+       spinlock_t              bind_lock;
+       struct mutex            pg_vec_lock;
+       unsigned long           flags;
+-      unsigned int            running;        /* bind_lock must be held */
+       int                     pressure;
+       int                     ifindex;        /* bound device         */
+       __be16                  num;
+@@ -146,6 +145,7 @@ enum packet_sock_flags {
+       PACKET_SOCK_TX_HAS_OFF,
+       PACKET_SOCK_TP_LOSS,
+       PACKET_SOCK_HAS_VNET_HDR,
++      PACKET_SOCK_RUNNING,
+ };
+ static inline void packet_sock_flag_set(struct packet_sock *po,
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-packet-convert-po-tp_loss-to-an-atomic-flag.patch b/queue-6.1/net-packet-convert-po-tp_loss-to-an-atomic-flag.patch
new file mode 100644 (file)
index 0000000..f8f2494
--- /dev/null
@@ -0,0 +1,92 @@
+From 77ae60fdc1c3953fa714875b4114a83a77382503 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Mar 2023 01:10:11 +0000
+Subject: net/packet: convert po->tp_loss to an atomic flag
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 164bddace2e03f6005e650cb88f101a66ebdc05a ]
+
+tp_loss can be read locklessly.
+
+Convert it to an atomic flag to avoid races.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 2c054e17d9d4 ("net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/packet/af_packet.c | 6 +++---
+ net/packet/diag.c      | 2 +-
+ net/packet/internal.h  | 4 ++--
+ 3 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
+index 1ceb8f765114b3..490bfec158035e 100644
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -2900,7 +2900,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
+               if (unlikely(tp_len < 0)) {
+ tpacket_error:
+-                      if (po->tp_loss) {
++                      if (packet_sock_flag(po, PACKET_SOCK_TP_LOSS)) {
+                               __packet_set_status(po, ph,
+                                               TP_STATUS_AVAILABLE);
+                               packet_increment_head(&po->tx_ring);
+@@ -3957,7 +3957,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval,
+               if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
+                       ret = -EBUSY;
+               } else {
+-                      po->tp_loss = !!val;
++                      packet_sock_flag_set(po, PACKET_SOCK_TP_LOSS, val);
+                       ret = 0;
+               }
+               release_sock(sk);
+@@ -4166,7 +4166,7 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
+               val = po->tp_reserve;
+               break;
+       case PACKET_LOSS:
+-              val = po->tp_loss;
++              val = packet_sock_flag(po, PACKET_SOCK_TP_LOSS);
+               break;
+       case PACKET_TIMESTAMP:
+               val = po->tp_tstamp;
+diff --git a/net/packet/diag.c b/net/packet/diag.c
+index 057ee37bd0766c..677d442cd930fe 100644
+--- a/net/packet/diag.c
++++ b/net/packet/diag.c
+@@ -29,7 +29,7 @@ static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb)
+               pinfo.pdi_flags |= PDI_ORIGDEV;
+       if (po->has_vnet_hdr)
+               pinfo.pdi_flags |= PDI_VNETHDR;
+-      if (po->tp_loss)
++      if (packet_sock_flag(po, PACKET_SOCK_TP_LOSS))
+               pinfo.pdi_flags |= PDI_LOSS;
+       return nla_put(nlskb, PACKET_DIAG_INFO, sizeof(pinfo), &pinfo);
+diff --git a/net/packet/internal.h b/net/packet/internal.h
+index 31bac09a687233..82a997824e5733 100644
+--- a/net/packet/internal.h
++++ b/net/packet/internal.h
+@@ -118,8 +118,7 @@ struct packet_sock {
+       struct mutex            pg_vec_lock;
+       unsigned long           flags;
+       unsigned int            running;        /* bind_lock must be held */
+-      unsigned int            has_vnet_hdr:1, /* writer must hold sock lock */
+-                              tp_loss:1;
++      unsigned int            has_vnet_hdr:1; /* writer must hold sock lock */
+       int                     pressure;
+       int                     ifindex;        /* bound device         */
+       __be16                  num;
+@@ -146,6 +145,7 @@ enum packet_sock_flags {
+       PACKET_SOCK_ORIGDEV,
+       PACKET_SOCK_AUXDATA,
+       PACKET_SOCK_TX_HAS_OFF,
++      PACKET_SOCK_TP_LOSS,
+ };
+ static inline void packet_sock_flag_set(struct packet_sock *po,
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-packet-convert-po-tp_tx_has_off-to-an-atomic-fla.patch b/queue-6.1/net-packet-convert-po-tp_tx_has_off-to-an-atomic-fla.patch
new file mode 100644 (file)
index 0000000..1fbbb73
--- /dev/null
@@ -0,0 +1,77 @@
+From 7f5cb358e765ae78093a336a4e03a1c85474e9db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Mar 2023 01:10:10 +0000
+Subject: net/packet: convert po->tp_tx_has_off to an atomic flag
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit 7438344660fa55b33b8234c1797c886eb73667a7 ]
+
+This is to use existing space in po->flags, and reclaim
+the storage used by the non atomic bit fields.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 2c054e17d9d4 ("net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/packet/af_packet.c | 6 +++---
+ net/packet/internal.h  | 4 ++--
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
+index 502d2f6de18a29..1ceb8f765114b3 100644
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -2723,7 +2723,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
+               return -EMSGSIZE;
+       }
+-      if (unlikely(po->tp_tx_has_off)) {
++      if (unlikely(packet_sock_flag(po, PACKET_SOCK_TX_HAS_OFF))) {
+               int off_min, off_max;
+               off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll);
+@@ -4064,7 +4064,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval,
+               lock_sock(sk);
+               if (!po->rx_ring.pg_vec && !po->tx_ring.pg_vec)
+-                      po->tp_tx_has_off = !!val;
++                      packet_sock_flag_set(po, PACKET_SOCK_TX_HAS_OFF, val);
+               release_sock(sk);
+               return 0;
+@@ -4191,7 +4191,7 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
+               lv = sizeof(rstats);
+               break;
+       case PACKET_TX_HAS_OFF:
+-              val = po->tp_tx_has_off;
++              val = packet_sock_flag(po, PACKET_SOCK_TX_HAS_OFF);
+               break;
+       case PACKET_QDISC_BYPASS:
+               val = packet_use_direct_xmit(po);
+diff --git a/net/packet/internal.h b/net/packet/internal.h
+index b2edfe6fc8e770..31bac09a687233 100644
+--- a/net/packet/internal.h
++++ b/net/packet/internal.h
+@@ -119,8 +119,7 @@ struct packet_sock {
+       unsigned long           flags;
+       unsigned int            running;        /* bind_lock must be held */
+       unsigned int            has_vnet_hdr:1, /* writer must hold sock lock */
+-                              tp_loss:1,
+-                              tp_tx_has_off:1;
++                              tp_loss:1;
+       int                     pressure;
+       int                     ifindex;        /* bound device         */
+       __be16                  num;
+@@ -146,6 +145,7 @@ static inline struct packet_sock *pkt_sk(struct sock *sk)
+ enum packet_sock_flags {
+       PACKET_SOCK_ORIGDEV,
+       PACKET_SOCK_AUXDATA,
++      PACKET_SOCK_TX_HAS_OFF,
+ };
+ static inline void packet_sock_flag_set(struct packet_sock *po,
+-- 
+2.53.0
+
diff --git a/queue-6.1/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpa.patch b/queue-6.1/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpa.patch
new file mode 100644 (file)
index 0000000..edf5cce
--- /dev/null
@@ -0,0 +1,96 @@
+From d224d8234c6d43a6c634e3671b057f508601244f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Apr 2026 19:20:06 +0800
+Subject: net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()
+
+From: Bingquan Chen <patzilla007@gmail.com>
+
+[ Upstream commit 2c054e17d9d41f1020376806c7f750834ced4dc5 ]
+
+In tpacket_snd(), when PACKET_VNET_HDR is enabled, vnet_hdr points
+directly into the mmap'd TX ring buffer shared with userspace. The
+kernel validates the header via __packet_snd_vnet_parse() but then
+re-reads all fields later in virtio_net_hdr_to_skb(). A concurrent
+userspace thread can modify the vnet_hdr fields between validation
+and use, bypassing all safety checks.
+
+The non-TPACKET path (packet_snd()) already correctly copies vnet_hdr
+to a stack-local variable. All other vnet_hdr consumers in the kernel
+(tun.c, tap.c, virtio_net.c) also use stack copies. The TPACKET TX
+path is the only caller of virtio_net_hdr_to_skb() that reads directly
+from user-controlled shared memory.
+
+Fix this by copying vnet_hdr from the mmap'd ring buffer to a
+stack-local variable before validation and use, consistent with the
+approach used in packet_snd() and all other callers.
+
+Fixes: 1d036d25e560 ("packet: tpacket_snd gso and checksum offload")
+Signed-off-by: Bingquan Chen <patzilla007@gmail.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/20260418112006.78823-1-patzilla007@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/packet/af_packet.c | 25 +++++++++++++++----------
+ 1 file changed, 15 insertions(+), 10 deletions(-)
+
+diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
+index 36347814ec7ceb..f3850784d66404 100644
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -2767,7 +2767,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
+ {
+       struct sk_buff *skb = NULL;
+       struct net_device *dev;
+-      struct virtio_net_hdr *vnet_hdr = NULL;
++      struct virtio_net_hdr vnet_hdr;
++      bool has_vnet_hdr = false;
+       struct sockcm_cookie sockc;
+       __be16 proto;
+       int err, reserve = 0;
+@@ -2868,16 +2869,20 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
+               hlen = LL_RESERVED_SPACE(dev);
+               tlen = dev->needed_tailroom;
+               if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) {
+-                      vnet_hdr = data;
+-                      data += sizeof(*vnet_hdr);
+-                      tp_len -= sizeof(*vnet_hdr);
+-                      if (tp_len < 0 ||
+-                          __packet_snd_vnet_parse(vnet_hdr, tp_len)) {
++                      data += sizeof(vnet_hdr);
++                      tp_len -= sizeof(vnet_hdr);
++                      if (tp_len < 0) {
++                              tp_len = -EINVAL;
++                              goto tpacket_error;
++                      }
++                      memcpy(&vnet_hdr, data - sizeof(vnet_hdr), sizeof(vnet_hdr));
++                      if (__packet_snd_vnet_parse(&vnet_hdr, tp_len)) {
+                               tp_len = -EINVAL;
+                               goto tpacket_error;
+                       }
+                       copylen = __virtio16_to_cpu(vio_le(),
+-                                                  vnet_hdr->hdr_len);
++                                                  vnet_hdr.hdr_len);
++                      has_vnet_hdr = true;
+               }
+               copylen = max_t(int, copylen, dev->hard_header_len);
+               skb = sock_alloc_send_skb(&po->sk,
+@@ -2914,12 +2919,12 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
+                       }
+               }
+-              if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) {
+-                      if (virtio_net_hdr_to_skb(skb, vnet_hdr, vio_le())) {
++              if (has_vnet_hdr) {
++                      if (virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le())) {
+                               tp_len = -EINVAL;
+                               goto tpacket_error;
+                       }
+-                      virtio_net_hdr_set_proto(skb, vnet_hdr);
++                      virtio_net_hdr_set_proto(skb, &vnet_hdr);
+               }
+               skb->destructor = tpacket_destruct_skb;
+-- 
+2.53.0
+
diff --git a/queue-6.1/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-25226 b/queue-6.1/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-25226
new file mode 100644 (file)
index 0000000..4878f90
--- /dev/null
@@ -0,0 +1,68 @@
+From acb45424624dbd089eb23e89603533941407cff5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:13:39 +0200
+Subject: RDMA/rxe: Fix double free in rxe_srq_from_init
+
+From: Jiasheng Jiang <jiashengjiangcool@gmail.com>
+
+commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream.
+
+In rxe_srq_from_init(), the queue pointer 'q' is assigned to
+'srq->rq.queue' before copying the SRQ number to user space.
+If copy_to_user() fails, the function calls rxe_queue_cleanup()
+to free the queue, but leaves the now-invalid pointer in
+'srq->rq.queue'.
+
+The caller of rxe_srq_from_init() (rxe_create_srq) eventually
+calls rxe_srq_cleanup() upon receiving the error, which triggers
+a second rxe_queue_cleanup() on the same memory, leading to a
+double free.
+
+The call trace looks like this:
+   kmem_cache_free+0x.../0x...
+   rxe_queue_cleanup+0x1a/0x30 [rdma_rxe]
+   rxe_srq_cleanup+0x42/0x60 [rdma_rxe]
+   rxe_elem_release+0x31/0x70 [rdma_rxe]
+   rxe_create_srq+0x12b/0x1a0 [rdma_rxe]
+   ib_create_srq_user+0x9a/0x150 [ib_core]
+
+Fix this by moving 'srq->rq.queue = q' after copy_to_user.
+
+Fixes: aae0484e15f0 ("IB/rxe: avoid srq memory leak")
+Signed-off-by: Jiasheng Jiang <jiashengjiangcool@gmail.com>
+Link: https://patch.msgid.link/20260112015412.29458-1-jiashengjiangcool@gmail.com
+Reviewed-by: Zhu Yanjun <yanjun.Zhu@linux.dev>
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+[bwh: Backported to 6.1: There was no assignment to init->attr.max_wr
+ here; don't add it]
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/infiniband/sw/rxe/rxe_srq.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
+index 02b39498c370d2..038a9cd55413e0 100644
+--- a/drivers/infiniband/sw/rxe/rxe_srq.c
++++ b/drivers/infiniband/sw/rxe/rxe_srq.c
+@@ -69,8 +69,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               return -ENOMEM;
+       }
+-      srq->rq.queue = q;
+-
+       err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf,
+                          q->buf_size, &q->ip);
+       if (err) {
+@@ -87,6 +85,8 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               }
+       }
++      srq->rq.queue = q;
++
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch b/queue-6.1/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch
new file mode 100644 (file)
index 0000000..2a73135
--- /dev/null
@@ -0,0 +1,35 @@
+From a3c06ee57522a4446b12cdab414e52d175fad96a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:13:17 +0200
+Subject: Revert "RDMA/rxe: Fix double free in rxe_srq_from_init"
+
+From: Ben Hutchings <benh@debian.org>
+
+This reverts commit d286f0d4e3ad3caf5f0e673cdad7bf89bf37d947, which
+was commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream.  The
+backported version did not move but duplicated the problematic
+assignment, so it did not fix the bug.  A proper backport will follow.
+
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/infiniband/sw/rxe/rxe_srq.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
+index 115ff5428f6cfb..02b39498c370d2 100644
+--- a/drivers/infiniband/sw/rxe/rxe_srq.c
++++ b/drivers/infiniband/sw/rxe/rxe_srq.c
+@@ -87,9 +87,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
+               }
+       }
+-      srq->rq.queue = q;
+-      init->attr.max_wr = srq->rq.max_wr;
+-
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/revert-selftests-bpf-add-a-cgroup-prog-bpf_get_ns_cu.patch b/queue-6.1/revert-selftests-bpf-add-a-cgroup-prog-bpf_get_ns_cu.patch
new file mode 100644 (file)
index 0000000..a4b9923
--- /dev/null
@@ -0,0 +1,146 @@
+From 7b922c862481a1e71cc830831d0d5cfe080a4e4f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:41:46 +0200
+Subject: Revert "selftests/bpf: Add a cgroup prog
+ bpf_get_ns_current_pid_tgid() test"
+
+From: Paul Chaignon <paul.chaignon@gmail.com>
+
+This reverts commit 4d8fb7ed7a55 ("selftests/bpf: Add a cgroup prog
+bpf_get_ns_current_pid_tgid() test").
+
+That commit should have never been backported to 6.1 because it
+introduces a test for a feature that isn't supported:
+bpf_get_ns_current_pid_tgid() cannot be called from cgroup BPF programs
+in 6.1.
+
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../bpf/prog_tests/ns_current_pid_tgid.c      | 73 -------------------
+ .../bpf/progs/test_ns_current_pid_tgid.c      |  7 --
+ 2 files changed, 80 deletions(-)
+
+diff --git a/tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c b/tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c
+index 2c57ceede095eb..a84c41862ff8c9 100644
+--- a/tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c
++++ b/tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c
+@@ -12,7 +12,6 @@
+ #include <sys/wait.h>
+ #include <sys/mount.h>
+ #include <fcntl.h>
+-#include "network_helpers.h"
+ #define STACK_SIZE (1024 * 1024)
+ static char child_stack[STACK_SIZE];
+@@ -75,50 +74,6 @@ static int test_current_pid_tgid_tp(void *args)
+       return ret;
+ }
+-static int test_current_pid_tgid_cgrp(void *args)
+-{
+-      struct test_ns_current_pid_tgid__bss *bss;
+-      struct test_ns_current_pid_tgid *skel;
+-      int server_fd = -1, ret = -1, err;
+-      int cgroup_fd = *(int *)args;
+-      pid_t tgid, pid;
+-
+-      skel = test_ns_current_pid_tgid__open();
+-      if (!ASSERT_OK_PTR(skel, "test_ns_current_pid_tgid__open"))
+-              return ret;
+-
+-      bpf_program__set_autoload(skel->progs.cgroup_bind4, true);
+-
+-      err = test_ns_current_pid_tgid__load(skel);
+-      if (!ASSERT_OK(err, "test_ns_current_pid_tgid__load"))
+-              goto cleanup;
+-
+-      bss = skel->bss;
+-      if (get_pid_tgid(&pid, &tgid, bss))
+-              goto cleanup;
+-
+-      skel->links.cgroup_bind4 = bpf_program__attach_cgroup(
+-              skel->progs.cgroup_bind4, cgroup_fd);
+-      if (!ASSERT_OK_PTR(skel->links.cgroup_bind4, "bpf_program__attach_cgroup"))
+-              goto cleanup;
+-
+-      server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
+-      if (!ASSERT_GE(server_fd, 0, "start_server"))
+-              goto cleanup;
+-
+-      if (!ASSERT_EQ(bss->user_pid, pid, "pid"))
+-              goto cleanup;
+-      if (!ASSERT_EQ(bss->user_tgid, tgid, "tgid"))
+-              goto cleanup;
+-      ret = 0;
+-
+-cleanup:
+-      if (server_fd >= 0)
+-              close(server_fd);
+-      test_ns_current_pid_tgid__destroy(skel);
+-      return ret;
+-}
+-
+ static void test_ns_current_pid_tgid_new_ns(int (*fn)(void *), void *arg)
+ {
+       int wstatus;
+@@ -140,25 +95,6 @@ static void test_ns_current_pid_tgid_new_ns(int (*fn)(void *), void *arg)
+               return;
+ }
+-static void test_in_netns(int (*fn)(void *), void *arg)
+-{
+-      struct nstoken *nstoken = NULL;
+-
+-      SYS(cleanup, "ip netns add ns_current_pid_tgid");
+-      SYS(cleanup, "ip -net ns_current_pid_tgid link set dev lo up");
+-
+-      nstoken = open_netns("ns_current_pid_tgid");
+-      if (!ASSERT_OK_PTR(nstoken, "open_netns"))
+-              goto cleanup;
+-
+-      test_ns_current_pid_tgid_new_ns(fn, arg);
+-
+-cleanup:
+-      if (nstoken)
+-              close_netns(nstoken);
+-      SYS_NOFAIL("ip netns del ns_current_pid_tgid");
+-}
+-
+ /* TODO: use a different tracepoint */
+ void serial_test_ns_current_pid_tgid(void)
+ {
+@@ -166,13 +102,4 @@ void serial_test_ns_current_pid_tgid(void)
+               test_current_pid_tgid_tp(NULL);
+       if (test__start_subtest("new_ns_tp"))
+               test_ns_current_pid_tgid_new_ns(test_current_pid_tgid_tp, NULL);
+-      if (test__start_subtest("new_ns_cgrp")) {
+-              int cgroup_fd = -1;
+-
+-              cgroup_fd = test__join_cgroup("/sock_addr");
+-              if (ASSERT_GE(cgroup_fd, 0, "join_cgroup")) {
+-                      test_in_netns(test_current_pid_tgid_cgrp, &cgroup_fd);
+-                      close(cgroup_fd);
+-              }
+-      }
+ }
+diff --git a/tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c b/tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c
+index d0010e698f6688..aa3ec7ca16d9b6 100644
+--- a/tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c
++++ b/tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c
+@@ -28,11 +28,4 @@ int tp_handler(const void *ctx)
+       return 0;
+ }
+-SEC("?cgroup/bind4")
+-int cgroup_bind4(struct bpf_sock_addr *ctx)
+-{
+-      get_pid_tgid();
+-      return 1;
+-}
+-
+ char _license[] SEC("license") = "GPL";
+-- 
+2.53.0
+
diff --git a/queue-6.1/revert-selftests-bpf-add-tests-for-_opts-variants-of.patch b/queue-6.1/revert-selftests-bpf-add-tests-for-_opts-variants-of.patch
new file mode 100644 (file)
index 0000000..0bfbd63
--- /dev/null
@@ -0,0 +1,173 @@
+From 5cebc9d33436dcf0e359610bba98f970eca386cf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:40:57 +0200
+Subject: Revert "selftests/bpf: Add tests for _opts variants of
+ bpf_*_get_fd_by_id()"
+
+From: Paul Chaignon <paul.chaignon@gmail.com>
+
+This reverts commit 45108a7b4866 ("selftests/bpf: Add tests for _opts
+variants of bpf_*_get_fd_by_id()"). As explained in the previous patch,
+it introduces a new selftest for a feature that doesn't exist in 6.1. It
+was backported as a stable-dep of a1914d146622 ("selftests/bpf:
+Workaround strict bpf_lsm return value check"), also reverted in the
+previous patch.
+
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/bpf/DENYLIST.s390x    |  1 -
+ .../bpf/prog_tests/libbpf_get_fd_by_id_opts.c | 87 -------------------
+ .../bpf/progs/test_libbpf_get_fd_by_id_opts.c | 36 --------
+ 3 files changed, 124 deletions(-)
+ delete mode 100644 tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c
+ delete mode 100644 tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c
+
+diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x
+index beef1232a47aeb..0fb03b8047d535 100644
+--- a/tools/testing/selftests/bpf/DENYLIST.s390x
++++ b/tools/testing/selftests/bpf/DENYLIST.s390x
+@@ -76,4 +76,3 @@ lookup_key                               # JIT does not support calling kernel f
+ verify_pkcs7_sig                         # JIT does not support calling kernel function                                (kfunc)
+ kfunc_dynptr_param                       # JIT does not support calling kernel function                                (kfunc)
+ deny_namespace                           # failed to attach: ERROR: strerror_r(-524)=22                                (trampoline)
+-libbpf_get_fd_by_id_opts                 # failed to attach: ERROR: strerror_r(-524)=22                                (trampoline)
+diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c
+deleted file mode 100644
+index 25e5dfa9c315ce..00000000000000
+--- a/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c
++++ /dev/null
+@@ -1,87 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-
+-/*
+- * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+- *
+- * Author: Roberto Sassu <roberto.sassu@huawei.com>
+- */
+-
+-#include <test_progs.h>
+-
+-#include "test_libbpf_get_fd_by_id_opts.skel.h"
+-
+-void test_libbpf_get_fd_by_id_opts(void)
+-{
+-      struct test_libbpf_get_fd_by_id_opts *skel;
+-      struct bpf_map_info info_m = {};
+-      __u32 len = sizeof(info_m), value;
+-      int ret, zero = 0, fd = -1;
+-      LIBBPF_OPTS(bpf_get_fd_by_id_opts, fd_opts_rdonly,
+-              .open_flags = BPF_F_RDONLY,
+-      );
+-
+-      skel = test_libbpf_get_fd_by_id_opts__open_and_load();
+-      if (!ASSERT_OK_PTR(skel,
+-                         "test_libbpf_get_fd_by_id_opts__open_and_load"))
+-              return;
+-
+-      ret = test_libbpf_get_fd_by_id_opts__attach(skel);
+-      if (!ASSERT_OK(ret, "test_libbpf_get_fd_by_id_opts__attach"))
+-              goto close_prog;
+-
+-      ret = bpf_obj_get_info_by_fd(bpf_map__fd(skel->maps.data_input),
+-                                   &info_m, &len);
+-      if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd"))
+-              goto close_prog;
+-
+-      fd = bpf_map_get_fd_by_id(info_m.id);
+-      if (!ASSERT_LT(fd, 0, "bpf_map_get_fd_by_id"))
+-              goto close_prog;
+-
+-      fd = bpf_map_get_fd_by_id_opts(info_m.id, NULL);
+-      if (!ASSERT_LT(fd, 0, "bpf_map_get_fd_by_id_opts"))
+-              goto close_prog;
+-
+-      fd = bpf_map_get_fd_by_id_opts(info_m.id, &fd_opts_rdonly);
+-      if (!ASSERT_GE(fd, 0, "bpf_map_get_fd_by_id_opts"))
+-              goto close_prog;
+-
+-      /* Map lookup should work with read-only fd. */
+-      ret = bpf_map_lookup_elem(fd, &zero, &value);
+-      if (!ASSERT_OK(ret, "bpf_map_lookup_elem"))
+-              goto close_prog;
+-
+-      if (!ASSERT_EQ(value, 0, "map value mismatch"))
+-              goto close_prog;
+-
+-      /* Map update should not work with read-only fd. */
+-      ret = bpf_map_update_elem(fd, &zero, &len, BPF_ANY);
+-      if (!ASSERT_LT(ret, 0, "bpf_map_update_elem"))
+-              goto close_prog;
+-
+-      /* Map update should work with read-write fd. */
+-      ret = bpf_map_update_elem(bpf_map__fd(skel->maps.data_input), &zero,
+-                                &len, BPF_ANY);
+-      if (!ASSERT_OK(ret, "bpf_map_update_elem"))
+-              goto close_prog;
+-
+-      /* Prog get fd with opts set should not work (no kernel support). */
+-      ret = bpf_prog_get_fd_by_id_opts(0, &fd_opts_rdonly);
+-      if (!ASSERT_EQ(ret, -EINVAL, "bpf_prog_get_fd_by_id_opts"))
+-              goto close_prog;
+-
+-      /* Link get fd with opts set should not work (no kernel support). */
+-      ret = bpf_link_get_fd_by_id_opts(0, &fd_opts_rdonly);
+-      if (!ASSERT_EQ(ret, -EINVAL, "bpf_link_get_fd_by_id_opts"))
+-              goto close_prog;
+-
+-      /* BTF get fd with opts set should not work (no kernel support). */
+-      ret = bpf_btf_get_fd_by_id_opts(0, &fd_opts_rdonly);
+-      ASSERT_EQ(ret, -EINVAL, "bpf_btf_get_fd_by_id_opts");
+-
+-close_prog:
+-      if (fd >= 0)
+-              close(fd);
+-
+-      test_libbpf_get_fd_by_id_opts__destroy(skel);
+-}
+diff --git a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c
+deleted file mode 100644
+index f5ac5f3e89196f..00000000000000
+--- a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c
++++ /dev/null
+@@ -1,36 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-
+-/*
+- * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+- *
+- * Author: Roberto Sassu <roberto.sassu@huawei.com>
+- */
+-
+-#include "vmlinux.h"
+-#include <errno.h>
+-#include <bpf/bpf_helpers.h>
+-#include <bpf/bpf_tracing.h>
+-
+-/* From include/linux/mm.h. */
+-#define FMODE_WRITE   0x2
+-
+-struct {
+-      __uint(type, BPF_MAP_TYPE_ARRAY);
+-      __uint(max_entries, 1);
+-      __type(key, __u32);
+-      __type(value, __u32);
+-} data_input SEC(".maps");
+-
+-char _license[] SEC("license") = "GPL";
+-
+-SEC("lsm/bpf_map")
+-int BPF_PROG(check_access, struct bpf_map *map, fmode_t fmode)
+-{
+-      if (map != (struct bpf_map *)&data_input)
+-              return 0;
+-
+-      if (fmode & FMODE_WRITE)
+-              return -EACCES;
+-
+-      return 0;
+-}
+-- 
+2.53.0
+
diff --git a/queue-6.1/revert-selftests-bpf-workaround-strict-bpf_lsm-retur.patch b/queue-6.1/revert-selftests-bpf-workaround-strict-bpf_lsm-retur.patch
new file mode 100644 (file)
index 0000000..19704f7
--- /dev/null
@@ -0,0 +1,40 @@
+From 7387948319daf8a9508c239821524a836e3cb6bb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:40:48 +0200
+Subject: Revert "selftests/bpf: Workaround strict bpf_lsm return value check."
+
+From: Paul Chaignon <paul.chaignon@gmail.com>
+
+This reverts commit a1914d146622 ("selftests/bpf: Workaround strict
+bpf_lsm return value check"). It seems it was picked up by mistake.
+
+It applies to a selftest that didn't exist in 6.1. The whole selftest
+was then backported as a stable-dep in commit 45108a7b4866
+("selftests/bpf: Add tests for _opts variants of bpf_*_get_fd_by_id()")
+(reverted as well in the next patch).
+
+The new selftest covers the bpf_*_get_fd_by_id structures. Those don't
+exist in 6.1 so the selftest shouldn't either.
+
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c  | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c
+index 568816307f7125..f5ac5f3e89196f 100644
+--- a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c
++++ b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c
+@@ -31,7 +31,6 @@ int BPF_PROG(check_access, struct bpf_map *map, fmode_t fmode)
+       if (fmode & FMODE_WRITE)
+               return -EACCES;
+-      barrier();
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.1/selftests-bpf-add-generic-bpf-program-tester-loader.patch b/queue-6.1/selftests-bpf-add-generic-bpf-program-tester-loader.patch
new file mode 100644 (file)
index 0000000..a3d6e6b
--- /dev/null
@@ -0,0 +1,398 @@
+From 87e3312c479bf46aa628049f486bb2068cd9b49a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:39:58 +0200
+Subject: selftests/bpf: add generic BPF program tester-loader
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 537c3f66eac137a02ec50a40219d2da6597e5dc9 ]
+
+It's become a common pattern to have a collection of small BPF programs
+in one BPF object file, each representing one test case. On user-space
+side of such tests we maintain a table of program names and expected
+failure or success, along with optional expected verifier log message.
+
+This works, but each set of tests reimplement this mundane code over and
+over again, which is a waste of time for anyone trying to add a new set
+of tests. Furthermore, it's quite error prone as it's way too easy to miss
+some entries in these manually maintained test tables (as evidences by
+dynptr_fail tests, in which ringbuf_release_uninit_dynptr subtest was
+accidentally missed; this is fixed in next patch).
+
+So this patch implements generic test_loader, which accepts skeleton
+name and handles the rest of details: opens and loads BPF object file,
+making sure each program is tested in isolation. Optionally each test
+case can specify expected BPF verifier log message. In case of failure,
+tester makes sure to report verifier log, but it also reports verifier
+log in verbose mode unconditionally.
+
+Now, the interesting deviation from existing custom implementations is
+the use of btf_decl_tag attribute to specify expected-to-fail vs
+expected-to-succeed markers and, optionally, expected log message
+directly next to BPF program source code, eliminating the need to
+manually create and update table of tests.
+
+We define few macros wrapping btf_decl_tag with a convention that all
+values of btf_decl_tag start with "comment:" prefix, and then utilizing
+a very simple "just_some_text_tag" or "some_key_name=<value>" pattern to
+define things like expected success/failure, expected verifier message,
+extra verifier log level (if necessary). This approach is demonstrated
+by next patch in which two existing sets of failure tests are converted.
+
+Tester supports both expected-to-fail and expected-to-succeed programs,
+though this patch set didn't convert any existing expected-to-succeed
+programs yet, as existing tests couple BPF program loading with their
+further execution through attach or test_prog_run. One way to allow
+testing scenarios like this would be ability to specify custom callback,
+executed for each successfully loaded BPF program. This is left for
+follow up patches, after some more analysis of existing test cases.
+
+This test_loader is, hopefully, a start of a test_verifier-like runner,
+but integrated into test_progs infrastructure. It will allow much better
+"user experience" of defining low-level verification tests that can take
+advantage of all the libbpf-provided nicety features on BPF side: global
+variables, declarative maps, etc.  All while having a choice of defining
+it in C or as BPF assembly (through __attribute__((naked)) functions and
+using embedded asm), depending on what makes most sense in each
+particular case. This will be explored in follow up patches as well.
+
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20221207201648.2990661-1-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Stable-dep-of: 95ebb376176c ("selftests/bpf: Convert test_global_funcs test to test_loader framework")
+[ Note: Minor conflict in Makefile. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/bpf/Makefile         |   2 +-
+ tools/testing/selftests/bpf/progs/bpf_misc.h |   5 +
+ tools/testing/selftests/bpf/test_loader.c    | 233 +++++++++++++++++++
+ tools/testing/selftests/bpf/test_progs.h     |  33 +++
+ 4 files changed, 272 insertions(+), 1 deletion(-)
+ create mode 100644 tools/testing/selftests/bpf/test_loader.c
+
+diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
+index b09205d925114a..541f251036ae7b 100644
+--- a/tools/testing/selftests/bpf/Makefile
++++ b/tools/testing/selftests/bpf/Makefile
+@@ -519,7 +519,7 @@ TRUNNER_BPF_PROGS_DIR := progs
+ TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c        \
+                        network_helpers.c testing_helpers.c            \
+                        btf_helpers.c flow_dissector_load.h            \
+-                       cap_helpers.c
++                       cap_helpers.c test_loader.c
+ TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko        \
+                      $(OUTPUT)/liburandom_read.so                     \
+                      $(OUTPUT)/xdp_synproxy                           \
+diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
+index 5bb11fe595a439..4a01ea9113bfd7 100644
+--- a/tools/testing/selftests/bpf/progs/bpf_misc.h
++++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
+@@ -2,6 +2,11 @@
+ #ifndef __BPF_MISC_H__
+ #define __BPF_MISC_H__
++#define __msg(msg)            __attribute__((btf_decl_tag("comment:test_expect_msg=" msg)))
++#define __failure             __attribute__((btf_decl_tag("comment:test_expect_failure")))
++#define __success             __attribute__((btf_decl_tag("comment:test_expect_success")))
++#define __log_level(lvl)      __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
++
+ #if defined(__TARGET_ARCH_x86)
+ #define SYSCALL_WRAPPER 1
+ #define SYS_PREFIX "__x64_"
+diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
+new file mode 100644
+index 00000000000000..679efb3aa785e3
+--- /dev/null
++++ b/tools/testing/selftests/bpf/test_loader.c
+@@ -0,0 +1,233 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
++#include <stdlib.h>
++#include <test_progs.h>
++#include <bpf/btf.h>
++
++#define str_has_pfx(str, pfx) \
++      (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0)
++
++#define TEST_LOADER_LOG_BUF_SZ 1048576
++
++#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure"
++#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success"
++#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg="
++#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
++
++struct test_spec {
++      const char *name;
++      bool expect_failure;
++      const char *expect_msg;
++      int log_level;
++};
++
++static int tester_init(struct test_loader *tester)
++{
++      if (!tester->log_buf) {
++              tester->log_buf_sz = TEST_LOADER_LOG_BUF_SZ;
++              tester->log_buf = malloc(tester->log_buf_sz);
++              if (!ASSERT_OK_PTR(tester->log_buf, "tester_log_buf"))
++                      return -ENOMEM;
++      }
++
++      return 0;
++}
++
++void test_loader_fini(struct test_loader *tester)
++{
++      if (!tester)
++              return;
++
++      free(tester->log_buf);
++}
++
++static int parse_test_spec(struct test_loader *tester,
++                         struct bpf_object *obj,
++                         struct bpf_program *prog,
++                         struct test_spec *spec)
++{
++      struct btf *btf;
++      int func_id, i;
++
++      memset(spec, 0, sizeof(*spec));
++
++      spec->name = bpf_program__name(prog);
++
++      btf = bpf_object__btf(obj);
++      if (!btf) {
++              ASSERT_FAIL("BPF object has no BTF");
++              return -EINVAL;
++      }
++
++      func_id = btf__find_by_name_kind(btf, spec->name, BTF_KIND_FUNC);
++      if (func_id < 0) {
++              ASSERT_FAIL("failed to find FUNC BTF type for '%s'", spec->name);
++              return -EINVAL;
++      }
++
++      for (i = 1; i < btf__type_cnt(btf); i++) {
++              const struct btf_type *t;
++              const char *s;
++
++              t = btf__type_by_id(btf, i);
++              if (!btf_is_decl_tag(t))
++                      continue;
++
++              if (t->type != func_id || btf_decl_tag(t)->component_idx != -1)
++                      continue;
++
++              s = btf__str_by_offset(btf, t->name_off);
++              if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) {
++                      spec->expect_failure = true;
++              } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) {
++                      spec->expect_failure = false;
++              } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) {
++                      spec->expect_msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1;
++              } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) {
++                      errno = 0;
++                      spec->log_level = strtol(s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1, NULL, 0);
++                      if (errno) {
++                              ASSERT_FAIL("failed to parse test log level from '%s'", s);
++                              return -EINVAL;
++                      }
++              }
++      }
++
++      return 0;
++}
++
++static void prepare_case(struct test_loader *tester,
++                       struct test_spec *spec,
++                       struct bpf_object *obj,
++                       struct bpf_program *prog)
++{
++      int min_log_level = 0;
++
++      if (env.verbosity > VERBOSE_NONE)
++              min_log_level = 1;
++      if (env.verbosity > VERBOSE_VERY)
++              min_log_level = 2;
++
++      bpf_program__set_log_buf(prog, tester->log_buf, tester->log_buf_sz);
++
++      /* Make sure we set at least minimal log level, unless test requirest
++       * even higher level already. Make sure to preserve independent log
++       * level 4 (verifier stats), though.
++       */
++      if ((spec->log_level & 3) < min_log_level)
++              bpf_program__set_log_level(prog, (spec->log_level & 4) | min_log_level);
++      else
++              bpf_program__set_log_level(prog, spec->log_level);
++
++      tester->log_buf[0] = '\0';
++}
++
++static void emit_verifier_log(const char *log_buf, bool force)
++{
++      if (!force && env.verbosity == VERBOSE_NONE)
++              return;
++      fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log_buf);
++}
++
++static void validate_case(struct test_loader *tester,
++                        struct test_spec *spec,
++                        struct bpf_object *obj,
++                        struct bpf_program *prog,
++                        int load_err)
++{
++      if (spec->expect_msg) {
++              char *match;
++
++              match = strstr(tester->log_buf, spec->expect_msg);
++              if (!ASSERT_OK_PTR(match, "expect_msg")) {
++                      /* if we are in verbose mode, we've already emitted log */
++                      if (env.verbosity == VERBOSE_NONE)
++                              emit_verifier_log(tester->log_buf, true /*force*/);
++                      fprintf(stderr, "EXPECTED MSG: '%s'\n", spec->expect_msg);
++                      return;
++              }
++      }
++}
++
++/* this function is forced noinline and has short generic name to look better
++ * in test_progs output (in case of a failure)
++ */
++static noinline
++void run_subtest(struct test_loader *tester,
++               const char *skel_name,
++               skel_elf_bytes_fn elf_bytes_factory)
++{
++      LIBBPF_OPTS(bpf_object_open_opts, open_opts, .object_name = skel_name);
++      struct bpf_object *obj = NULL, *tobj;
++      struct bpf_program *prog, *tprog;
++      const void *obj_bytes;
++      size_t obj_byte_cnt;
++      int err;
++
++      if (tester_init(tester) < 0)
++              return; /* failed to initialize tester */
++
++      obj_bytes = elf_bytes_factory(&obj_byte_cnt);
++      obj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, &open_opts);
++      if (!ASSERT_OK_PTR(obj, "obj_open_mem"))
++              return;
++
++      bpf_object__for_each_program(prog, obj) {
++              const char *prog_name = bpf_program__name(prog);
++              struct test_spec spec;
++
++              if (!test__start_subtest(prog_name))
++                      continue;
++
++              /* if we can't derive test specification, go to the next test */
++              err = parse_test_spec(tester, obj, prog, &spec);
++              if (!ASSERT_OK(err, "parse_test_spec"))
++                      continue;
++
++              tobj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, &open_opts);
++              if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */
++                      continue;
++
++              bpf_object__for_each_program(tprog, tobj)
++                      bpf_program__set_autoload(tprog, false);
++
++              bpf_object__for_each_program(tprog, tobj) {
++                      /* only load specified program */
++                      if (strcmp(bpf_program__name(tprog), prog_name) == 0) {
++                              bpf_program__set_autoload(tprog, true);
++                              break;
++                      }
++              }
++
++              prepare_case(tester, &spec, tobj, tprog);
++
++              err = bpf_object__load(tobj);
++              if (spec.expect_failure) {
++                      if (!ASSERT_ERR(err, "unexpected_load_success")) {
++                              emit_verifier_log(tester->log_buf, false /*force*/);
++                              goto tobj_cleanup;
++                      }
++              } else {
++                      if (!ASSERT_OK(err, "unexpected_load_failure")) {
++                              emit_verifier_log(tester->log_buf, true /*force*/);
++                              goto tobj_cleanup;
++                      }
++              }
++
++              emit_verifier_log(tester->log_buf, false /*force*/);
++              validate_case(tester, &spec, tobj, tprog, err);
++
++tobj_cleanup:
++              bpf_object__close(tobj);
++      }
++
++      bpf_object__close(obj);
++}
++
++void test_loader__run_subtests(struct test_loader *tester,
++                             const char *skel_name,
++                             skel_elf_bytes_fn elf_bytes_factory)
++{
++      /* see comment in run_subtest() for why we do this function nesting */
++      run_subtest(tester, skel_name, elf_bytes_factory);
++}
+diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h
+index feb14f14006d98..ff1caffefa5256 100644
+--- a/tools/testing/selftests/bpf/test_progs.h
++++ b/tools/testing/selftests/bpf/test_progs.h
+@@ -1,4 +1,7 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
++#ifndef __TEST_PROGS_H
++#define __TEST_PROGS_H
++
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <errno.h>
+@@ -210,6 +213,12 @@ int test__join_cgroup(const char *path);
+ #define CHECK_ATTR(condition, tag, format...) \
+       _CHECK(condition, tag, tattr.duration, format)
++#define ASSERT_FAIL(fmt, args...) ({                                  \
++      static int duration = 0;                                        \
++      CHECK(false, "", fmt"\n", ##args);                              \
++      false;                                                          \
++})
++
+ #define ASSERT_TRUE(actual, name) ({                                  \
+       static int duration = 0;                                        \
+       bool ___ok = (actual);                                          \
+@@ -395,3 +404,27 @@ int write_sysctl(const char *sysctl, const char *value);
+ #endif
+ #define BPF_TESTMOD_TEST_FILE "/sys/kernel/bpf_testmod"
++
++struct test_loader {
++      char *log_buf;
++      size_t log_buf_sz;
++
++      struct bpf_object *obj;
++};
++
++typedef const void *(*skel_elf_bytes_fn)(size_t *sz);
++
++extern void test_loader__run_subtests(struct test_loader *tester,
++                                    const char *skel_name,
++                                    skel_elf_bytes_fn elf_bytes_factory);
++
++extern void test_loader_fini(struct test_loader *tester);
++
++#define RUN_TESTS(skel) ({                                                   \
++      struct test_loader tester = {};                                        \
++                                                                             \
++      test_loader__run_subtests(&tester, #skel, skel##__elf_bytes);          \
++      test_loader_fini(&tester);                                             \
++})
++
++#endif /* __TEST_PROGS_H */
+-- 
+2.53.0
+
diff --git a/queue-6.1/selftests-bpf-add-read_build_id-function.patch b/queue-6.1/selftests-bpf-add-read_build_id-function.patch
new file mode 100644 (file)
index 0000000..c36979c
--- /dev/null
@@ -0,0 +1,148 @@
+From 8f6682bf60d248de4e5130bc840b9b3828b172ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:41:07 +0200
+Subject: selftests/bpf: Add read_build_id function
+
+From: Jiri Olsa <jolsa@kernel.org>
+
+[ Upstream commit 88dc8b3605b38a440fba45edcc53a6c7a98eee3b ]
+
+Adding read_build_id function that parses out build id from
+specified binary.
+
+It will replace extract_build_id and also be used in following
+changes.
+
+Signed-off-by: Jiri Olsa <jolsa@kernel.org>
+Link: https://lore.kernel.org/r/20230331093157.1749137-3-jolsa@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Fixes: be4e85369e5a ("selftests/bpf: Replace extract_build_id with read_build_id")
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/bpf/trace_helpers.c | 82 +++++++++++++++++++++
+ tools/testing/selftests/bpf/trace_helpers.h |  5 ++
+ 2 files changed, 87 insertions(+)
+
+diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c
+index 9c4be2cdb21a02..afc33ba36ccc6e 100644
+--- a/tools/testing/selftests/bpf/trace_helpers.c
++++ b/tools/testing/selftests/bpf/trace_helpers.c
+@@ -11,6 +11,9 @@
+ #include <linux/perf_event.h>
+ #include <sys/mman.h>
+ #include "trace_helpers.h"
++#include <linux/limits.h>
++#include <libelf.h>
++#include <gelf.h>
+ #define DEBUGFS "/sys/kernel/debug/tracing/"
+@@ -224,3 +227,82 @@ ssize_t get_rel_offset(uintptr_t addr)
+       fclose(f);
+       return -EINVAL;
+ }
++
++static int
++parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id)
++{
++      Elf32_Word note_offs = 0;
++
++      while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
++              Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
++
++              if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") &&
++                  !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 &&
++                  nhdr->n_descsz <= BPF_BUILD_ID_SIZE) {
++                      memcpy(build_id, note_start + note_offs +
++                             ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz);
++                      memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz);
++                      return (int) nhdr->n_descsz;
++              }
++
++              note_offs = note_offs + sizeof(Elf32_Nhdr) +
++                         ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
++      }
++
++      return -ENOENT;
++}
++
++/* Reads binary from *path* file and returns it in the *build_id* buffer
++ * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes.
++ * Returns size of build id on success. On error the error value is
++ * returned.
++ */
++int read_build_id(const char *path, char *build_id, size_t size)
++{
++      int fd, err = -EINVAL;
++      Elf *elf = NULL;
++      GElf_Ehdr ehdr;
++      size_t max, i;
++
++      if (size < BPF_BUILD_ID_SIZE)
++              return -EINVAL;
++
++      fd = open(path, O_RDONLY | O_CLOEXEC);
++      if (fd < 0)
++              return -errno;
++
++      (void)elf_version(EV_CURRENT);
++
++      elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
++      if (!elf)
++              goto out;
++      if (elf_kind(elf) != ELF_K_ELF)
++              goto out;
++      if (!gelf_getehdr(elf, &ehdr))
++              goto out;
++
++      for (i = 0; i < ehdr.e_phnum; i++) {
++              GElf_Phdr mem, *phdr;
++              char *data;
++
++              phdr = gelf_getphdr(elf, i, &mem);
++              if (!phdr)
++                      goto out;
++              if (phdr->p_type != PT_NOTE)
++                      continue;
++              data = elf_rawfile(elf, &max);
++              if (!data)
++                      goto out;
++              if (phdr->p_offset + phdr->p_memsz > max)
++                      goto out;
++              err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id);
++              if (err > 0)
++                      break;
++      }
++
++out:
++      if (elf)
++              elf_end(elf);
++      close(fd);
++      return err;
++}
+diff --git a/tools/testing/selftests/bpf/trace_helpers.h b/tools/testing/selftests/bpf/trace_helpers.h
+index 238a9c98cde27f..709871f3285256 100644
+--- a/tools/testing/selftests/bpf/trace_helpers.h
++++ b/tools/testing/selftests/bpf/trace_helpers.h
+@@ -4,6 +4,9 @@
+ #include <bpf/libbpf.h>
++#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
++#define ALIGN(x, a)           __ALIGN_MASK(x, (typeof(x))(a)-1)
++
+ struct ksym {
+       long addr;
+       char *name;
+@@ -21,4 +24,6 @@ void read_trace_pipe(void);
+ ssize_t get_uprobe_offset(const void *addr);
+ ssize_t get_rel_offset(uintptr_t addr);
++int read_build_id(const char *path, char *build_id, size_t size);
++
+ #endif
+-- 
+2.53.0
+
diff --git a/queue-6.1/selftests-bpf-convert-test_global_funcs-test-to-test.patch b/queue-6.1/selftests-bpf-convert-test_global_funcs-test-to-test.patch
new file mode 100644 (file)
index 0000000..ea49b1f
--- /dev/null
@@ -0,0 +1,642 @@
+From e18371de4fcaddfe95085750932ac7de86e02fe4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:40:40 +0200
+Subject: selftests/bpf: Convert test_global_funcs test to test_loader
+ framework
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 95ebb376176c52382293e05e63f142114a5e40ef ]
+
+Convert 17 test_global_funcs subtests into test_loader framework for
+easier maintenance and more declarative way to define expected
+failures/successes.
+
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Stanislav Fomichev <sdf@google.com>
+Link: https://lore.kernel.org/bpf/20230216045954.3002473-3-andrii@kernel.org
+Fixes: bbac91d57ac2 ("bpf: Allow reads from uninit stack")
+[ Notes: This backport fixes backport commit bbac91d57ac2 ("bpf: Allow
+  reads from uninit stack"), which broke the BPF selftest build. A minor
+  conflict needed resolution in test_global_func10.c on the error
+  message. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../bpf/prog_tests/test_global_funcs.c        | 131 +++++-------------
+ .../selftests/bpf/progs/test_global_func1.c   |   6 +-
+ .../selftests/bpf/progs/test_global_func10.c  |   1 +
+ .../selftests/bpf/progs/test_global_func11.c  |   4 +-
+ .../selftests/bpf/progs/test_global_func12.c  |   4 +-
+ .../selftests/bpf/progs/test_global_func13.c  |   4 +-
+ .../selftests/bpf/progs/test_global_func14.c  |   4 +-
+ .../selftests/bpf/progs/test_global_func15.c  |   4 +-
+ .../selftests/bpf/progs/test_global_func16.c  |   4 +-
+ .../selftests/bpf/progs/test_global_func17.c  |   4 +-
+ .../selftests/bpf/progs/test_global_func2.c   |  43 +++++-
+ .../selftests/bpf/progs/test_global_func3.c   |  10 +-
+ .../selftests/bpf/progs/test_global_func4.c   |  55 +++++++-
+ .../selftests/bpf/progs/test_global_func5.c   |   4 +-
+ .../selftests/bpf/progs/test_global_func6.c   |   4 +-
+ .../selftests/bpf/progs/test_global_func7.c   |   4 +-
+ .../selftests/bpf/progs/test_global_func8.c   |   4 +-
+ .../selftests/bpf/progs/test_global_func9.c   |   4 +-
+ 18 files changed, 172 insertions(+), 122 deletions(-)
+
+diff --git a/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c b/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c
+index 7295cc60f72487..2ff4d5c7abfceb 100644
+--- a/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c
++++ b/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c
+@@ -1,104 +1,41 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /* Copyright (c) 2020 Facebook */
+ #include <test_progs.h>
+-
+-const char *err_str;
+-bool found;
+-
+-static int libbpf_debug_print(enum libbpf_print_level level,
+-                            const char *format, va_list args)
+-{
+-      char *log_buf;
+-
+-      if (level != LIBBPF_WARN ||
+-          strcmp(format, "libbpf: \n%s\n")) {
+-              vprintf(format, args);
+-              return 0;
+-      }
+-
+-      log_buf = va_arg(args, char *);
+-      if (!log_buf)
+-              goto out;
+-      if (err_str && strstr(log_buf, err_str) == 0)
+-              found = true;
+-out:
+-      printf(format, log_buf);
+-      return 0;
+-}
+-
+-extern int extra_prog_load_log_flags;
+-
+-static int check_load(const char *file)
+-{
+-      struct bpf_object *obj = NULL;
+-      struct bpf_program *prog;
+-      int err;
+-
+-      found = false;
+-
+-      obj = bpf_object__open_file(file, NULL);
+-      err = libbpf_get_error(obj);
+-      if (err)
+-              return err;
+-
+-      prog = bpf_object__next_program(obj, NULL);
+-      if (!prog) {
+-              err = -ENOENT;
+-              goto err_out;
+-      }
+-
+-      bpf_program__set_flags(prog, BPF_F_TEST_RND_HI32);
+-      bpf_program__set_log_level(prog, extra_prog_load_log_flags);
+-
+-      err = bpf_object__load(obj);
+-
+-err_out:
+-      bpf_object__close(obj);
+-      return err;
+-}
+-
+-struct test_def {
+-      const char *file;
+-      const char *err_str;
+-};
++#include "test_global_func1.skel.h"
++#include "test_global_func2.skel.h"
++#include "test_global_func3.skel.h"
++#include "test_global_func4.skel.h"
++#include "test_global_func5.skel.h"
++#include "test_global_func6.skel.h"
++#include "test_global_func7.skel.h"
++#include "test_global_func8.skel.h"
++#include "test_global_func9.skel.h"
++#include "test_global_func10.skel.h"
++#include "test_global_func11.skel.h"
++#include "test_global_func12.skel.h"
++#include "test_global_func13.skel.h"
++#include "test_global_func14.skel.h"
++#include "test_global_func15.skel.h"
++#include "test_global_func16.skel.h"
++#include "test_global_func17.skel.h"
+ void test_test_global_funcs(void)
+ {
+-      struct test_def tests[] = {
+-              { "test_global_func1.bpf.o", "combined stack size of 4 calls is 544" },
+-              { "test_global_func2.bpf.o" },
+-              { "test_global_func3.bpf.o", "the call stack of 8 frames" },
+-              { "test_global_func4.bpf.o" },
+-              { "test_global_func5.bpf.o", "expected pointer to ctx, but got PTR" },
+-              { "test_global_func6.bpf.o", "modified ctx ptr R2" },
+-              { "test_global_func7.bpf.o", "foo() doesn't return scalar" },
+-              { "test_global_func8.bpf.o" },
+-              { "test_global_func9.bpf.o" },
+-              { "test_global_func10.bpf.o", "invalid indirect read from stack" },
+-              { "test_global_func11.bpf.o", "Caller passes invalid args into func#1" },
+-              { "test_global_func12.bpf.o", "invalid mem access 'mem_or_null'" },
+-              { "test_global_func13.bpf.o", "Caller passes invalid args into func#1" },
+-              { "test_global_func14.bpf.o", "reference type('FWD S') size cannot be determined" },
+-              { "test_global_func15.bpf.o", "At program exit the register R0 has value" },
+-              { "test_global_func16.bpf.o", "invalid indirect read from stack" },
+-              { "test_global_func17.bpf.o", "Caller passes invalid args into func#1" },
+-      };
+-      libbpf_print_fn_t old_print_fn = NULL;
+-      int err, i, duration = 0;
+-
+-      old_print_fn = libbpf_set_print(libbpf_debug_print);
+-
+-      for (i = 0; i < ARRAY_SIZE(tests); i++) {
+-              const struct test_def *test = &tests[i];
+-
+-              if (!test__start_subtest(test->file))
+-                      continue;
+-
+-              err_str = test->err_str;
+-              err = check_load(test->file);
+-              CHECK_FAIL(!!err ^ !!err_str);
+-              if (err_str)
+-                      CHECK(found, "", "expected string '%s'", err_str);
+-      }
+-      libbpf_set_print(old_print_fn);
++      RUN_TESTS(test_global_func1);
++      RUN_TESTS(test_global_func2);
++      RUN_TESTS(test_global_func3);
++      RUN_TESTS(test_global_func4);
++      RUN_TESTS(test_global_func5);
++      RUN_TESTS(test_global_func6);
++      RUN_TESTS(test_global_func7);
++      RUN_TESTS(test_global_func8);
++      RUN_TESTS(test_global_func9);
++      RUN_TESTS(test_global_func10);
++      RUN_TESTS(test_global_func11);
++      RUN_TESTS(test_global_func12);
++      RUN_TESTS(test_global_func13);
++      RUN_TESTS(test_global_func14);
++      RUN_TESTS(test_global_func15);
++      RUN_TESTS(test_global_func16);
++      RUN_TESTS(test_global_func17);
+ }
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func1.c b/tools/testing/selftests/bpf/progs/test_global_func1.c
+index 7b42dad187b894..23970a20b3249d 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func1.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func1.c
+@@ -3,10 +3,9 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+-#ifndef MAX_STACK
+ #define MAX_STACK (512 - 3 * 32 + 8)
+-#endif
+ static __attribute__ ((noinline))
+ int f0(int var, struct __sk_buff *skb)
+@@ -39,7 +38,8 @@ int f3(int val, struct __sk_buff *skb, int var)
+ }
+ SEC("tc")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("combined stack size of 4 calls is 544")
++int global_func1(struct __sk_buff *skb)
+ {
+       return f0(1, skb) + f1(skb) + f2(2, skb) + f3(3, skb, 4);
+ }
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func10.c b/tools/testing/selftests/bpf/progs/test_global_func10.c
+index d361eba167f6a4..8fba3f3649e227 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func10.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func10.c
+@@ -2,6 +2,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ struct Small {
+       long x;
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func11.c b/tools/testing/selftests/bpf/progs/test_global_func11.c
+index ef5277d982d921..283e036dc401e9 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func11.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func11.c
+@@ -2,6 +2,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ struct S {
+       int x;
+@@ -13,7 +14,8 @@ __noinline int foo(const struct S *s)
+ }
+ SEC("cgroup_skb/ingress")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("Caller passes invalid args into func#1")
++int global_func11(struct __sk_buff *skb)
+ {
+       return foo((const void *)skb);
+ }
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func12.c b/tools/testing/selftests/bpf/progs/test_global_func12.c
+index 62343527cc598d..7f159d83c6f673 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func12.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func12.c
+@@ -2,6 +2,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ struct S {
+       int x;
+@@ -13,7 +14,8 @@ __noinline int foo(const struct S *s)
+ }
+ SEC("cgroup_skb/ingress")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("invalid mem access 'mem_or_null'")
++int global_func12(struct __sk_buff *skb)
+ {
+       const struct S s = {.x = skb->len };
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func13.c b/tools/testing/selftests/bpf/progs/test_global_func13.c
+index ff8897c1ac22b4..02ea80da75b573 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func13.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func13.c
+@@ -2,6 +2,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ struct S {
+       int x;
+@@ -16,7 +17,8 @@ __noinline int foo(const struct S *s)
+ }
+ SEC("cgroup_skb/ingress")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("Caller passes invalid args into func#1")
++int global_func13(struct __sk_buff *skb)
+ {
+       const struct S *s = (const struct S *)(0xbedabeda);
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func14.c b/tools/testing/selftests/bpf/progs/test_global_func14.c
+index 698c77199ebf7d..33b7d5efd7b262 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func14.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func14.c
+@@ -2,6 +2,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ struct S;
+@@ -14,7 +15,8 @@ __noinline int foo(const struct S *s)
+ }
+ SEC("cgroup_skb/ingress")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("reference type('FWD S') size cannot be determined")
++int global_func14(struct __sk_buff *skb)
+ {
+       return foo(NULL);
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func15.c b/tools/testing/selftests/bpf/progs/test_global_func15.c
+index c19c435988d557..b512d6a6c75e54 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func15.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func15.c
+@@ -2,6 +2,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ __noinline int foo(unsigned int *v)
+ {
+@@ -12,7 +13,8 @@ __noinline int foo(unsigned int *v)
+ }
+ SEC("cgroup_skb/ingress")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("At program exit the register R0 has value")
++int global_func15(struct __sk_buff *skb)
+ {
+       unsigned int v = 1;
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func16.c b/tools/testing/selftests/bpf/progs/test_global_func16.c
+index 0312d1e8d8c060..e7206304632e15 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func16.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func16.c
+@@ -2,6 +2,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ __noinline int foo(int (*arr)[10])
+ {
+@@ -12,7 +13,8 @@ __noinline int foo(int (*arr)[10])
+ }
+ SEC("cgroup_skb/ingress")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("invalid indirect read from stack")
++int global_func16(struct __sk_buff *skb)
+ {
+       int array[10];
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func17.c b/tools/testing/selftests/bpf/progs/test_global_func17.c
+index 2b8b9b8ba0183c..a32e11c7d933ee 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func17.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func17.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+ #include <vmlinux.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ __noinline int foo(int *p)
+ {
+@@ -10,7 +11,8 @@ __noinline int foo(int *p)
+ const volatile int i;
+ SEC("tc")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("Caller passes invalid args into func#1")
++int global_func17(struct __sk_buff *skb)
+ {
+       return foo((int *)&i);
+ }
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func2.c b/tools/testing/selftests/bpf/progs/test_global_func2.c
+index 2c18d82923a2d5..3dce97fb52a4b7 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func2.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func2.c
+@@ -1,4 +1,45 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+ /* Copyright (c) 2020 Facebook */
++#include <stddef.h>
++#include <linux/bpf.h>
++#include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
++
+ #define MAX_STACK (512 - 3 * 32)
+-#include "test_global_func1.c"
++
++static __attribute__ ((noinline))
++int f0(int var, struct __sk_buff *skb)
++{
++      return skb->len;
++}
++
++__attribute__ ((noinline))
++int f1(struct __sk_buff *skb)
++{
++      volatile char buf[MAX_STACK] = {};
++
++      return f0(0, skb) + skb->len;
++}
++
++int f3(int, struct __sk_buff *skb, int);
++
++__attribute__ ((noinline))
++int f2(int val, struct __sk_buff *skb)
++{
++      return f1(skb) + f3(val, skb, 1);
++}
++
++__attribute__ ((noinline))
++int f3(int val, struct __sk_buff *skb, int var)
++{
++      volatile char buf[MAX_STACK] = {};
++
++      return skb->ifindex * val * var;
++}
++
++SEC("tc")
++__success
++int global_func2(struct __sk_buff *skb)
++{
++      return f0(1, skb) + f1(skb) + f2(2, skb) + f3(3, skb, 4);
++}
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func3.c b/tools/testing/selftests/bpf/progs/test_global_func3.c
+index 01bf8275dfd640..142b682d3c2f08 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func3.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func3.c
+@@ -3,6 +3,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ __attribute__ ((noinline))
+ int f1(struct __sk_buff *skb)
+@@ -46,20 +47,15 @@ int f7(struct __sk_buff *skb)
+       return f6(skb);
+ }
+-#ifndef NO_FN8
+ __attribute__ ((noinline))
+ int f8(struct __sk_buff *skb)
+ {
+       return f7(skb);
+ }
+-#endif
+ SEC("tc")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("the call stack of 8 frames")
++int global_func3(struct __sk_buff *skb)
+ {
+-#ifndef NO_FN8
+       return f8(skb);
+-#else
+-      return f7(skb);
+-#endif
+ }
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func4.c b/tools/testing/selftests/bpf/progs/test_global_func4.c
+index 610f75edf27643..1733d87ad3f3ea 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func4.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func4.c
+@@ -1,4 +1,55 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+ /* Copyright (c) 2020 Facebook */
+-#define NO_FN8
+-#include "test_global_func3.c"
++#include <stddef.h>
++#include <linux/bpf.h>
++#include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
++
++__attribute__ ((noinline))
++int f1(struct __sk_buff *skb)
++{
++      return skb->len;
++}
++
++__attribute__ ((noinline))
++int f2(int val, struct __sk_buff *skb)
++{
++      return f1(skb) + val;
++}
++
++__attribute__ ((noinline))
++int f3(int val, struct __sk_buff *skb, int var)
++{
++      return f2(var, skb) + val;
++}
++
++__attribute__ ((noinline))
++int f4(struct __sk_buff *skb)
++{
++      return f3(1, skb, 2);
++}
++
++__attribute__ ((noinline))
++int f5(struct __sk_buff *skb)
++{
++      return f4(skb);
++}
++
++__attribute__ ((noinline))
++int f6(struct __sk_buff *skb)
++{
++      return f5(skb);
++}
++
++__attribute__ ((noinline))
++int f7(struct __sk_buff *skb)
++{
++      return f6(skb);
++}
++
++SEC("tc")
++__success
++int global_func4(struct __sk_buff *skb)
++{
++      return f7(skb);
++}
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func5.c b/tools/testing/selftests/bpf/progs/test_global_func5.c
+index 9248d03e0d06fb..cc55aedaf82d57 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func5.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func5.c
+@@ -3,6 +3,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ __attribute__ ((noinline))
+ int f1(struct __sk_buff *skb)
+@@ -25,7 +26,8 @@ int f3(int val, struct __sk_buff *skb)
+ }
+ SEC("tc")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("expected pointer to ctx, but got PTR")
++int global_func5(struct __sk_buff *skb)
+ {
+       return f1(skb) + f2(2, skb) + f3(3, skb);
+ }
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func6.c b/tools/testing/selftests/bpf/progs/test_global_func6.c
+index af8c78bdfb2575..46c38c8f2cf032 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func6.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func6.c
+@@ -3,6 +3,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ __attribute__ ((noinline))
+ int f1(struct __sk_buff *skb)
+@@ -25,7 +26,8 @@ int f3(int val, struct __sk_buff *skb)
+ }
+ SEC("tc")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("modified ctx ptr R2")
++int global_func6(struct __sk_buff *skb)
+ {
+       return f1(skb) + f2(2, skb) + f3(3, skb);
+ }
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func7.c b/tools/testing/selftests/bpf/progs/test_global_func7.c
+index 6cb8e2f5254cf1..f182febfde3c08 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func7.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func7.c
+@@ -3,6 +3,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ __attribute__ ((noinline))
+ void foo(struct __sk_buff *skb)
+@@ -11,7 +12,8 @@ void foo(struct __sk_buff *skb)
+ }
+ SEC("tc")
+-int test_cls(struct __sk_buff *skb)
++__failure __msg("foo() doesn't return scalar")
++int global_func7(struct __sk_buff *skb)
+ {
+       foo(skb);
+       return 0;
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func8.c b/tools/testing/selftests/bpf/progs/test_global_func8.c
+index d55a6544b1abd2..9b9c57fa2dd34b 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func8.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func8.c
+@@ -3,6 +3,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ __noinline int foo(struct __sk_buff *skb)
+ {
+@@ -10,7 +11,8 @@ __noinline int foo(struct __sk_buff *skb)
+ }
+ SEC("cgroup_skb/ingress")
+-int test_cls(struct __sk_buff *skb)
++__success
++int global_func8(struct __sk_buff *skb)
+ {
+       if (!foo(skb))
+               return 0;
+diff --git a/tools/testing/selftests/bpf/progs/test_global_func9.c b/tools/testing/selftests/bpf/progs/test_global_func9.c
+index bd233ddede98ae..1f2cb0159b8d83 100644
+--- a/tools/testing/selftests/bpf/progs/test_global_func9.c
++++ b/tools/testing/selftests/bpf/progs/test_global_func9.c
+@@ -2,6 +2,7 @@
+ #include <stddef.h>
+ #include <linux/bpf.h>
+ #include <bpf/bpf_helpers.h>
++#include "bpf_misc.h"
+ struct S {
+       int x;
+@@ -74,7 +75,8 @@ __noinline int quuz(int **p)
+ }
+ SEC("cgroup_skb/ingress")
+-int test_cls(struct __sk_buff *skb)
++__success
++int global_func9(struct __sk_buff *skb)
+ {
+       int result = 0;
+-- 
+2.53.0
+
diff --git a/queue-6.1/selftests-bpf-enhance-align-selftest-s-expected-log-.patch b/queue-6.1/selftests-bpf-enhance-align-selftest-s-expected-log-.patch
new file mode 100644 (file)
index 0000000..939e1ec
--- /dev/null
@@ -0,0 +1,66 @@
+From 600bb6b05ff2caef1797b6532ff200c786083f65 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:41:34 +0200
+Subject: selftests/bpf: enhance align selftest's expected log matching
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 6f876e75d316a75957f3d43c3a8c2a6fe9bc18b2 ]
+
+Allow to search for expected register state in all the verifier log
+output that's related to specified instruction number.
+
+See added comment for an example of possible situation that is happening
+due to a simple enhancement done in the next patch, which fixes handling
+of env->test_state_freq flag in state checkpointing logic.
+
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20230302235015.2044271-4-andrii@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+[ Note: Backport needed to fix the align selftest where some of the
+  expected log messages can't be found. This is happening because
+  commit 1a8a315f008a ("bpf: Ensure proper register state printing for
+  cond jumps") was also backported to 6.1. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/bpf/prog_tests/align.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/tools/testing/selftests/bpf/prog_tests/align.c b/tools/testing/selftests/bpf/prog_tests/align.c
+index 8baebb41541dc7..b9277059256300 100644
+--- a/tools/testing/selftests/bpf/prog_tests/align.c
++++ b/tools/testing/selftests/bpf/prog_tests/align.c
+@@ -660,16 +660,22 @@ static int do_test_single(struct bpf_align_test *test)
+                        * func#0 @0
+                        * 0: R1=ctx(off=0,imm=0) R10=fp0
+                        * 0: (b7) r3 = 2                 ; R3_w=2
++                       *
++                       * Sometimes it's actually two lines below, e.g. when
++                       * searching for "6: R3_w=scalar(umax=255,var_off=(0x0; 0xff))":
++                       *   from 4 to 6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0
++                       *   6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0
++                       *   6: (71) r3 = *(u8 *)(r2 +0)           ; R2_w=pkt(off=0,r=8,imm=0) R3_w=scalar(umax=255,var_off=(0x0; 0xff))
+                        */
+-                      if (!strstr(line_ptr, m.match)) {
++                      while (!strstr(line_ptr, m.match)) {
+                               cur_line = -1;
+                               line_ptr = strtok(NULL, "\n");
+-                              sscanf(line_ptr, "%u: ", &cur_line);
++                              sscanf(line_ptr ?: "", "%u: ", &cur_line);
++                              if (!line_ptr || cur_line != m.line)
++                                      break;
+                       }
+-                      if (cur_line != m.line || !line_ptr ||
+-                          !strstr(line_ptr, m.match)) {
+-                              printf("Failed to find match %u: %s\n",
+-                                     m.line, m.match);
++                      if (cur_line != m.line || !line_ptr || !strstr(line_ptr, m.match)) {
++                              printf("Failed to find match %u: %s\n", m.line, m.match);
+                               ret = 1;
+                               printf("%s", bpf_vlog);
+                               break;
+-- 
+2.53.0
+
diff --git a/queue-6.1/selftests-bpf-fix-arg_ptr_to_long-half-uninitialized.patch b/queue-6.1/selftests-bpf-fix-arg_ptr_to_long-half-uninitialized.patch
new file mode 100644 (file)
index 0000000..d704ea0
--- /dev/null
@@ -0,0 +1,57 @@
+From 0d9262b00e2d1f3dc00e7e9252603450556be798 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:41:55 +0200
+Subject: selftests/bpf: Fix ARG_PTR_TO_LONG {half-,}uninitialized test
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+[ Upstream commit b8e188f023e07a733b47d5865311ade51878fe40 ]
+
+The assumption of 'in privileged mode reads from uninitialized stack locations
+are permitted' is not quite correct since the verifier was probing for read
+access rather than write access. Both tests need to be annotated as __success
+for privileged and unprivileged.
+
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20240913191754.13290-6-daniel@iogearbox.net
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+[ Note: The format of logs completely changed since 6.1 so this change
+  had to be reapplied to the old test file. This commit needs to be
+  backported because it fixes a test broken by commit 32556ce93bc4
+  ("bpf: Fix helper writes to read-only maps") from the same patchset. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/bpf/verifier/int_ptr.c | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+diff --git a/tools/testing/selftests/bpf/verifier/int_ptr.c b/tools/testing/selftests/bpf/verifier/int_ptr.c
+index 02d9e004260b33..8c74cff2090364 100644
+--- a/tools/testing/selftests/bpf/verifier/int_ptr.c
++++ b/tools/testing/selftests/bpf/verifier/int_ptr.c
+@@ -25,9 +25,8 @@
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+-      .result = REJECT,
++      .result = ACCEPT,
+       .prog_type = BPF_PROG_TYPE_CGROUP_SYSCTL,
+-      .errstr = "invalid indirect read from stack R4 off -16+0 size 8",
+ },
+ {
+       "ARG_PTR_TO_LONG half-uninitialized",
+@@ -57,9 +56,6 @@
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+-      .result_unpriv = REJECT,
+-      .errstr_unpriv = "invalid indirect read from stack R4 off -16+4 size 8",
+-      /* in privileged mode reads from uninitialized stack locations are permitted */
+       .result = ACCEPT,
+ },
+ {
+-- 
+2.53.0
+
diff --git a/queue-6.1/selftests-bpf-s-iptables-iptables-legacy-in-the-bpf_.patch b/queue-6.1/selftests-bpf-s-iptables-iptables-legacy-in-the-bpf_.patch
new file mode 100644 (file)
index 0000000..fb7169c
--- /dev/null
@@ -0,0 +1,84 @@
+From 16be54df6956e1b0372b80ae057e7b3675dc72a3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:42:04 +0200
+Subject: selftests/bpf: S/iptables/iptables-legacy/ in the bpf_nf and
+ xdp_synproxy test
+
+From: Martin KaFai Lau <martin.lau@kernel.org>
+
+[ Upstream commit de9c8d848d90cf2e53aced50b350827442ca5a4f ]
+
+The recent vm image in CI has reported error in selftests that use
+the iptables command.  Manu Bretelle has pointed out the difference
+in the recent vm image that the iptables is sym-linked to the iptables-nft.
+With this knowledge,  I can also reproduce the CI error by manually running
+with the 'iptables-nft'.
+
+This patch is to replace the iptables command with iptables-legacy
+to unblock the CI tests.
+
+Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Acked-by: David Vernet <void@manifault.com>
+Link: https://lore.kernel.org/bpf/20221012221235.3529719-1-martin.lau@linux.dev
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/bpf/prog_tests/bpf_nf.c       | 6 +++---
+ tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c | 6 +++---
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
+index b2998896f9f7bc..b30ff6b3b81ae3 100644
+--- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
++++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
+@@ -49,14 +49,14 @@ static int connect_to_server(int srv_fd)
+ static void test_bpf_nf_ct(int mode)
+ {
+-      const char *iptables = "iptables -t raw %s PREROUTING -j CONNMARK --set-mark 42/0";
++      const char *iptables = "iptables-legacy -t raw %s PREROUTING -j CONNMARK --set-mark 42/0";
+       int srv_fd = -1, client_fd = -1, srv_client_fd = -1;
+       struct sockaddr_in peer_addr = {};
+       struct test_bpf_nf *skel;
+       int prog_fd, err;
+       socklen_t len;
+       u16 srv_port;
+-      char cmd[64];
++      char cmd[128];
+       LIBBPF_OPTS(bpf_test_run_opts, topts,
+               .data_in = &pkt_v4,
+               .data_size_in = sizeof(pkt_v4),
+@@ -69,7 +69,7 @@ static void test_bpf_nf_ct(int mode)
+       /* Enable connection tracking */
+       snprintf(cmd, sizeof(cmd), iptables, "-A");
+-      if (!ASSERT_OK(system(cmd), "iptables"))
++      if (!ASSERT_OK(system(cmd), cmd))
+               goto end;
+       srv_port = (mode == TEST_XDP) ? 5005 : 5006;
+diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c
+index 879f5da2f21e63..13daa3746064af 100644
+--- a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c
++++ b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c
+@@ -94,12 +94,12 @@ static void test_synproxy(bool xdp)
+       SYS("sysctl -w net.ipv4.tcp_syncookies=2");
+       SYS("sysctl -w net.ipv4.tcp_timestamps=1");
+       SYS("sysctl -w net.netfilter.nf_conntrack_tcp_loose=0");
+-      SYS("iptables -t raw -I PREROUTING \
++      SYS("iptables-legacy -t raw -I PREROUTING \
+           -i tmp1 -p tcp -m tcp --syn --dport 8080 -j CT --notrack");
+-      SYS("iptables -t filter -A INPUT \
++      SYS("iptables-legacy -t filter -A INPUT \
+           -i tmp1 -p tcp -m tcp --dport 8080 -m state --state INVALID,UNTRACKED \
+           -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460");
+-      SYS("iptables -t filter -A INPUT \
++      SYS("iptables-legacy -t filter -A INPUT \
+           -i tmp1 -m state --state INVALID -j DROP");
+       ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 \
+-- 
+2.53.0
+
diff --git a/queue-6.1/selftests-bpf-update-bpf_clone_redirect-expected-ret.patch b/queue-6.1/selftests-bpf-update-bpf_clone_redirect-expected-ret.patch
new file mode 100644 (file)
index 0000000..c6810ff
--- /dev/null
@@ -0,0 +1,90 @@
+From 8d982a71260603aa8c9bc5df1238aa3add555921 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 21:41:25 +0200
+Subject: selftests/bpf: Update bpf_clone_redirect expected return code
+
+From: Stanislav Fomichev <sdf@google.com>
+
+[ Upstream commit b772b70b69046c5b76e3f2eda680f692dee5e6d5 ]
+
+Commit 151e887d8ff9 ("veth: Fixing transmit return status for dropped
+packets") started propagating proper NET_XMIT_DROP error to the caller
+which means it's now possible to get positive error code when calling
+bpf_clone_redirect() in this particular test. Update the test to reflect
+that.
+
+Reported-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Stanislav Fomichev <sdf@google.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Link: https://lore.kernel.org/bpf/20230911194731.286342-2-sdf@google.com
+[ Note: Commit 151e887d8ff9 was backported to 6.1 so this fix should be
+  as well. ]
+Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
+Acked-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/bpf/prog_tests/empty_skb.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c
+index 0613f3bb8b5e4e..329e34e5226e3a 100644
+--- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c
++++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c
+@@ -29,6 +29,7 @@ void serial_test_empty_skb(void)
+               int *ifindex;
+               int err;
+               int ret;
++              int lwt_egress_ret; /* expected retval at lwt/egress */
+               bool success_on_tc;
+       } tests[] = {
+               /* Empty packets are always rejected. */
+@@ -62,6 +63,7 @@ void serial_test_empty_skb(void)
+                       .data_size_in = sizeof(eth_hlen),
+                       .ifindex = &veth_ifindex,
+                       .ret = -ERANGE,
++                      .lwt_egress_ret = -ERANGE,
+                       .success_on_tc = true,
+               },
+               {
+@@ -75,6 +77,7 @@ void serial_test_empty_skb(void)
+                       .data_size_in = sizeof(eth_hlen),
+                       .ifindex = &ipip_ifindex,
+                       .ret = -ERANGE,
++                      .lwt_egress_ret = -ERANGE,
+               },
+               /* ETH_HLEN+1-sized packet should be redirected. */
+@@ -84,6 +87,7 @@ void serial_test_empty_skb(void)
+                       .data_in = eth_hlen_pp,
+                       .data_size_in = sizeof(eth_hlen_pp),
+                       .ifindex = &veth_ifindex,
++                      .lwt_egress_ret = 1, /* veth_xmit NET_XMIT_DROP */
+               },
+               {
+                       .msg = "ipip ETH_HLEN+1 packet ingress",
+@@ -113,8 +117,12 @@ void serial_test_empty_skb(void)
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
+               bpf_object__for_each_program(prog, bpf_obj->obj) {
+-                      char buf[128];
++                      bool at_egress = strstr(bpf_program__name(prog), "egress") != NULL;
+                       bool at_tc = !strncmp(bpf_program__section_name(prog), "tc", 2);
++                      int expected_ret;
++                      char buf[128];
++
++                      expected_ret = at_egress && !at_tc ? tests[i].lwt_egress_ret : tests[i].ret;
+                       tattr.data_in = tests[i].data_in;
+                       tattr.data_size_in = tests[i].data_size_in;
+@@ -133,7 +141,7 @@ void serial_test_empty_skb(void)
+                       if (at_tc && tests[i].success_on_tc)
+                               ASSERT_GE(bpf_obj->bss->ret, 0, buf);
+                       else
+-                              ASSERT_EQ(bpf_obj->bss->ret, tests[i].ret, buf);
++                              ASSERT_EQ(bpf_obj->bss->ret, expected_ret, buf);
+               }
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.1/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch b/queue-6.1/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch
new file mode 100644 (file)
index 0000000..c0bd9a8
--- /dev/null
@@ -0,0 +1,108 @@
+From 5c9a34d2e3f45d48ca596c75bd1b37d8dbb77bd4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 13:33:34 +0200
+Subject: selftests: forwarding: lib: Add helpers for checksum handling
+
+From: Petr Machata <petrm@nvidia.com>
+
+commit 952e0ee38c7215c45192d8c899acd1830873f28b upstream.
+
+In order to generate IGMPv3 and MLDv2 packets on the fly, we will need
+helpers to calculate the packet checksum.
+
+The approach presented in this patch revolves around payload templates
+for mausezahn. These are mausezahn-like payload strings (01:23:45:...)
+with possibly one 2-byte sequence replaced with the word PAYLOAD. The
+main function is payload_template_calc_checksum(), which calculates
+RFC 1071 checksum of the message. There are further helpers to then
+convert the checksum to the payload format, and to expand it.
+
+For IPv6, MLDv2 message checksum is computed using a pseudoheader that
+differs from the header used in the payload itself. The fact that the
+two messages are different means that the checksum needs to be
+returned as a separate quantity, instead of being expanded in-place in
+the payload itself. Furthermore, the pseudoheader includes a length of
+the message. Much like the checksum, this needs to be expanded in
+mausezahn format. And likewise for number of addresses for (S,G)
+entries. Thus we have several places where a computed quantity needs
+to be presented in the payload format. Add a helper u16_to_bytes(),
+which will be used in all these cases.
+
+Signed-off-by: Petr Machata <petrm@nvidia.com>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 02cb2e6bacbb ("selftests: forwarding: vxlan_bridge_1d: fix test failure with br_netfilter enabled")
+[bwh: Backported to 6,1: adjust context]
+Signed-off-by: Ben Hutchings <benh@debian.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/net/forwarding/lib.sh | 56 +++++++++++++++++++
+ 1 file changed, 56 insertions(+)
+
+diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
+index 06027772cf79af..48d913341af267 100755
+--- a/tools/testing/selftests/net/forwarding/lib.sh
++++ b/tools/testing/selftests/net/forwarding/lib.sh
+@@ -1701,3 +1701,59 @@ hw_stats_monitor_test()
+       log_test "${type}_stats notifications"
+ }
++
++u16_to_bytes()
++{
++      local u16=$1; shift
++
++      printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'
++}
++
++# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),
++# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,
++# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)
++# stands for 00:00.
++payload_template_calc_checksum()
++{
++      local payload=$1; shift
++
++      (
++          # Set input radix.
++          echo "16i"
++          # Push zero for the initial checksum.
++          echo 0
++
++          # Pad the payload with a terminating 00: in case we get an odd
++          # number of bytes.
++          echo "${payload%:}:00:" |
++              sed 's/CHECKSUM/00:00/g' |
++              tr '[:lower:]' '[:upper:]' |
++              # Add the word to the checksum.
++              sed 's/\(..\):\(..\):/\1\2+\n/g' |
++              # Strip the extra odd byte we pushed if left unconverted.
++              sed 's/\(..\):$//'
++
++          echo "10000 ~ +"    # Calculate and add carry.
++          echo "FFFF r - p"   # Bit-flip and print.
++      ) |
++          dc |
++          tr '[:upper:]' '[:lower:]'
++}
++
++payload_template_expand_checksum()
++{
++      local payload=$1; shift
++      local checksum=$1; shift
++
++      local ckbytes=$(u16_to_bytes $checksum)
++
++      echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"
++}
++
++payload_template_nbytes()
++{
++      local payload=$1; shift
++
++      payload_template_expand_checksum "${payload%:}" 0 |
++              sed 's/:/\n/g' | wc -l
++}
+-- 
+2.53.0
+
index 460625468f09e9718044f627c6edb5b27073bf64..d8b5c07ddbe65b76739979f32773c5787c84e344 100644 (file)
@@ -39,3 +39,37 @@ sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch
 ipv6-fix-possible-infinite-loop-in-rt6_fill_node.patch
 ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch
 net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch
+batman-adv-v-stop-ogmv2-on-disabled-interface.patch
+batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
+batman-adv-tt-reject-oversized-local-tvlv-buffers.patch
+batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
+batman-adv-tvlv-reject-oversized-tvlv-packets.patch
+batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
+selftests-forwarding-lib-add-helpers-for-checksum-ha.patch
+batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch
+batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
+batman-adv-tt-avoid-empty-vlan-responses.patch
+batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
+mm-page_alloc-clear-page-private-in-free_pages_prepa.patch
+selftests-bpf-add-generic-bpf-program-tester-loader.patch
+selftests-bpf-convert-test_global_funcs-test-to-test.patch
+revert-selftests-bpf-workaround-strict-bpf_lsm-retur.patch
+revert-selftests-bpf-add-tests-for-_opts-variants-of.patch
+selftests-bpf-add-read_build_id-function.patch
+bpf-fix-a-few-selftest-failures-due-to-llvm18-change.patch
+selftests-bpf-update-bpf_clone_redirect-expected-ret.patch
+selftests-bpf-enhance-align-selftest-s-expected-log-.patch
+revert-selftests-bpf-add-a-cgroup-prog-bpf_get_ns_cu.patch
+selftests-bpf-fix-arg_ptr_to_long-half-uninitialized.patch
+selftests-bpf-s-iptables-iptables-legacy-in-the-bpf_.patch
+net-packet-convert-po-tp_tx_has_off-to-an-atomic-fla.patch
+net-packet-convert-po-tp_loss-to-an-atomic-flag.patch
+net-packet-convert-po-has_vnet_hdr-to-an-atomic-flag.patch
+net-packet-convert-po-running-to-an-atomic-flag.patch
+net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpa.patch
+drm-i915-psr-add-defininitions-for-intel_wa_register.patch
+drm-dp-add-edp-1.5-bit-definition.patch
+drm-i915-psr-read-intel-dpcd-workaround-register.patch
+drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
+revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch
+rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-25226
diff --git a/queue-6.12/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-6.12/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
new file mode 100644 (file)
index 0000000..579d94a
--- /dev/null
@@ -0,0 +1,232 @@
+From 963533f502c669c85cee2e202682c19d4be6663b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:10:42 +0200
+Subject: batman-adv: bla: avoid double decrement of bla.num_requests
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 83ab69bd12b80f6ea169c8bea6977701b53a043d upstream.
+
+The bla.num_requests is increased when no request_sent was in progress. And
+it is decremented in various places (announcement was received, backbone is
+purged, periodic work). But the check if the request_sent is actually set
+to a specific state and the atomic_dec/_inc are not safe because they are
+not atomic (TOCTOU) and multiple such code portions can run concurrently.
+
+At the same time, it is necessary to modify request_sent (state) and
+bla.num_requests atomically. Otherwise batadv_bla_send_request() might set
+request_sent to 1 and is interrupted.  batadv_handle_announce() can then
+set request_sent back to 0 and decrement num_requests before
+batadv_bla_send_request() incremented it.
+
+The two operations must therefore be locked. And since state (request_sent)
+and wait_periods are only accessed inside this lock, they can be converted
+to simpler datatypes. And to avoid that the bla.num_requests is touched by
+a parallel running context with a valid backbone_gw reference after
+batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to
+correctly signal that a backbone_gw is in the state of being cleaned up.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++--------
+ net/batman-adv/soft-interface.c        |  1 +
+ net/batman-adv/types.h                 | 39 ++++++++++++++++----
+ 3 files changed, 67 insertions(+), 24 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index 17db4191ddb39e..15aeb07285e61f 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -516,8 +516,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+-      atomic_set(&entry->request_sent, 0);
+-      atomic_set(&entry->wait_periods, 0);
++      entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
++      entry->wait_periods = 0;
+       ether_addr_copy(entry->orig, orig);
+       INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
+       kref_init(&entry->refcount);
+@@ -546,9 +546,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+               batadv_bla_send_announce(bat_priv, entry);
+               /* this will be decreased in the worker thread */
+-              atomic_inc(&entry->request_sent);
+-              atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
+-              atomic_inc(&bat_priv->bla.num_requests);
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++                      entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
++                      entry->wait_periods = BATADV_BLA_WAIT_PERIODS;
++                      atomic_inc(&bat_priv->bla.num_requests);
++              }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       return entry;
+@@ -651,10 +655,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw)
+                             backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);
+       /* no local broadcasts should be sent or received, for now. */
+-      if (!atomic_read(&backbone_gw->request_sent)) {
++      spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
++      if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++              backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
+               atomic_inc(&backbone_gw->bat_priv->bla.num_requests);
+-              atomic_set(&backbone_gw->request_sent, 1);
+       }
++      spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
+ }
+ /**
+@@ -875,10 +881,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               /* if we have sent a request and the crc was OK,
+                * we can allow traffic again.
+                */
+-              if (atomic_read(&backbone_gw->request_sent)) {
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) {
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
+               }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       batadv_backbone_gw_put(backbone_gw);
+@@ -1257,9 +1265,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                               purged = true;
+                               /* don't wait for the pending request anymore */
+-                              if (atomic_read(&backbone_gw->request_sent))
++                              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED)
+                                       atomic_dec(&bat_priv->bla.num_requests);
++                              backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
++                              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
++
+                               batadv_bla_del_backbone_claims(backbone_gw);
+                               hlist_del_rcu(&backbone_gw->hash_entry);
+@@ -1510,7 +1522,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                               batadv_bla_send_loopdetect(bat_priv,
+                                                          backbone_gw);
+-                      /* request_sent is only set after creation to avoid
++                      /* state is only set to unsynced after creation to avoid
+                        * problems when we are not yet known as backbone gw
+                        * in the backbone.
+                        *
+@@ -1519,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                        * some grace time.
+                        */
+-                      if (atomic_read(&backbone_gw->request_sent) == 0)
+-                              continue;
++                      spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                      if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
++                              goto unlock_next;
+-                      if (!atomic_dec_and_test(&backbone_gw->wait_periods))
+-                              continue;
++                      if (backbone_gw->wait_periods > 0)
++                              backbone_gw->wait_periods--;
++
++                      if (backbone_gw->wait_periods > 0)
++                              goto unlock_next;
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
++
++unlock_next:
++                      spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+               }
+               rcu_read_unlock();
+       }
+diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
+index 2758aba47a2f25..f46064333f3343 100644
+--- a/net/batman-adv/soft-interface.c
++++ b/net/batman-adv/soft-interface.c
+@@ -787,6 +787,7 @@ static int batadv_softif_init_late(struct net_device *dev)
+       atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
+ #ifdef CONFIG_BATMAN_ADV_BLA
+       atomic_set(&bat_priv->bla.num_requests, 0);
++      spin_lock_init(&bat_priv->bla.num_requests_lock);
+ #endif
+       atomic_set(&bat_priv->tp_num, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 0ccd55b251fa26..f703d266780d74 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1091,6 +1091,12 @@ struct batadv_priv_bla {
+       /** @num_requests: number of bla requests in flight */
+       atomic_t num_requests;
++      /**
++       * @num_requests_lock: locks update num_requests +
++       * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update
++       */
++      spinlock_t num_requests_lock;
++
+       /**
+        * @claim_hash: hash table containing mesh nodes this host has claimed
+        */
+@@ -1825,6 +1831,27 @@ struct batadv_priv {
+ #ifdef CONFIG_BATMAN_ADV_BLA
++enum batadv_bla_backbone_gw_state {
++      /**
++       * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed
++       * and it must not longer work on requests
++       */
++      BATADV_BLA_BACKBONE_GW_STOPPED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out
++       * of sync and a request was send. No traffic is forwarded until the
++       * situation is resolved
++       */
++      BATADV_BLA_BACKBONE_GW_UNSYNCED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in
++       * sync. traffic can be forwarded
++       */
++      BATADV_BLA_BACKBONE_GW_SYNCED,
++};
++
+ /**
+  * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
+  */
+@@ -1850,16 +1877,12 @@ struct batadv_bla_backbone_gw {
+       /**
+        * @wait_periods: grace time for bridge forward delays and bla group
+        *  forming at bootup phase - no bcast traffic is formwared until it has
+-       *  elapsed
++       *  elapsed. Must only be access with num_requests_lock.
+        */
+-      atomic_t wait_periods;
++      u8 wait_periods;
+-      /**
+-       * @request_sent: if this bool is set to true we are out of sync with
+-       *  this backbone gateway - no bcast traffic is formwared until the
+-       *  situation was resolved
+-       */
+-      atomic_t request_sent;
++      /** @state: sync state. Must only be access with num_requests_lock. */
++      enum batadv_bla_backbone_gw_state state;
+       /** @crc: crc16 checksum over all claims */
+       u16 crc;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-6.12/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
new file mode 100644 (file)
index 0000000..b41f387
--- /dev/null
@@ -0,0 +1,53 @@
+From 9dfb7ab4aa826246f27d18f2d57a9c192d0110a4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:11:25 +0200
+Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 upstream.
+
+Without rtnl_lock held, a hardif might be retrieved as primary interface of
+a meshif, but then (while operating on this interface) getting decoupled
+from the mesh interface. In this case, the meshif still exists but the
+pointer from the primary hardif to the meshif is set to NULL.
+
+The mesh_iface must be checked first to be non-NULL before continuing to
+send an ARP request using meshif.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Reported-by: Ido Schimmel <idosch@nvidia.com>
+Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index e77f3ef3d7336a..17db4191ddb39e 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
+              sizeof(local_claim_dest));
+       local_claim_dest.type = claimtype;
+-      soft_iface = primary_if->soft_iface;
++      soft_iface = READ_ONCE(primary_if->soft_iface);
++      if (!soft_iface)
++              goto out;
+       skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
+                        /* IP DST: 0.0.0.0 */
+                        zeroip,
+-                       primary_if->soft_iface,
++                       soft_iface,
+                        /* IP SRC: 0.0.0.0 */
+                        zeroip,
+                        /* Ethernet DST: Broadcast */
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-6.12/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
new file mode 100644 (file)
index 0000000..deff785
--- /dev/null
@@ -0,0 +1,230 @@
+From 1b885ca819cefa038080fb411f5d2a3f012c2d5b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:10:00 +0200
+Subject: batman-adv: iv: recover OGM scheduling after forward packet error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit aa3153bd139a6c48667dcd02608d3b2c80bff02c upstream.
+
+When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward
+packet for OGM transmission, the work item that drives periodic OGM
+scheduling is never re-armed. This silently halts transmission of the
+node's own OGMs on the affected interface — only OGMs from other peers
+continue to be aggregated and forwarded.
+
+Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively
+batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet.
+When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing
+a dedicated recovery work item (reschedule_work) that fires after one
+originator interval and calls batadv_iv_ogm_schedule() again.
+
+Cc: stable@kernel.org
+Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++----------
+ net/batman-adv/types.h      |  3 ++
+ 2 files changed, 60 insertions(+), 19 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 42b687c1a76807..b37c9fb178ae50 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -223,6 +223,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_iv.ogm_buff = NULL;
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
++
++      cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
+ }
+ static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
+@@ -527,8 +529,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
+  * @if_incoming: interface where the packet was received
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
++static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                                       int packet_len, unsigned long send_time,
+                                       bool direct_link,
+                                       struct batadv_hard_iface *if_incoming,
+@@ -552,13 +556,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+       skb = netdev_alloc_skb_ip_align(NULL, skb_size);
+       if (!skb)
+-              return;
++              return false;
+       forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
+                                                   queue_left, bat_priv, skb);
+       if (!forw_packet_aggr) {
+               kfree_skb(skb);
+-              return;
++              return false;
+       }
+       forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
+@@ -580,6 +584,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                         batadv_iv_send_outstanding_bat_ogm_packet);
+       batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
++
++      return true;
+ }
+ /* aggregate a new packet into the existing ogm packet */
+@@ -609,8 +615,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
+  * @send_time: timestamp (jiffies) when the packet is to be sent
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
++static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+                                   unsigned char *packet_buff,
+                                   int packet_len,
+                                   struct batadv_hard_iface *if_incoming,
+@@ -662,14 +670,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+               if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
+                       send_time += max_aggregation_jiffies;
+-              batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
+-                                          send_time, direct_link,
+-                                          if_incoming, if_outgoing,
+-                                          own_packet);
++              return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
++                                                 send_time, direct_link,
++                                                 if_incoming, if_outgoing,
++                                                 own_packet);
+       } else {
+               batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
+                                       packet_len, direct_link);
+               spin_unlock_bh(&bat_priv->forw_bat_list_lock);
++
++              return true;
+       }
+ }
+@@ -781,6 +791,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      bool reschedule = false;
++      bool scheduled;
+       int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -809,11 +821,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                                                      ogm_buff_len,
+                                                      BATADV_OGM_HLEN);
+               if (ret < 0) {
+-                      /* OGMs must be queued even when the buffer allocation for
+-                       * TVLVs failed. just fall back to the non-TVLV version
+-                       */
+-                      ret = 0;
+-                      *ogm_buff_len = BATADV_OGM_HLEN;
++                      reschedule = true;
++                      goto out;
+               }
+               tvlv_len = ret;
+@@ -835,8 +844,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               /* OGMs from secondary interfaces are only scheduled on their
+                * respective interfaces.
+                */
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
+-                                      hard_iface, hard_iface, 1, send_time);
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
++                                                  hard_iface, hard_iface, 1, send_time);
++              if (!scheduled)
++                      reschedule = true;
++
+               goto out;
+       }
+@@ -851,15 +863,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
+                       continue;
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
+-                                      *ogm_buff_len, hard_iface,
+-                                      tmp_hard_iface, 1, send_time);
+-
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
++                                                  *ogm_buff_len, hard_iface,
++                                                  tmp_hard_iface, 1, send_time);
+               batadv_hardif_put(tmp_hard_iface);
++
++              if (!scheduled && tmp_hard_iface == hard_iface)
++                      reschedule = true;
+       }
+       rcu_read_unlock();
+ out:
++      if (reschedule) {
++              /* there was a failure scheduling the own forward packet.
++               * as result, the batadv_iv_send_outstanding_bat_ogm_packet()
++               * work item is no longer scheduled. it is therefore necessary
++               * to reschedule it manually
++               */
++              queue_delayed_work(batadv_event_workqueue,
++                                 &hard_iface->bat_iv.reschedule_work,
++                                 msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
++      }
++
+       batadv_hardif_put(primary_if);
+ }
+@@ -874,6 +899,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+ }
++static void batadv_iv_ogm_reschedule(struct work_struct *work)
++{
++      struct delayed_work *delayed_work = to_delayed_work(work);
++      struct batadv_hard_iface *hard_iface;
++
++      hard_iface = container_of(delayed_work,
++                                struct batadv_hard_iface,
++                                bat_iv.reschedule_work);
++      batadv_iv_ogm_schedule(hard_iface);
++}
++
+ /**
+  * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
+  * @orig_node: originator which reproadcasted the OGMs directly
+@@ -2277,6 +2313,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
+ static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
+ {
++      INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
++
+       /* begin scheduling originator messages on that interface */
+       batadv_iv_ogm_schedule(hard_iface);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index fe774ec8b80b75..0ccd55b251fa26 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv {
+       /** @ogm_seqno: OGM sequence number - used to identify each OGM */
+       atomic_t ogm_seqno;
++      /** @reschedule_work: recover OGM schedule after schedule error */
++      struct delayed_work reschedule_work;
++
+       /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
+       struct mutex ogm_buff_mutex;
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch b/queue-6.12/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch
new file mode 100644 (file)
index 0000000..caff854
--- /dev/null
@@ -0,0 +1,196 @@
+From 58fa179914a593328458ad3eb28c4b2e65f7c786 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:09:05 +0200
+Subject: batman-adv: tp_meter: avoid role confusion in tp_list
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit ff24f2ecfd94c07a2b89bac497433e3b23271cac upstream.
+
+Session lookups in tp_list matched only on destination address (and
+optionally session ID), leaving role validation to the caller. If two
+sessions with the same other_end coexisted (one as sender, one as receiver)
+a lookup could silently return the wrong one, causing the caller's role to
+bail out early, potentially skipping necessary cleanup.
+
+Move the role check into the lookup functions themselves so the correct
+entry is always returned, or none at all. Since batadv_tp_start()
+legitimately needs to detect any active session to a destination regardless
+of role, introduce a dedicated helper for that case rather than bending the
+existing lookup semantics.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++---------------
+ 1 file changed, 36 insertions(+), 23 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 04a83d6be45bc0..bc3dc377f0bfd0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
+  * batadv_tp_list_find() - find a tp_vars object in the global list
+  * @bat_priv: the bat priv with all the soft interface information
+  * @dst: the other endpoint MAC address to look for
++ * @role: role of the session
+  *
+  * Look for a tp_vars object matching dst as end_point and return it after
+  * having increment the refcounter. Return NULL is not found
+@@ -262,7 +263,8 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
+  * Return: matching tp_vars or NULL when no tp_vars with @dst was found
+  */
+ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+-                                                const u8 *dst)
++                                                const u8 *dst,
++                                                enum batadv_tp_meter_role role)
+ {
+       struct batadv_tp_vars *pos, *tp_vars = NULL;
+@@ -271,6 +273,9 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(pos->other_end, dst))
+                       continue;
++              if (pos->role != role)
++                      continue;
++
+               /* most of the time this function is invoked during the normal
+                * process..it makes sens to pay more when the session is
+                * finished and to speed the process up during the measurement
+@@ -286,12 +291,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+       return tp_vars;
+ }
++/**
++ * batadv_tp_list_active() - check if session from/to destination is ongoing
++ * @bat_priv: the bat priv with all the mesh interface information
++ * @dst: the other endpoint MAC address to look for
++ *
++ * Return: if matching session with @dst was found
++ */
++static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
++      __must_hold(&bat_priv->tp_list_lock)
++{
++      struct batadv_tp_vars *tp_vars;
++
++      hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) {
++              if (batadv_compare_eth(tp_vars->other_end, dst))
++                      return true;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tp_list_find_session() - find tp_vars session object in the global
+  *  list
+  * @bat_priv: the bat priv with all the soft interface information
+  * @dst: the other endpoint MAC address to look for
+  * @session: session identifier
++ * @role: role of the session
+  *
+  * Look for a tp_vars object matching dst as end_point, session as tp meter
+  * session and return it after having increment the refcounter. Return NULL
+@@ -301,7 +327,7 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+  */
+ static struct batadv_tp_vars *
+ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
+-                          const u8 *session)
++                          const u8 *session, enum batadv_tp_meter_role role)
+ {
+       struct batadv_tp_vars *pos, *tp_vars = NULL;
+@@ -313,6 +339,9 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
+               if (memcmp(pos->session, session, sizeof(pos->session)) != 0)
+                       continue;
++              if (pos->role != role)
++                      continue;
++
+               /* most of the time this function is invoked during the normal
+                * process..it makes sense to pay more when the session is
+                * finished and to speed the process up during the measurement
+@@ -671,13 +700,10 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+       /* find the tp_vars */
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                            icmp->session);
++                                            icmp->session, BATADV_TP_SENDER);
+       if (unlikely(!tp_vars))
+               return;
+-      if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+-              goto out;
+-
+       if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+               goto out;
+@@ -986,10 +1012,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+               return;
+       }
+-      tp_vars = batadv_tp_list_find(bat_priv, dst);
+-      if (tp_vars) {
++      if (batadv_tp_list_active(bat_priv, dst)) {
+               spin_unlock_bh(&bat_priv->tp_list_lock);
+-              batadv_tp_vars_put(tp_vars);
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                          "Meter: test to or from the same node already ongoing, aborting\n");
+               batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING,
+@@ -1110,18 +1134,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
+       if (!orig_node)
+               return;
+-      tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig);
++      tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER);
+       if (!tp_vars) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                          "Meter: trying to interrupt an already over connection\n");
+               goto out_put_orig_node;
+       }
+-      if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+-              goto out_put_tp_vars;
+-
+       batadv_tp_sender_shutdown(tp_vars, return_value);
+-out_put_tp_vars:
+       batadv_tp_vars_put(tp_vars);
+ out_put_orig_node:
+       batadv_orig_node_put(orig_node);
+@@ -1377,7 +1397,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+               goto out_unlock;
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                            icmp->session);
++                                            icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars)
+               goto out_unlock;
+@@ -1448,7 +1468,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               }
+       } else {
+               tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                                    icmp->session);
++                                                    icmp->session, BATADV_TP_RECEIVER);
+               if (!tp_vars) {
+                       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                                  "Unexpected packet from %pM!\n",
+@@ -1457,13 +1477,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               }
+       }
+-      if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) {
+-              batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+-                         "Meter: dropping packet: not expected (role=%u)\n",
+-                         tp_vars->role);
+-              goto out;
+-      }
+-
+       tp_vars->last_recv_time = jiffies;
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/queue-6.12/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch
new file mode 100644 (file)
index 0000000..b6a3c8f
--- /dev/null
@@ -0,0 +1,60 @@
+From a5dbd1e85a7af01c6569e23d6e66a8954288c2ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 22:21:35 +0200
+Subject: batman-adv: tp_meter: directly shut down timer on cleanup
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d5487249a81ea658717614009c8f46acc5b7101a upstream.
+
+batadv_tp_sender_cleanup() was calling timer_delete_sync() followed by
+timer_delete() to guard against the timer handler re-arming itself between
+the two calls. This double-deletion hack relied on the sending status being
+set to 0 to suppress re-arming.
+
+Replace both calls with a single timer_shutdown_sync(). This function both
+waits for any running timer callback to complete (like timer_delete_sync())
+and permanently disarms the timer so it cannot be re-armed afterwards,
+making re-arming prevention unconditional and self-documenting.
+
+The re-arming property is also required because otherwise:
+
+1. context 0 (batadv_tp_recv_ack()) checks in
+   batadv_tp_reset_sender_timer() if sending is still 1 -> it is
+2. context 1 changes in batadv_tp_sender_shutdown() sending to 0 and in
+   this process forces the kthread to stop timer in
+   batadv_tp_sender_cleanup()
+3. context 0 continues in batadv_tp_reset_sender_timer() and rearms the
+   timer -> but the reference for it is already gone
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ adapt pre-hunk to old del_timer* names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index bc3dc377f0bfd0..dfc3374549921f 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -430,13 +430,7 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
+       batadv_tp_list_detach(tp_vars);
+       /* kill the timer and remove its reference */
+-      del_timer_sync(&tp_vars->timer);
+-      /* the worker might have rearmed itself therefore we kill it again. Note
+-       * that if the worker should run again before invoking the following
+-       * del_timer(), it would not re-arm itself once again because the status
+-       * is OFF now
+-       */
+-      del_timer(&tp_vars->timer);
++      timer_shutdown_sync(&tp_vars->timer);
+       batadv_tp_vars_put(tp_vars);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-6.12/batman-adv-tt-avoid-empty-vlan-responses.patch
new file mode 100644 (file)
index 0000000..96014bb
--- /dev/null
@@ -0,0 +1,91 @@
+From 5642f51788b0bd26b6a7728d5740a72c5fe5ce56 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:04:50 +0200
+Subject: batman-adv: tt: avoid empty VLAN responses
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 upstream.
+
+The commit 16116dac2339 ("batman-adv: prevent TT request storms by not
+sending inconsistent TT TLVLs") added checks to the local (direct) TT
+response code. But the response can also be done indirectly by another node
+using the global TT state. To avoid such inconsistency states reported in
+the original fix, also avoid sending empty VLANs for replies from the
+global TT state.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index d4cebe122e528a..4045ddefc29b47 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+                                  s32 *tt_len)
+ {
+       u16 num_vlan = 0;
+-      u16 num_entries = 0;
+       u16 tvlv_len = 0;
+       unsigned int change_offset;
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_orig_node_vlan *vlan;
++      u16 total_entries = 0;
+       u8 *tt_change_ptr;
++      int vlan_entries;
+       spin_lock_bh(&orig_node->vlan_list_lock);
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              total_entries += vlan_entries;
+               num_vlan++;
+-              num_entries += atomic_read(&vlan->tt.num_entries);
+       }
+       change_offset = sizeof(**tt_data);
+@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       /* if tt_len is negative, allocate the space needed by the full table */
+       if (*tt_len < 0)
+-              *tt_len = batadv_tt_len(num_entries);
++              *tt_len = batadv_tt_len(total_entries);
+       if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) {
+               *tt_len = 0;
+@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              if (vlan_entries < 1)
++                      continue;
++
+               tt_vlan->vid = htons(vlan->vid);
+               tt_vlan->crc = htonl(vlan->tt.crc);
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-6.12/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
new file mode 100644 (file)
index 0000000..b978744
--- /dev/null
@@ -0,0 +1,78 @@
+From 2f1e6d3e5d741e8138179ae67735e3d39e217381 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 22:57:44 +0200
+Subject: batman-adv: tt: fix TOCTOU race for reported vlans
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 94d27005016be15ffc638b2ecbc4d58805ad7b48 upstream.
+
+The local TT based TVLV is generated by first checking the number of VLANs
+which have at least one TT entry. A new buffer with the correct size for
+the VLANs is then allocated. Only then, the list of VLANs s used to fill
+the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock
+is held. But the actual number of TT entries of each VLAN can still
+increase during this time - just not the number of VLANs in the list.
+
+But the prefilter used in the buffer size calculation might still cause an
+increase of the number of VLANs which need to be stored. Simply because a
+VLAN might now suddenly have at least one entry when it had none in the
+pre-alloc check - and then needs to occupy space which was not allocated.
+
+It is better to overestimate the buffer size at the beginning and then fill
+the buffer only with the VLANs which are not empty.
+
+Cc: stable@kernel.org
+Fixes: 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 8ffebece03c529..d4cebe122e528a 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+-              if (vlan_entries < 1)
+-                      continue;
+-
+-              num_vlan++;
+               total_entries += vlan_entries;
++              num_vlan++;
+       }
+       change_offset = sizeof(**tt_data);
+@@ -964,6 +961,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+               if (vlan_entries < 1)
+@@ -974,8 +972,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch b/queue-6.12/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch
new file mode 100644 (file)
index 0000000..4b45e82
--- /dev/null
@@ -0,0 +1,62 @@
+From 7ec95e77d8cf28983bb8e5e7cad19943f138f2b3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:03:00 +0200
+Subject: batman-adv: tt: reject oversized local TVLV buffers
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 1e9fab756f8395096d5bba7be0c373c4c8f5d165 upstream.
+
+The commit 3a359bf5c61d ("batman-adv: reject oversized global TT response
+buffers") added a check to ensure that a global return buffer size can be
+stored in an u16. The same buffer handling also exists for the local data
+buffer but was not touched.
+
+A similar check should be also be in place for the local TVLV buffer. It
+doesn't have the similar attack surface because it is only generated from
+locally discovered MAC addresses but the dynamic nature could still cause
+temporarily to large buffers.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index d830ccf016697b..8ffebece03c529 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -924,12 +924,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+ {
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_softif_vlan *vlan;
++      size_t change_offset;
+       u16 num_vlan = 0;
+       u16 vlan_entries = 0;
+       u16 total_entries = 0;
+       u16 tvlv_len;
+       u8 *tt_change_ptr;
+-      int change_offset;
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+@@ -948,8 +948,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       if (*tt_len < 0)
+               *tt_len = batadv_tt_len(total_entries);
+-      tvlv_len = *tt_len;
+-      tvlv_len += change_offset;
++      if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) {
++              tvlv_len = 0;
++              goto out;
++      }
+       *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
+       if (!*tt_data) {
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-6.12/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
new file mode 100644 (file)
index 0000000..69e56f8
--- /dev/null
@@ -0,0 +1,196 @@
+From 9b197968072387191d0b3f4d3cdb5396109fbaba Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:46:02 +0200
+Subject: batman-adv: tvlv: abort OGM send on tvlv append failure
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 501368506563e151b322c8c3f228b796e615b90d upstream.
+
+batadv_tvlv_container_ogm_append() could fail in two ways: a memory
+allocation failure when resizing the packet buffer, or the tvlv data
+exceeding U16_MAX bytes. In both cases the function previously returned the
+old (now stale) tvlv_value_len rather than signalling an error, causing the
+OGM/OGM2 send path to transmit a packet whose TVLV length field no longer
+matched the actual buffer contents. And because it also didn't fill in the
+new TVLV data, sending either uninitialized or corrupted data on the wire.
+
+All errors in batadv_tvlv_container_ogm_append() must be forwarded to the
+caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N.
+IV, it is currently not allowed to abort the send. The non-TVLV part of the
+OGM must be queued up instead.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 16 +++++++++++++---
+ net/batman-adv/bat_v_ogm.c  | 26 ++++++++++++++------------
+ net/batman-adv/tvlv.c       | 17 ++++++++++++-----
+ net/batman-adv/tvlv.h       |  2 +-
+ 4 files changed, 40 insertions(+), 21 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 748188d3b878b2..42b687c1a76807 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -781,6 +781,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -804,9 +805,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                * appended as it may alter the tt tvlv container
+                */
+               batadv_tt_local_commit_changes(bat_priv);
+-              tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+-                                                          ogm_buff_len,
+-                                                          BATADV_OGM_HLEN);
++              ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                                     ogm_buff_len,
++                                                     BATADV_OGM_HLEN);
++              if (ret < 0) {
++                      /* OGMs must be queued even when the buffer allocation for
++                       * TVLVs failed. just fall back to the non-TVLV version
++                       */
++                      ret = 0;
++                      *ogm_buff_len = BATADV_OGM_HLEN;
++              }
++
++              tvlv_len = ret;
+       }
+       batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 310248a5812c49..8cfc3944dcfd52 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -271,9 +271,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       struct batadv_hard_iface *hard_iface;
+       struct batadv_ogm2_packet *ogm_packet;
+       struct sk_buff *skb, *skb_tmp;
+-      unsigned char *ogm_buff;
+-      int ogm_buff_len;
+-      u16 tvlv_len = 0;
++      unsigned char **ogm_buff;
++      int *ogm_buff_len;
++      u16 tvlv_len;
+       int ret;
+       lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
+@@ -281,25 +281,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
+               goto out;
+-      ogm_buff = bat_priv->bat_v.ogm_buff;
+-      ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
++      ogm_buff = &bat_priv->bat_v.ogm_buff;
++      ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
++
+       /* tt changes have to be committed before the tvlv data is
+        * appended as it may alter the tt tvlv container
+        */
+       batadv_tt_local_commit_changes(bat_priv);
+-      tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
+-                                                  &ogm_buff_len,
+-                                                  BATADV_OGM2_HLEN);
++      ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                             ogm_buff_len,
++                                             BATADV_OGM2_HLEN);
++      if (ret < 0)
++              goto reschedule;
+-      bat_priv->bat_v.ogm_buff = ogm_buff;
+-      bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
++      tvlv_len = ret;
+-      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
++      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len);
+       if (!skb)
+               goto reschedule;
+       skb_reserve(skb, ETH_HLEN);
+-      skb_put_data(skb, ogm_buff, ogm_buff_len);
++      skb_put_data(skb, *ogm_buff, *ogm_buff_len);
+       ogm_packet = (struct batadv_ogm2_packet *)skb->data;
+       ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 2a583215d439bd..76c6e0599694c7 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -8,6 +8,7 @@
+ #include <linux/byteorder/generic.h>
+ #include <linux/container_of.h>
++#include <linux/errno.h>
+ #include <linux/etherdevice.h>
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
+  * The ogm packet might be enlarged or shrunk depending on the current size
+  * and the size of the to-be-appended tvlv containers.
+  *
+- * Return: size of all appended tvlv containers in bytes.
++ * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
++ *  if operation failed
+  */
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len)
+ {
+@@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_len;
+       void *tvlv_value;
++      int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+@@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-
+-      if (!ret)
++      if (!ret) {
++              tvlv_len_ret = -ENOMEM;
+               goto end;
++      }
++
++      tvlv_len_ret = tvlv_value_len;
+       if (!tvlv_value_len)
+               goto end;
+@@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ end:
+       spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+-      return tvlv_value_len;
++
++      return tvlv_len_ret;
+ }
+ /**
+diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h
+index e5697230d99173..f96f6b3f44a001 100644
+--- a/net/batman-adv/tvlv.h
++++ b/net/batman-adv/tvlv.h
+@@ -16,7 +16,7 @@
+ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+                                   u8 type, u8 version,
+                                   void *tvlv_value, u16 tvlv_value_len);
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len);
+ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-6.12/batman-adv-tvlv-reject-oversized-tvlv-packets.patch
new file mode 100644 (file)
index 0000000..2e0e365
--- /dev/null
@@ -0,0 +1,85 @@
+From 41c0d1833ed99ea68e8473043f65fbba76f136bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:08:04 +0200
+Subject: batman-adv: tvlv: reject oversized TVLV packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f50487e3566358b2b982b7801945e858c78ad9ab upstream.
+
+batadv_tvlv_container_ogm_append() builds a TVLV packet section from
+the tvlv.container_list. The total size of this section is computed by
+batadv_tvlv_container_list_size(), which sums the sizes of all registered
+containers.
+
+The return type and accumulator in batadv_tvlv_container_list_size() were
+u16. If the accumulated size exceeds U16_MAX, the value wraps around,
+causing the subsequent allocation in batadv_tvlv_container_ogm_append()
+to be undersized. The memcpy-style copy that follows would then write
+beyond the end of the allocated buffer, corrupting kernel memory.
+
+Fix this by widening the return type of batadv_tvlv_container_list_size()
+to size_t. In batadv_tvlv_container_ogm_append(), check the computed length
+against U16_MAX before proceeding, and bail out as if the allocation had
+failed when the limit is exceeded.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 76c6e0599694c7..8d6b017c433cc9 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -13,6 +13,7 @@
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+ #include <linux/kref.h>
++#include <linux/limits.h>
+ #include <linux/list.h>
+ #include <linux/lockdep.h>
+ #include <linux/netdevice.h>
+@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
+  *
+  * Return: size of all currently registered tvlv containers in bytes.
+  */
+-static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
++static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+ {
+       struct batadv_tvlv_container *tvlv;
+-      u16 tvlv_len = 0;
++      size_t tvlv_len = 0;
+       lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
+@@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ {
+       struct batadv_tvlv_container *tvlv;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+-      u16 tvlv_value_len;
++      size_t tvlv_value_len;
+       void *tvlv_value;
+       int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+       tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
++      if (tvlv_value_len > U16_MAX) {
++              tvlv_len_ret = -E2BIG;
++              goto end;
++      }
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-- 
+2.53.0
+
diff --git a/queue-6.12/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-6.12/batman-adv-v-stop-ogmv2-on-disabled-interface.patch
new file mode 100644 (file)
index 0000000..7e901f2
--- /dev/null
@@ -0,0 +1,149 @@
+From a4866cc765d098305ad5e0af312c15a329862277 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:27:33 +0200
+Subject: batman-adv: v: stop OGMv2 on disabled interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f8ce8b8331a1bc44ad4905886a482214d428b253 upstream.
+
+When a batadv_hard_iface is disabled, its mesh_iface pointer is set to
+NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via
+batadv_v_ogm_queue_on_if() for interfaces that have since lost their
+mesh_iface association. This results in a NULL pointer dereference when
+batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the
+now NULL hard_iface->mesh_iface to retrieve the batadv_priv.
+
+It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that
+it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was
+called.
+
+Cc: stable@kernel.org
+Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 8f89ffe6020ced..310248a5812c49 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
+ /**
+  * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to send
+  * @hard_iface: the interface to use to send the OGM
+  */
+-static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
++static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
++                                  struct sk_buff *skb,
+                                   struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+-
+       if (hard_iface->if_status != BATADV_IF_ACTIVE) {
+               kfree_skb(skb);
+               return;
+@@ -189,6 +189,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+ /**
+  * batadv_v_ogm_aggr_send() - flush & send aggregation queue
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @hard_iface: the interface with the aggregation queue to flush
+  *
+  * Aggregates all OGMv2 packets currently in the aggregation queue into a
+@@ -198,7 +199,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+  *
+  * Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
+  */
+-static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
++static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
++                                 struct batadv_hard_iface *hard_iface)
+ {
+       unsigned int aggr_len = hard_iface->bat_v.aggr_len;
+       struct sk_buff *skb_aggr;
+@@ -228,27 +230,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
+               consume_skb(skb);
+       }
+-      batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
++      batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ }
+ /**
+  * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to queue
+  * @hard_iface: the interface to queue the OGM on
+  */
+-static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
++static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
++                                   struct sk_buff *skb,
+                                    struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      if (hard_iface->soft_iface != bat_priv->soft_iface) {
++              kfree_skb(skb);
++              return;
++      }
+       if (!atomic_read(&bat_priv->aggregated_ogms)) {
+-              batadv_v_ogm_send_to_if(skb, hard_iface);
++              batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+               return;
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+-              batadv_v_ogm_aggr_send(hard_iface);
++              batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
+@@ -347,7 +354,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+                       break;
+               }
+-              batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
++              batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+               batadv_hardif_put(hard_iface);
+       }
+       rcu_read_unlock();
+@@ -387,12 +394,14 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
+ {
+       struct batadv_hard_iface_bat_v *batv;
+       struct batadv_hard_iface *hard_iface;
++      struct batadv_priv *bat_priv;
+       batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
+       hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
++      bat_priv = netdev_priv(hard_iface->soft_iface);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+-      batadv_v_ogm_aggr_send(hard_iface);
++      batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+       batadv_v_ogm_start_queue_timer(hard_iface);
+@@ -582,7 +591,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
+                  if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
+                  ogm_forward->ttl, if_incoming->net_dev->name);
+-      batadv_v_ogm_queue_on_if(skb, if_outgoing);
++      batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+ out:
+       batadv_orig_ifinfo_put(orig_ifinfo);
+-- 
+2.53.0
+
diff --git a/queue-6.12/drm-dp-add-edp-1.5-bit-definition.patch b/queue-6.12/drm-dp-add-edp-1.5-bit-definition.patch
new file mode 100644 (file)
index 0000000..c55af3c
--- /dev/null
@@ -0,0 +1,41 @@
+From 1be1a226934affa1bea20e0a351aae1e7c164050 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:42:05 +0300
+Subject: drm/dp: Add eDP 1.5 bit definition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Suraj Kandpal <suraj.kandpal@intel.com>
+
+commit 5dfc37a6b77bf6beedbd30d70184b54e1a08ccac upstream.
+
+Add the eDP revision bit value for 1.5.
+
+Spec: eDPv1.5 Table 16-5
+Signed-off-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Reviewed-by: Arun R Murthy <arun.r.murthy@intel.com>
+Tested-by: Ben Kao <ben.kao@intel.com>
+Acked-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250206063253.2827017-2-suraj.kandpal@intel.com
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/drm/display/drm_dp.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h
+index 3bd9f482f0c3e6..dd218400a613e3 100644
+--- a/include/drm/display/drm_dp.h
++++ b/include/drm/display/drm_dp.h
+@@ -997,6 +997,7 @@
+ # define DP_EDP_14                        0x03
+ # define DP_EDP_14a                         0x04    /* eDP 1.4a */
+ # define DP_EDP_14b                         0x05    /* eDP 1.4b */
++# define DP_EDP_15                        0x06    /* eDP 1.5 */
+ #define DP_EDP_GENERAL_CAP_1              0x701
+ # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP         (1 << 0)
+-- 
+2.53.0
+
diff --git a/queue-6.12/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-6.12/drm-i915-psr-add-defininitions-for-intel_wa_register.patch
new file mode 100644 (file)
index 0000000..e65931e
--- /dev/null
@@ -0,0 +1,73 @@
+From 570a6a6f6b17c1ab5a96e802e3d8e6a069cc833a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:42:03 +0300
+Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD
+ register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream.
+
+EDP specification says:
+
+"If either VSC SDP is unable to be transmitted 100 ns before the SU region,
+the Source device may optionally transmit the VSC SDP during the prior
+video scan line’s HBlank period There is a Intel specific drm dp register
+currently containing bits related how TCON can support PSR2 with SDP on
+prior line."
+
+Unfortunately many panels are having problems in implementing this. So
+there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to
+figure out if this is properly implemented on a panel or if panel doesn't
+require that 100 ns delay before the SU region. Here are the definitions in
+this custom DPCD address:
+
+0 = Panel doesn't support SDP on prior line
+1 = Panel supports SDP on prior line
+2 = Panel doesn't have 100ns requirement
+3 = Reserved
+
+Add definitions for this new register and it's values into new header
+intel_dpcd.h.
+
+v2: add INTEL_DPCD_ prefix to definitions
+
+Bspec: 74741
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com
+(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+ create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h
+
+diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h
+new file mode 100644
+index 00000000000000..4aea5326f2ed48
+--- /dev/null
++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: MIT */
++/*
++ * Copyright © 2026 Intel Corporation
++ */
++
++#ifndef __INTEL_DPCD_H__
++#define __INTEL_DPCD_H__
++
++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS                                     0x3f0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK        REG_GENMASK(1, 0)
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1                  0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE           1
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE                2
++
++#endif /* __INTEL_DPCD_H__ */
+-- 
+2.53.0
+
diff --git a/queue-6.12/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-6.12/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
new file mode 100644 (file)
index 0000000..5fb8747
--- /dev/null
@@ -0,0 +1,77 @@
+From 13a8e2b69c4b6c16032c020b9e4f5a8304622a25 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:42:06 +0300
+Subject: drm/i915/psr: Apply Intel DPCD workaround when SDP on prior line used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit 4703049f768fc1c1caac754134118bee1a3af189 upstream.
+
+There is Intel specific workaround DPCD address containing workaround for
+case where SDP is on prior line. Apply this workaround according to values
+in the offset.
+
+Fixes: 61e887329e33 ("drm/i915/xelpd: Handle PSR2 SDP indication in the prior scanline")
+Cc: <stable@vger.kernel.org> # v5.15+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com
+(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_psr.c | 27 +++++++++++++++++++++++-
+ 1 file changed, 26 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 9c38b957701675..5173f5759ce888 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -1303,6 +1303,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp,
+       return true;
+ }
++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp,
++                                       struct intel_crtc_state *crtc_state)
++{
++      u8 early_scanline_support = intel_dp->intel_wa_dpcd &
++              INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK;
++
++      if (intel_dp->edp_dpcd[0] >= DP_EDP_15)
++              return true;
++
++      switch (early_scanline_support) {
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return false;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE:
++              return true;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return true;
++      default:
++              MISSING_CASE(early_scanline_support);
++              return false;
++      }
++}
++
+ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp,
+                                                       struct intel_crtc_state *crtc_state)
+ {
+@@ -1324,7 +1348,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d
+               return false;
+       crtc_state->req_psr2_sdp_prior_scanline = true;
+-      return true;
++
++      return apply_scanline_indication_wa(intel_dp, crtc_state);
+ }
+ static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp,
+-- 
+2.53.0
+
diff --git a/queue-6.12/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-6.12/drm-i915-psr-read-intel-dpcd-workaround-register.patch
new file mode 100644 (file)
index 0000000..3434a31
--- /dev/null
@@ -0,0 +1,67 @@
+From 7e7c761578ce407e19bb206dced0bac66da9a83e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:42:04 +0300
+Subject: drm/i915/psr: Read Intel DPCD workaround register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream.
+
+Read Intel DPCD workaround register and store it into
+intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains
+only PSR workaround for PSR2 SDP on prior scanline implementation.
+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com
+(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_display_types.h | 1 +
+ drivers/gpu/drm/i915/display/intel_psr.c           | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
+index 2039c17a9ee787..992945b37190e6 100644
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -1773,6 +1773,7 @@ struct intel_dp {
+       u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE];
+       u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE];
+       u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE];
++      u8 intel_wa_dpcd;
+       /* source rates */
+       int num_source_rates;
+       const int *source_rates;
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 2a7f379c59fefd..9c38b957701675 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -35,6 +35,7 @@
+ #include "intel_de.h"
+ #include "intel_display_types.h"
+ #include "intel_dp.h"
++#include "intel_dpcd.h"
+ #include "intel_dp_aux.h"
+ #include "intel_frontbuffer.h"
+ #include "intel_hdmi.h"
+@@ -665,6 +666,12 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp)
+               drm_dbg_kms(display->drm, "PSR2 %ssupported\n",
+                           intel_dp->psr.sink_psr2_support ? "" : "not ");
+       }
++
++      if (intel_dp->psr.sink_psr2_support)
++              drm_dp_dpcd_read(&intel_dp->aux,
++                               INTEL_DPCD_INTEL_WA_REGISTER_CAPS,
++                               &intel_dp->intel_wa_dpcd,
++                               sizeof(intel_dp->intel_wa_dpcd));
+ }
+ void intel_psr_init_dpcd(struct intel_dp *intel_dp)
+-- 
+2.53.0
+
diff --git a/queue-6.12/hid-core-add-printk_ratelimited-variants-to-hid_warn.patch b/queue-6.12/hid-core-add-printk_ratelimited-variants-to-hid_warn.patch
new file mode 100644 (file)
index 0000000..c291160
--- /dev/null
@@ -0,0 +1,42 @@
+From f7fde9d942389143ae0aa178819bd77ece7adbf4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 1 Jun 2026 09:36:09 +0100
+Subject: HID: core: Add printk_ratelimited variants to hid_warn() etc
+
+From: Vicki Pfau <vi@endrift.com>
+
+[ Upstream commit 1d64624243af8329b4b219d8c39e28ea448f9929 ]
+
+hid_warn_ratelimited() is needed. Add the others as part of the block.
+
+Signed-off-by: Vicki Pfau <vi@endrift.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/hid.h | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/include/linux/hid.h b/include/linux/hid.h
+index 7d8d09318fa91d..bef017d6b44042 100644
+--- a/include/linux/hid.h
++++ b/include/linux/hid.h
+@@ -1245,4 +1245,15 @@ void hid_quirks_exit(__u16 bus);
+ #define hid_dbg_once(hid, fmt, ...)                   \
+       dev_dbg_once(&(hid)->dev, fmt, ##__VA_ARGS__)
++#define hid_err_ratelimited(hid, fmt, ...)                    \
++      dev_err_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
++#define hid_notice_ratelimited(hid, fmt, ...)                 \
++      dev_notice_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
++#define hid_warn_ratelimited(hid, fmt, ...)                   \
++      dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
++#define hid_info_ratelimited(hid, fmt, ...)                   \
++      dev_info_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
++#define hid_dbg_ratelimited(hid, fmt, ...)                    \
++      dev_dbg_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
++
+ #endif
+-- 
+2.53.0
+
diff --git a/queue-6.12/hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch b/queue-6.12/hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch
new file mode 100644 (file)
index 0000000..52305b5
--- /dev/null
@@ -0,0 +1,67 @@
+From 61a38d8797ea96decb246fc6fbd728f7f1d09310 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 1 Jun 2026 09:36:12 +0100
+Subject: HID: core: Fix size_t specifier in hid_report_raw_event()
+
+From: Nathan Chancellor <nathan@kernel.org>
+
+[ Upstream commit 4d3a2a466b8d68d852a1f3bbf11204b718428dc4 ]
+
+When building for 32-bit platforms, for which 'size_t' is
+'unsigned int', there are warnings around using the incorrect format
+specifier to print bsize in hid_report_raw_event():
+
+  drivers/hid/hid-core.c:2054:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
+   2053 |                 hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
+        |                                                                                         ~~~
+        |                                                                                         %zu
+   2054 |                                      report->id, csize, bsize);
+        |                                                         ^~~~~
+  drivers/hid/hid-core.c:2076:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
+   2075 |                 hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
+        |                                                                                          ~~~
+        |                                                                                          %zu
+   2076 |                                      report->id, rsize, bsize);
+        |                                                         ^~~~~
+
+Use the proper 'size_t' format specifier, '%zu', to clear up the
+warnings.
+
+Cc: stable@vger.kernel.org
+Fixes: 2c85c61d1332 ("HID: pass the buffer size to hid_report_raw_event")
+Reported-by: Miguel Ojeda <ojeda@kernel.org>
+Closes: https://lore.kernel.org/20260516020430.110135-1-ojeda@kernel.org/
+Signed-off-by: Nathan Chancellor <nathan@kernel.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+(cherry picked from commit 3ab135238832446399614e7a4bb796d620717806)
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-core.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
+index d9ea99cdb68e06..87d990ada8688a 100644
+--- a/drivers/hid/hid-core.c
++++ b/drivers/hid/hid-core.c
+@@ -2015,7 +2015,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
+               return 0;
+       if (unlikely(bsize < csize)) {
+-              hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
++              hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %zu)\n",
+                                    report->id, csize, bsize);
+               return -EINVAL;
+       }
+@@ -2037,7 +2037,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
+               rsize = max_buffer_size;
+       if (bsize < rsize) {
+-              hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
++              hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %zu)\n",
+                                    report->id, rsize, bsize);
+               return -EINVAL;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.12/hid-core-introduce-hid_safe_input_report.patch b/queue-6.12/hid-core-introduce-hid_safe_input_report.patch
new file mode 100644 (file)
index 0000000..513947a
--- /dev/null
@@ -0,0 +1,139 @@
+From 993f336256ff20948bbc58104e32524d9d813fa4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 1 Jun 2026 09:36:11 +0100
+Subject: HID: core: introduce hid_safe_input_report()
+
+From: Benjamin Tissoires <bentiss@kernel.org>
+
+[ Upstream commit 206342541fc887ae919774a43942dc883161fece ]
+
+hid_input_report() is used in too many places to have a commit that
+doesn't cross subsystem borders. Instead of changing the API, introduce
+a new one when things matters in the transport layers:
+- usbhid
+- i2chid
+
+This effectively revert to the old behavior for those two transport
+layers.
+
+Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()")
+Cc: stable@vger.kernel.org
+Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+(cherry picked from commit 301338b8edadc67a42b1c86add975091e66768d9)
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-core.c             | 25 +++++++++++++++++++++++++
+ drivers/hid/i2c-hid/i2c-hid-core.c |  7 ++++---
+ drivers/hid/usbhid/hid-core.c      | 11 ++++++-----
+ include/linux/hid.h                |  2 ++
+ 4 files changed, 37 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
+index ceff91722c3c83..d9ea99cdb68e06 100644
+--- a/drivers/hid/hid-core.c
++++ b/drivers/hid/hid-core.c
+@@ -2146,6 +2146,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
+  * @interrupt: distinguish between interrupt and control transfers
+  *
+  * This is data entry for lower layers.
++ * Legacy, please use hid_safe_input_report() instead.
+  */
+ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
+                    int interrupt)
+@@ -2156,6 +2157,30 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data
+ }
+ EXPORT_SYMBOL_GPL(hid_input_report);
++/**
++ * hid_safe_input_report - report data from lower layer (usb, bt...)
++ *
++ * @hid: hid device
++ * @type: HID report type (HID_*_REPORT)
++ * @data: report contents
++ * @bufsize: allocated size of the data buffer
++ * @size: useful size of data parameter
++ * @interrupt: distinguish between interrupt and control transfers
++ *
++ * This is data entry for lower layers.
++ * Please use this function instead of the non safe version because we provide
++ * here the size of the buffer, allowing hid-core to make smarter decisions
++ * regarding the incoming buffer.
++ */
++int hid_safe_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data,
++                        size_t bufsize, u32 size, int interrupt)
++{
++      return __hid_input_report(hid, type, data, bufsize, size, interrupt, 0,
++                                false, /* from_bpf */
++                                false /* lock_already_taken */);
++}
++EXPORT_SYMBOL_GPL(hid_safe_input_report);
++
+ bool hid_match_one_id(const struct hid_device *hdev,
+                     const struct hid_device_id *id)
+ {
+diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
+index cf8ae0df0cda95..8ce0535fc42d60 100644
+--- a/drivers/hid/i2c-hid/i2c-hid-core.c
++++ b/drivers/hid/i2c-hid/i2c-hid-core.c
+@@ -568,9 +568,10 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
+               if (ihid->hid->group != HID_GROUP_RMI)
+                       pm_wakeup_event(&ihid->client->dev, 0);
+-              hid_input_report(ihid->hid, HID_INPUT_REPORT,
+-                              ihid->inbuf + sizeof(__le16),
+-                              ret_size - sizeof(__le16), 1);
++              hid_safe_input_report(ihid->hid, HID_INPUT_REPORT,
++                                    ihid->inbuf + sizeof(__le16),
++                                    ihid->bufsize - sizeof(__le16),
++                                    ret_size - sizeof(__le16), 1);
+       }
+       return;
+diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
+index f14b46ce00cb67..336ad7cf3d484e 100644
+--- a/drivers/hid/usbhid/hid-core.c
++++ b/drivers/hid/usbhid/hid-core.c
+@@ -283,9 +283,9 @@ static void hid_irq_in(struct urb *urb)
+                       break;
+               usbhid_mark_busy(usbhid);
+               if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
+-                      hid_input_report(urb->context, HID_INPUT_REPORT,
+-                                       urb->transfer_buffer,
+-                                       urb->actual_length, 1);
++                      hid_safe_input_report(urb->context, HID_INPUT_REPORT,
++                                            urb->transfer_buffer, urb->transfer_buffer_length,
++                                            urb->actual_length, 1);
+                       /*
+                        * autosuspend refused while keys are pressed
+                        * because most keyboards don't wake up when
+@@ -482,9 +482,10 @@ static void hid_ctrl(struct urb *urb)
+       switch (status) {
+       case 0:                 /* success */
+               if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
+-                      hid_input_report(urb->context,
++                      hid_safe_input_report(urb->context,
+                               usbhid->ctrl[usbhid->ctrltail].report->type,
+-                              urb->transfer_buffer, urb->actual_length, 0);
++                              urb->transfer_buffer, urb->transfer_buffer_length,
++                              urb->actual_length, 0);
+               break;
+       case -ESHUTDOWN:        /* unplug */
+               unplug = 1;
+diff --git a/include/linux/hid.h b/include/linux/hid.h
+index fdd401e4ebde3d..7d05b1edacd80c 100644
+--- a/include/linux/hid.h
++++ b/include/linux/hid.h
+@@ -949,6 +949,8 @@ struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_ty
+ int hid_set_field(struct hid_field *, unsigned, __s32);
+ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
+                    int interrupt);
++int hid_safe_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data,
++                        size_t bufsize, u32 size, int interrupt);
+ struct hid_field *hidinput_get_led_field(struct hid_device *hid);
+ unsigned int hidinput_count_leds(struct hid_device *hid);
+ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
+-- 
+2.53.0
+
diff --git a/queue-6.12/hid-pass-the-buffer-size-to-hid_report_raw_event.patch b/queue-6.12/hid-pass-the-buffer-size-to-hid_report_raw_event.patch
new file mode 100644 (file)
index 0000000..a597728
--- /dev/null
@@ -0,0 +1,362 @@
+From 159621bf6568570c7d59925c9e23a54ecc46fe3b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 1 Jun 2026 09:36:10 +0100
+Subject: HID: pass the buffer size to hid_report_raw_event
+
+From: Benjamin Tissoires <bentiss@kernel.org>
+
+[ Upstream commit 2c85c61d1332e1e16f020d76951baf167dcb6f7a ]
+
+commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing
+bogus memset()") enforced the provided data to be at least the size of
+the declared buffer in the report descriptor to prevent a buffer
+overflow. However, we can try to be smarter by providing both the buffer
+size and the data size, meaning that hid_report_raw_event() can make
+better decision whether we should plaining reject the buffer (buffer
+overflow attempt) or if we can safely memset it to 0 and pass it to the
+rest of the stack.
+
+Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()")
+Cc: stable@vger.kernel.org
+Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
+Acked-by: Johan Hovold <johan@kernel.org>
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Stable-dep-of: 206342541fc8 ("HID: core: introduce hid_safe_input_report()")
+(cherry picked from commit 509c2605065004fc4cd86ee50a9350d402785307)
+[Lee: Backported to linux-6.12.y and beyond]
+Signed-off-by: Lee Jones <lee@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/bpf/hid_bpf_dispatch.c |  6 +++--
+ drivers/hid/hid-core.c             | 37 +++++++++++++++++++++---------
+ drivers/hid/hid-gfrm.c             |  4 ++--
+ drivers/hid/hid-logitech-hidpp.c   |  2 +-
+ drivers/hid/hid-multitouch.c       |  2 +-
+ drivers/hid/hid-primax.c           |  2 +-
+ drivers/hid/hid-vivaldi-common.c   |  2 +-
+ drivers/hid/wacom_sys.c            |  6 ++---
+ drivers/staging/greybus/hid.c      |  2 +-
+ include/linux/hid.h                |  4 ++--
+ include/linux/hid_bpf.h            | 14 +++++++----
+ 11 files changed, 51 insertions(+), 30 deletions(-)
+
+diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
+index 284861c166d9c4..b711d83dfde1d4 100644
+--- a/drivers/hid/bpf/hid_bpf_dispatch.c
++++ b/drivers/hid/bpf/hid_bpf_dispatch.c
+@@ -24,7 +24,8 @@ EXPORT_SYMBOL(hid_ops);
+ u8 *
+ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
+-                            u32 *size, int interrupt, u64 source, bool from_bpf)
++                            size_t *buf_size, u32 *size, int interrupt, u64 source,
++                            bool from_bpf)
+ {
+       struct hid_bpf_ctx_kern ctx_kern = {
+               .ctx = {
+@@ -74,6 +75,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
+               *size = ret;
+       }
++      *buf_size = ctx_kern.ctx.allocated_size;
+       return ctx_kern.data;
+ }
+ EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
+@@ -514,7 +516,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b
+       if (ret)
+               return ret;
+-      return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (u64)(long)ctx, true,
++      return hid_ops->hid_input_report(ctx->hid, type, buf, size, size, 0, (u64)(long)ctx, true,
+                                        lock_already_taken);
+ }
+diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
+index 294a25330ed030..ceff91722c3c83 100644
+--- a/drivers/hid/hid-core.c
++++ b/drivers/hid/hid-core.c
+@@ -1998,24 +1998,32 @@ int __hid_request(struct hid_device *hid, struct hid_report *report,
+ }
+ EXPORT_SYMBOL_GPL(__hid_request);
+-int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
+-                       int interrupt)
++int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
++                       size_t bufsize, u32 size, int interrupt)
+ {
+       struct hid_report_enum *report_enum = hid->report_enum + type;
+       struct hid_report *report;
+       struct hid_driver *hdrv;
+       int max_buffer_size = HID_MAX_BUFFER_SIZE;
+       u32 rsize, csize = size;
++      size_t bsize = bufsize;
+       u8 *cdata = data;
+       int ret = 0;
+       report = hid_get_report(report_enum, data);
+       if (!report)
+-              goto out;
++              return 0;
++
++      if (unlikely(bsize < csize)) {
++              hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
++                                   report->id, csize, bsize);
++              return -EINVAL;
++      }
+       if (report_enum->numbered) {
+               cdata++;
+               csize--;
++              bsize--;
+       }
+       rsize = hid_compute_report_size(report);
+@@ -2028,9 +2036,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
+       else if (rsize > max_buffer_size)
+               rsize = max_buffer_size;
++      if (bsize < rsize) {
++              hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
++                                   report->id, rsize, bsize);
++              return -EINVAL;
++      }
++
+       if (csize < rsize) {
+               dbg_hid("report %d is too short, (%d < %d)\n", report->id,
+-                              csize, rsize);
++                      csize, rsize);
+               memset(cdata + csize, 0, rsize - csize);
+       }
+@@ -2039,7 +2053,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
+       if (hid->claimed & HID_CLAIMED_HIDRAW) {
+               ret = hidraw_report_event(hid, data, size);
+               if (ret)
+-                      goto out;
++                      return ret;
+       }
+       if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
+@@ -2051,15 +2065,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
+       if (hid->claimed & HID_CLAIMED_INPUT)
+               hidinput_report_event(hid, report);
+-out:
++
+       return ret;
+ }
+ EXPORT_SYMBOL_GPL(hid_report_raw_event);
+ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
+-                            u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
+-                            bool lock_already_taken)
++                            u8 *data, size_t bufsize, u32 size, int interrupt, u64 source,
++                            bool from_bpf, bool lock_already_taken)
+ {
+       struct hid_report_enum *report_enum;
+       struct hid_driver *hdrv;
+@@ -2084,7 +2098,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
+       report_enum = hid->report_enum + type;
+       hdrv = hid->driver;
+-      data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf);
++      data = dispatch_hid_bpf_device_event(hid, type, data, &bufsize, &size, interrupt,
++                                           source, from_bpf);
+       if (IS_ERR(data)) {
+               ret = PTR_ERR(data);
+               goto unlock;
+@@ -2113,7 +2128,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
+                       goto unlock;
+       }
+-      ret = hid_report_raw_event(hid, type, data, size, interrupt);
++      ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt);
+ unlock:
+       if (!lock_already_taken)
+@@ -2135,7 +2150,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
+ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
+                    int interrupt)
+ {
+-      return __hid_input_report(hid, type, data, size, interrupt, 0,
++      return __hid_input_report(hid, type, data, size, size, interrupt, 0,
+                                 false, /* from_bpf */
+                                 false /* lock_already_taken */);
+ }
+diff --git a/drivers/hid/hid-gfrm.c b/drivers/hid/hid-gfrm.c
+index 699186ff2349e9..d2a56bf92b416e 100644
+--- a/drivers/hid/hid-gfrm.c
++++ b/drivers/hid/hid-gfrm.c
+@@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
+       switch (data[1]) {
+       case GFRM100_SEARCH_KEY_DOWN:
+               ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn,
+-                                         sizeof(search_key_dn), 1);
++                                         sizeof(search_key_dn), sizeof(search_key_dn), 1);
+               break;
+       case GFRM100_SEARCH_KEY_AUDIO_DATA:
+@@ -74,7 +74,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
+       case GFRM100_SEARCH_KEY_UP:
+               ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up,
+-                                         sizeof(search_key_up), 1);
++                                         sizeof(search_key_up), sizeof(search_key_up), 1);
+               break;
+       default:
+diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
+index d60cd4379e866a..858ac2ab46bd97 100644
+--- a/drivers/hid/hid-logitech-hidpp.c
++++ b/drivers/hid/hid-logitech-hidpp.c
+@@ -3691,7 +3691,7 @@ static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,
+       memcpy(&consumer_report[1], &data[3], 4);
+       /* We are called from atomic context */
+       hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
+-                           consumer_report, 5, 1);
++                           consumer_report, sizeof(consumer_report), 5, 1);
+       return 1;
+ }
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index fcf9a806f109a5..760f9db44c9e32 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -500,7 +500,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
+               }
+               ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
+-                                         size, 0);
++                                         size, size, 0);
+               if (ret)
+                       dev_warn(&hdev->dev, "failed to report feature\n");
+       }
+diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c
+index e44d79dff8de63..8db054280afbcd 100644
+--- a/drivers/hid/hid-primax.c
++++ b/drivers/hid/hid-primax.c
+@@ -44,7 +44,7 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
+                       data[0] |= (1 << (data[idx] - 0xE0));
+                       data[idx] = 0;
+               }
+-              hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0);
++              hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, size, 0);
+               return 1;
+       default:        /* unknown report */
+diff --git a/drivers/hid/hid-vivaldi-common.c b/drivers/hid/hid-vivaldi-common.c
+index bf734055d4b69d..b12bb5cc091aa3 100644
+--- a/drivers/hid/hid-vivaldi-common.c
++++ b/drivers/hid/hid-vivaldi-common.c
+@@ -85,7 +85,7 @@ void vivaldi_feature_mapping(struct hid_device *hdev,
+       }
+       ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
+-                                 report_len, 0);
++                                 report_len, report_len, 0);
+       if (ret) {
+               dev_warn(&hdev->dev, "failed to report feature %d\n",
+                        field->report->id);
+diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
+index 1b1112772777ca..ffcf65dcf71347 100644
+--- a/drivers/hid/wacom_sys.c
++++ b/drivers/hid/wacom_sys.c
+@@ -74,7 +74,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,
+               int err;
+               size = kfifo_out(fifo, buf, sizeof(buf));
+-              err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, false);
++              err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, size, false);
+               if (err) {
+                       hid_warn(hdev, "%s: unable to flush event due to error %d\n",
+                                __func__, err);
+@@ -319,7 +319,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
+                                              data, n, WAC_CMD_RETRIES);
+                       if (ret == n && features->type == HID_GENERIC) {
+                               ret = hid_report_raw_event(hdev,
+-                                      HID_FEATURE_REPORT, data, n, 0);
++                                      HID_FEATURE_REPORT, data, n, n, 0);
+                       } else if (ret == 2 && features->type != HID_GENERIC) {
+                               features->touch_max = data[1];
+                       } else {
+@@ -380,7 +380,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
+                                       data, n, WAC_CMD_RETRIES);
+               if (ret == n) {
+                       ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT,
+-                                                 data, n, 0);
++                                                 data, n, n, 0);
+               } else {
+                       hid_warn(hdev, "%s: could not retrieve sensor offsets\n",
+                                __func__);
+diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c
+index 63c77a3df59111..afa78c96ede898 100644
+--- a/drivers/staging/greybus/hid.c
++++ b/drivers/staging/greybus/hid.c
+@@ -201,7 +201,7 @@ static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report)
+        * we just need to setup the input fields, so using
+        * hid_report_raw_event is safe.
+        */
+-      hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1);
++      hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, ghid->bufsize, size, 1);
+ }
+ static void gb_hid_init_reports(struct gb_hid *ghid)
+diff --git a/include/linux/hid.h b/include/linux/hid.h
+index bef017d6b44042..fdd401e4ebde3d 100644
+--- a/include/linux/hid.h
++++ b/include/linux/hid.h
+@@ -1213,8 +1213,8 @@ static inline u32 hid_report_len(struct hid_report *report)
+       return DIV_ROUND_UP(report->size, 8) + (report->id > 0);
+ }
+-int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
+-                       int interrupt);
++int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
++                       size_t bufsize, u32 size, int interrupt);
+ /* HID quirks API */
+ unsigned long hid_lookup_quirk(const struct hid_device *hdev);
+diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
+index 6a47223e646006..aa87513acbcd24 100644
+--- a/include/linux/hid_bpf.h
++++ b/include/linux/hid_bpf.h
+@@ -72,8 +72,8 @@ struct hid_ops {
+       int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
+                                   u64 source, bool from_bpf);
+       int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type,
+-                              u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
+-                              bool lock_already_taken);
++                              u8 *data, size_t bufsize, u32 size, int interrupt, u64 source,
++                              bool from_bpf, bool lock_already_taken);
+       struct module *owner;
+       const struct bus_type *bus_type;
+ };
+@@ -200,7 +200,8 @@ struct hid_bpf {
+ #ifdef CONFIG_HID_BPF
+ u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
+-                                u32 *size, int interrupt, u64 source, bool from_bpf);
++                                size_t *buf_size, u32 *size, int interrupt, u64 source,
++                                bool from_bpf);
+ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
+                                 unsigned char reportnum, __u8 *buf,
+                                 u32 size, enum hid_report_type rtype,
+@@ -215,8 +216,11 @@ int hid_bpf_device_init(struct hid_device *hid);
+ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size);
+ #else /* CONFIG_HID_BPF */
+ static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
+-                                              u8 *data, u32 *size, int interrupt,
+-                                              u64 source, bool from_bpf) { return data; }
++                                              u8 *data, size_t *buf_size, u32 *size,
++                                              int interrupt, u64 source, bool from_bpf)
++{
++      return data;
++}
+ static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
+                                               unsigned char reportnum, u8 *buf,
+                                               u32 size, enum hid_report_type rtype,
+-- 
+2.53.0
+
diff --git a/queue-6.12/inet-frags-add-inet_frag_queue_flush.patch b/queue-6.12/inet-frags-add-inet_frag_queue_flush.patch
new file mode 100644 (file)
index 0000000..d621b7c
--- /dev/null
@@ -0,0 +1,98 @@
+From 9d595a2730135481b82f345fe859a5e2f8483826 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 17:29:51 +0800
+Subject: inet: frags: add inet_frag_queue_flush()
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ Upstream commit 1231eec6994be29d6bb5c303dfa54731ed9fc0e6 ]
+
+Instead of exporting inet_frag_rbtree_purge() which requires that
+caller takes care of memory accounting, add a new helper. We will
+need to call it from a few places in the next patch.
+
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20251207010942.1672972-3-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Rajani Kantha <681739313@139.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/inet_frag.h  |  5 ++---
+ net/ipv4/inet_fragment.c | 15 ++++++++++++---
+ net/ipv4/ip_fragment.c   |  6 +-----
+ 3 files changed, 15 insertions(+), 11 deletions(-)
+
+diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
+index 5af6eb14c5db15..94edc0e130d2c4 100644
+--- a/include/net/inet_frag.h
++++ b/include/net/inet_frag.h
+@@ -141,9 +141,8 @@ void inet_frag_kill(struct inet_frag_queue *q);
+ void inet_frag_destroy(struct inet_frag_queue *q);
+ struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key);
+-/* Free all skbs in the queue; return the sum of their truesizes. */
+-unsigned int inet_frag_rbtree_purge(struct rb_root *root,
+-                                  enum skb_drop_reason reason);
++void inet_frag_queue_flush(struct inet_frag_queue *q,
++                         enum skb_drop_reason reason);
+ static inline void inet_frag_put(struct inet_frag_queue *q)
+ {
+diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
+index d179a2c8422276..70640906337757 100644
+--- a/net/ipv4/inet_fragment.c
++++ b/net/ipv4/inet_fragment.c
+@@ -264,8 +264,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head)
+       kmem_cache_free(f->frags_cachep, q);
+ }
+-unsigned int inet_frag_rbtree_purge(struct rb_root *root,
+-                                  enum skb_drop_reason reason)
++static unsigned int
++inet_frag_rbtree_purge(struct rb_root *root, enum skb_drop_reason reason)
+ {
+       struct rb_node *p = rb_first(root);
+       unsigned int sum = 0;
+@@ -285,7 +285,16 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root,
+       }
+       return sum;
+ }
+-EXPORT_SYMBOL(inet_frag_rbtree_purge);
++
++void inet_frag_queue_flush(struct inet_frag_queue *q,
++                         enum skb_drop_reason reason)
++{
++      unsigned int sum;
++
++      sum = inet_frag_rbtree_purge(&q->rb_fragments, reason);
++      sub_frag_mem_limit(q->fqdir, sum);
++}
++EXPORT_SYMBOL(inet_frag_queue_flush);
+ void inet_frag_destroy(struct inet_frag_queue *q)
+ {
+diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
+index 183856b0b74094..eb5f6060b85d52 100644
+--- a/net/ipv4/ip_fragment.c
++++ b/net/ipv4/ip_fragment.c
+@@ -253,16 +253,12 @@ static int ip_frag_too_far(struct ipq *qp)
+ static int ip_frag_reinit(struct ipq *qp)
+ {
+-      unsigned int sum_truesize = 0;
+-
+       if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) {
+               refcount_inc(&qp->q.refcnt);
+               return -ETIMEDOUT;
+       }
+-      sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments,
+-                                            SKB_DROP_REASON_FRAG_TOO_FAR);
+-      sub_frag_mem_limit(qp->q.fqdir, sum_truesize);
++      inet_frag_queue_flush(&qp->q, SKB_DROP_REASON_FRAG_TOO_FAR);
+       qp->q.flags = 0;
+       qp->q.len = 0;
+-- 
+2.53.0
+
diff --git a/queue-6.12/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch b/queue-6.12/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch
new file mode 100644 (file)
index 0000000..8d1abec
--- /dev/null
@@ -0,0 +1,187 @@
+From 4d76798300d3b111bdaa22eccdfe650a382b9496 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 17:29:52 +0800
+Subject: inet: frags: flush pending skbs in fqdir_pre_exit()
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ Upstream commit 006a5035b495dec008805df249f92c22c89c3d2e ]
+
+We have been seeing occasional deadlocks on pernet_ops_rwsem since
+September in NIPA. The stuck task was usually modprobe (often loading
+a driver like ipvlan), trying to take the lock as a Writer.
+lockdep does not track readers for rwsems so the read wasn't obvious
+from the reports.
+
+On closer inspection the Reader holding the lock was conntrack looping
+forever in nf_conntrack_cleanup_net_list(). Based on past experience
+with occasional NIPA crashes I looked thru the tests which run before
+the crash and noticed that the crash follows ip_defrag.sh. An immediate
+red flag. Scouring thru (de)fragmentation queues reveals skbs sitting
+around, holding conntrack references.
+
+The problem is that since conntrack depends on nf_defrag_ipv6,
+nf_defrag_ipv6 will load first. Since nf_defrag_ipv6 loads first its
+netns exit hooks run _after_ conntrack's netns exit hook.
+
+Flush all fragment queue SKBs during fqdir_pre_exit() to release
+conntrack references before conntrack cleanup runs. Also flush
+the queues in timer expiry handlers when they discover fqdir->dead
+is set, in case packet sneaks in while we're running the pre_exit
+flush.
+
+The commit under Fixes is not exactly the culprit, but I think
+previously the timer firing would eventually unblock the spinning
+conntrack.
+
+Fixes: d5dd88794a13 ("inet: fix various use-after-free in defrags units")
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20251207010942.1672972-4-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Rajani Kantha <681739313@139.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/inet_frag.h  | 13 +------------
+ include/net/ipv6_frag.h  |  9 ++++++---
+ net/ipv4/inet_fragment.c | 36 ++++++++++++++++++++++++++++++++++++
+ net/ipv4/ip_fragment.c   | 12 +++++++-----
+ 4 files changed, 50 insertions(+), 20 deletions(-)
+
+diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
+index 94edc0e130d2c4..fcabb34fff35de 100644
+--- a/include/net/inet_frag.h
++++ b/include/net/inet_frag.h
+@@ -123,18 +123,7 @@ void inet_frags_fini(struct inet_frags *);
+ int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net);
+-static inline void fqdir_pre_exit(struct fqdir *fqdir)
+-{
+-      /* Prevent creation of new frags.
+-       * Pairs with READ_ONCE() in inet_frag_find().
+-       */
+-      WRITE_ONCE(fqdir->high_thresh, 0);
+-
+-      /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire()
+-       * and ip6frag_expire_frag_queue().
+-       */
+-      WRITE_ONCE(fqdir->dead, true);
+-}
++void fqdir_pre_exit(struct fqdir *fqdir);
+ void fqdir_exit(struct fqdir *fqdir);
+ void inet_frag_kill(struct inet_frag_queue *q);
+diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h
+index 7321ffe3a108c1..df61b98b521531 100644
+--- a/include/net/ipv6_frag.h
++++ b/include/net/ipv6_frag.h
+@@ -68,9 +68,6 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
+       struct sk_buff *head;
+       rcu_read_lock();
+-      /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */
+-      if (READ_ONCE(fq->q.fqdir->dead))
+-              goto out_rcu_unlock;
+       spin_lock(&fq->q.lock);
+       if (fq->q.flags & INET_FRAG_COMPLETE)
+@@ -79,6 +76,12 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
+       fq->q.flags |= INET_FRAG_DROP;
+       inet_frag_kill(&fq->q);
++      /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */
++      if (READ_ONCE(fq->q.fqdir->dead)) {
++              inet_frag_queue_flush(&fq->q, 0);
++              goto out;
++      }
++
+       dev = dev_get_by_index_rcu(net, fq->iif);
+       if (!dev)
+               goto out;
+diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
+index 70640906337757..f9cf20b21a0781 100644
+--- a/net/ipv4/inet_fragment.c
++++ b/net/ipv4/inet_fragment.c
+@@ -219,6 +219,41 @@ static int __init inet_frag_wq_init(void)
+ pure_initcall(inet_frag_wq_init);
++void fqdir_pre_exit(struct fqdir *fqdir)
++{
++      struct inet_frag_queue *fq;
++      struct rhashtable_iter hti;
++
++      /* Prevent creation of new frags.
++       * Pairs with READ_ONCE() in inet_frag_find().
++       */
++      WRITE_ONCE(fqdir->high_thresh, 0);
++
++      /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire()
++       * and ip6frag_expire_frag_queue().
++       */
++      WRITE_ONCE(fqdir->dead, true);
++
++      rhashtable_walk_enter(&fqdir->rhashtable, &hti);
++      rhashtable_walk_start(&hti);
++
++      while ((fq = rhashtable_walk_next(&hti))) {
++              if (IS_ERR(fq)) {
++                      if (PTR_ERR(fq) != -EAGAIN)
++                              break;
++                      continue;
++              }
++              spin_lock_bh(&fq->lock);
++              if (!(fq->flags & INET_FRAG_COMPLETE))
++                      inet_frag_queue_flush(fq, 0);
++              spin_unlock_bh(&fq->lock);
++      }
++
++      rhashtable_walk_stop(&hti);
++      rhashtable_walk_exit(&hti);
++}
++EXPORT_SYMBOL(fqdir_pre_exit);
++
+ void fqdir_exit(struct fqdir *fqdir)
+ {
+       INIT_WORK(&fqdir->destroy_work, fqdir_work_fn);
+@@ -291,6 +326,7 @@ void inet_frag_queue_flush(struct inet_frag_queue *q,
+ {
+       unsigned int sum;
++      reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT;
+       sum = inet_frag_rbtree_purge(&q->rb_fragments, reason);
+       sub_frag_mem_limit(q->fqdir, sum);
+ }
+diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
+index eb5f6060b85d52..124c0d64d4204b 100644
+--- a/net/ipv4/ip_fragment.c
++++ b/net/ipv4/ip_fragment.c
+@@ -148,11 +148,6 @@ static void ip_expire(struct timer_list *t)
+       net = qp->q.fqdir->net;
+       rcu_read_lock();
+-
+-      /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */
+-      if (READ_ONCE(qp->q.fqdir->dead))
+-              goto out_rcu_unlock;
+-
+       spin_lock(&qp->q.lock);
+       if (qp->q.flags & INET_FRAG_COMPLETE)
+@@ -160,6 +155,13 @@ static void ip_expire(struct timer_list *t)
+       qp->q.flags |= INET_FRAG_DROP;
+       ipq_kill(qp);
++
++      /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */
++      if (READ_ONCE(qp->q.fqdir->dead)) {
++              inet_frag_queue_flush(&qp->q, 0);
++              goto out;
++      }
++
+       __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
+       __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT);
+-- 
+2.53.0
+
diff --git a/queue-6.12/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch b/queue-6.12/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch
new file mode 100644 (file)
index 0000000..19becad
--- /dev/null
@@ -0,0 +1,759 @@
+From 696344ed3f941bb466477beb5ecb66d31998a47e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 20 Dec 2025 10:33:26 +0000
+Subject: media: rc: fix race between unregister and urb/irq callbacks
+
+From: Sean Young <sean@mess.org>
+
+[ Upstream commit dccc0c3ddf8f16071736f98a7d6dd46a2d43e037 ]
+
+Some rc device drivers have a race condition between rc_unregister_device()
+and irq or urb callbacks. This is because rc_unregister_device() does two
+things, it marks the device as unregistered so no new commands can be
+issued and then it calls rc_free_device(). This means the driver has no
+chance to cancel any pending urb callbacks or interrupts after the device
+has been marked as unregistered. Those callbacks may access struct rc_dev
+or its members (e.g. struct ir_raw_event_ctrl), which have been freed by
+rc_free_device().
+
+This change removes the implicit call to rc_free_device() from
+rc_unregister_device(). This means that device drivers can call
+rc_unregister_device() in their remove or disconnect function, then cancel
+all the urbs and interrupts before explicitly calling rc_free_device().
+
+Note this is an alternative fix for an issue found by Haotian Zhang, see
+the Closes: tags.
+
+Reported-by: Haotian Zhang <vulab@iscas.ac.cn>
+Closes: https://lore.kernel.org/linux-media/20251114101432.2566-1-vulab@iscas.ac.cn/
+Closes: https://lore.kernel.org/linux-media/20251114101418.2548-1-vulab@iscas.ac.cn/
+Closes: https://lore.kernel.org/linux-media/20251114101346.2530-1-vulab@iscas.ac.cn/
+Closes: https://lore.kernel.org/linux-media/20251114090605.2413-1-vulab@iscas.ac.cn/
+Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Stable-dep-of: 646ebdd31058 ("media: rc: ttusbir: fix inverted error logic")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/bridge/sil-sii8620.c        | 1 +
+ drivers/hid/hid-picolcd_cir.c               | 1 +
+ drivers/media/cec/core/cec-core.c           | 2 +-
+ drivers/media/common/siano/smsir.c          | 1 +
+ drivers/media/i2c/ir-kbd-i2c.c              | 2 ++
+ drivers/media/pci/bt8xx/bttv-input.c        | 3 ++-
+ drivers/media/pci/cx23885/cx23885-input.c   | 1 +
+ drivers/media/pci/cx88/cx88-input.c         | 3 ++-
+ drivers/media/pci/dm1105/dm1105.c           | 1 +
+ drivers/media/pci/mantis/mantis_input.c     | 1 +
+ drivers/media/pci/saa7134/saa7134-input.c   | 1 +
+ drivers/media/pci/smipcie/smipcie-ir.c      | 1 +
+ drivers/media/pci/ttpci/budget-ci.c         | 1 +
+ drivers/media/rc/ati_remote.c               | 6 +++---
+ drivers/media/rc/ene_ir.c                   | 2 +-
+ drivers/media/rc/fintek-cir.c               | 3 ++-
+ drivers/media/rc/igorplugusb.c              | 1 +
+ drivers/media/rc/iguanair.c                 | 1 +
+ drivers/media/rc/img-ir/img-ir-hw.c         | 3 ++-
+ drivers/media/rc/img-ir/img-ir-raw.c        | 3 ++-
+ drivers/media/rc/imon.c                     | 3 ++-
+ drivers/media/rc/ir-hix5hd2.c               | 2 +-
+ drivers/media/rc/ir_toy.c                   | 1 +
+ drivers/media/rc/ite-cir.c                  | 2 +-
+ drivers/media/rc/mceusb.c                   | 1 +
+ drivers/media/rc/rc-ir-raw.c                | 5 -----
+ drivers/media/rc/rc-loopback.c              | 1 +
+ drivers/media/rc/rc-main.c                  | 6 +-----
+ drivers/media/rc/redrat3.c                  | 4 +++-
+ drivers/media/rc/st_rc.c                    | 2 +-
+ drivers/media/rc/streamzap.c                | 7 ++++---
+ drivers/media/rc/sunxi-cir.c                | 1 +
+ drivers/media/rc/ttusbir.c                  | 2 +-
+ drivers/media/rc/winbond-cir.c              | 2 +-
+ drivers/media/rc/xbox_remote.c              | 5 +++--
+ drivers/media/usb/au0828/au0828-input.c     | 1 +
+ drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 1 +
+ drivers/media/usb/dvb-usb/dvb-usb-remote.c  | 6 ++++--
+ drivers/media/usb/em28xx/em28xx-input.c     | 1 +
+ drivers/staging/media/av7110/av7110_ir.c    | 1 +
+ include/media/rc-core.h                     | 2 --
+ 41 files changed, 58 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
+index 26b8d137bce096..2baeb1c5301ead 100644
+--- a/drivers/gpu/drm/bridge/sil-sii8620.c
++++ b/drivers/gpu/drm/bridge/sil-sii8620.c
+@@ -2220,6 +2220,7 @@ static void sii8620_detach(struct drm_bridge *bridge)
+               return;
+       rc_unregister_device(ctx->rc_dev);
++      rc_free_device(ctx->rc_dev);
+ }
+ static int sii8620_is_packing_required(struct sii8620 *ctx,
+diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
+index d6faa0e00f95ac..6d4c636e1c9f7e 100644
+--- a/drivers/hid/hid-picolcd_cir.c
++++ b/drivers/hid/hid-picolcd_cir.c
+@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data)
+       data->rc_dev = NULL;
+       rc_unregister_device(rdev);
++      rc_free_device(rdev);
+ }
+diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
+index 865d86f34add0e..b3c0710eb5b18d 100644
+--- a/drivers/media/cec/core/cec-core.c
++++ b/drivers/media/cec/core/cec-core.c
+@@ -337,8 +337,8 @@ int cec_register_adapter(struct cec_adapter *adap,
+       res = cec_devnode_register(&adap->devnode, adap->owner);
+       if (res) {
+ #ifdef CONFIG_MEDIA_CEC_RC
+-              /* Note: rc_unregister also calls rc_free */
+               rc_unregister_device(adap->rc);
++              rc_free_device(adap->rc);
+               adap->rc = NULL;
+ #endif
+               return res;
+diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
+index d85c78c104b990..5f4c0aa7a0d72a 100644
+--- a/drivers/media/common/siano/smsir.c
++++ b/drivers/media/common/siano/smsir.c
+@@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
+ void sms_ir_exit(struct smscore_device_t *coredev)
+ {
+       rc_unregister_device(coredev->ir.dev);
++      rc_free_device(coredev->ir.dev);
+       pr_debug("\n");
+ }
+diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
+index 5588cdd7ec20de..60474531700433 100644
+--- a/drivers/media/i2c/ir-kbd-i2c.c
++++ b/drivers/media/i2c/ir-kbd-i2c.c
+@@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work)
+               mutex_unlock(&ir->lock);
+               if (rc == -ENODEV) {
+                       rc_unregister_device(ir->rc);
++                      rc_free_device(ir->rc);
+                       ir->rc = NULL;
+                       return;
+               }
+@@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client)
+       i2c_unregister_device(ir->tx_c);
+       rc_unregister_device(ir->rc);
++      rc_free_device(ir->rc);
+ }
+ static const struct i2c_device_id ir_kbd_id[] = {
+diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
+index 41226f1d0e5b64..d70e6282c48b2e 100644
+--- a/drivers/media/pci/bt8xx/bttv-input.c
++++ b/drivers/media/pci/bt8xx/bttv-input.c
+@@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv)
+       if (btv->remote == NULL)
+               return;
+-      bttv_ir_stop(btv);
+       rc_unregister_device(btv->remote->dev);
++      bttv_ir_stop(btv);
++      rc_free_device(btv->remote->dev);
+       kfree(btv->remote);
+       btv->remote = NULL;
+ }
+diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
+index d2e84c6457e0ab..722329ef3fd2cc 100644
+--- a/drivers/media/pci/cx23885/cx23885-input.c
++++ b/drivers/media/pci/cx23885/cx23885-input.c
+@@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev)
+       if (dev->kernel_ir == NULL)
+               return;
+       rc_unregister_device(dev->kernel_ir->rc);
++      rc_free_device(dev->kernel_ir->rc);
+       kfree(dev->kernel_ir->phys);
+       kfree(dev->kernel_ir->name);
+       kfree(dev->kernel_ir);
+diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
+index a04a1d33fadb1b..74a8769dd6c79c 100644
+--- a/drivers/media/pci/cx88/cx88-input.c
++++ b/drivers/media/pci/cx88/cx88-input.c
+@@ -510,8 +510,9 @@ int cx88_ir_fini(struct cx88_core *core)
+       if (!ir)
+               return 0;
+-      cx88_ir_stop(core);
+       rc_unregister_device(ir->dev);
++      cx88_ir_stop(core);
++      rc_free_device(ir->dev);
+       kfree(ir);
+       /* done */
+diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
+index 9e9c7c071accce..e1185aa669f480 100644
+--- a/drivers/media/pci/dm1105/dm1105.c
++++ b/drivers/media/pci/dm1105/dm1105.c
+@@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105)
+ static void dm1105_ir_exit(struct dm1105_dev *dm1105)
+ {
+       rc_unregister_device(dm1105->ir.dev);
++      rc_free_device(dm1105->ir.dev);
+ }
+ static int dm1105_hw_init(struct dm1105_dev *dev)
+diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c
+index 34c0d979240fda..edb4cacf55d229 100644
+--- a/drivers/media/pci/mantis/mantis_input.c
++++ b/drivers/media/pci/mantis/mantis_input.c
+@@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init);
+ void mantis_input_exit(struct mantis_pci *mantis)
+ {
+       rc_unregister_device(mantis->rc);
++      rc_free_device(mantis->rc);
+ }
+ EXPORT_SYMBOL_GPL(mantis_input_exit);
+diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
+index 8610eb473b39e1..8a0f26d94d1de8 100644
+--- a/drivers/media/pci/saa7134/saa7134-input.c
++++ b/drivers/media/pci/saa7134/saa7134-input.c
+@@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
+               return;
+       rc_unregister_device(dev->remote->dev);
++      rc_free_device(dev->remote->dev);
+       kfree(dev->remote);
+       dev->remote = NULL;
+ }
+diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c
+index c0604d9c70119a..0bbe4fa2d5a84e 100644
+--- a/drivers/media/pci/smipcie/smipcie-ir.c
++++ b/drivers/media/pci/smipcie/smipcie-ir.c
+@@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev)
+       rc_unregister_device(rc_dev);
+       smi_ir_stop(ir);
++      rc_free_device(rc_dev);
+       ir->rc_dev = NULL;
+ }
+diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
+index 33f08adf4feb10..16973ac8e6a920 100644
+--- a/drivers/media/pci/ttpci/budget-ci.c
++++ b/drivers/media/pci/ttpci/budget-ci.c
+@@ -249,6 +249,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
+       cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work);
+       rc_unregister_device(budget_ci->ir.dev);
++      rc_free_device(budget_ci->ir.dev);
+ }
+ static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
+index d7721e60776edd..46d1844f5c9873 100644
+--- a/drivers/media/rc/ati_remote.c
++++ b/drivers/media/rc/ati_remote.c
+@@ -921,7 +921,6 @@ static int ati_remote_probe(struct usb_interface *interface,
+       input_free_device(input_dev);
+  exit_unregister_device:
+       rc_unregister_device(rc_dev);
+-      rc_dev = NULL;
+  exit_kill_urbs:
+       usb_kill_urb(ati_remote->irq_urb);
+       usb_kill_urb(ati_remote->out_urb);
+@@ -941,18 +940,19 @@ static void ati_remote_disconnect(struct usb_interface *interface)
+       struct ati_remote *ati_remote;
+       ati_remote = usb_get_intfdata(interface);
+-      usb_set_intfdata(interface, NULL);
+       if (!ati_remote) {
+               dev_warn(&interface->dev, "%s - null device?\n", __func__);
+               return;
+       }
++      rc_unregister_device(ati_remote->rdev);
++      usb_set_intfdata(interface, NULL);
+       usb_kill_urb(ati_remote->irq_urb);
+       usb_kill_urb(ati_remote->out_urb);
+       if (ati_remote->idev)
+               input_unregister_device(ati_remote->idev);
+-      rc_unregister_device(ati_remote->rdev);
+       ati_remote_free_buffers(ati_remote);
++      rc_free_device(ati_remote->rdev);
+       kfree(ati_remote);
+ }
+diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
+index 67722e2e47ff78..3fd51a41c3b2b6 100644
+--- a/drivers/media/rc/ene_ir.c
++++ b/drivers/media/rc/ene_ir.c
+@@ -1090,7 +1090,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
+       release_region(dev->hw_io, ENE_IO_SIZE);
+ exit_unregister_device:
+       rc_unregister_device(rdev);
+-      rdev = NULL;
+ exit_free_dev_rdev:
+       rc_free_device(rdev);
+       kfree(dev);
+@@ -1110,6 +1109,7 @@ static void ene_remove(struct pnp_dev *pnp_dev)
+       ene_rx_restore_hw_buffer(dev);
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
++      rc_free_device(dev->rdev);
+       free_irq(dev->irq, dev);
+       release_region(dev->hw_io, ENE_IO_SIZE);
+       kfree(dev);
+diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
+index 3fb0968efd57d3..9b789097cdd4c3 100644
+--- a/drivers/media/rc/fintek-cir.c
++++ b/drivers/media/rc/fintek-cir.c
+@@ -568,6 +568,7 @@ static void fintek_remove(struct pnp_dev *pdev)
+       struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+       unsigned long flags;
++      rc_unregister_device(fintek->rdev);
+       spin_lock_irqsave(&fintek->fintek_lock, flags);
+       /* disable CIR */
+       fintek_disable_cir(fintek);
+@@ -580,7 +581,7 @@ static void fintek_remove(struct pnp_dev *pdev)
+       free_irq(fintek->cir_irq, fintek);
+       release_region(fintek->cir_addr, fintek->cir_port_len);
+-      rc_unregister_device(fintek->rdev);
++      rc_free_device(fintek->rdev);
+       kfree(fintek);
+ }
+diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c
+index f3616607d4f52b..8bf059b9a31b45 100644
+--- a/drivers/media/rc/igorplugusb.c
++++ b/drivers/media/rc/igorplugusb.c
+@@ -247,6 +247,7 @@ static void igorplugusb_disconnect(struct usb_interface *intf)
+       usb_set_intfdata(intf, NULL);
+       usb_unpoison_urb(ir->urb);
+       usb_free_urb(ir->urb);
++      rc_free_device(ir->rc);
+       kfree(ir->buf_in);
+       kfree(ir->request);
+ }
+diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
+index 8af94246e5916e..7bd6dd7254157a 100644
+--- a/drivers/media/rc/iguanair.c
++++ b/drivers/media/rc/iguanair.c
+@@ -500,6 +500,7 @@ static void iguanair_disconnect(struct usb_interface *intf)
+       usb_set_intfdata(intf, NULL);
+       usb_kill_urb(ir->urb_in);
+       usb_kill_urb(ir->urb_out);
++      rc_free_device(ir->rc);
+       usb_free_urb(ir->urb_in);
+       usb_free_urb(ir->urb_out);
+       usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
+diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
+index 5da7479c1793b1..07f41372976eaf 100644
+--- a/drivers/media/rc/img-ir/img-ir-hw.c
++++ b/drivers/media/rc/img-ir/img-ir-hw.c
+@@ -1117,9 +1117,10 @@ void img_ir_remove_hw(struct img_ir_priv *priv)
+       struct rc_dev *rdev = hw->rdev;
+       if (!rdev)
+               return;
++      rc_unregister_device(rdev);
+       img_ir_set_decoder(priv, NULL, 0);
+       hw->rdev = NULL;
+-      rc_unregister_device(rdev);
++      rc_free_device(rdev);
+ #ifdef CONFIG_COMMON_CLK
+       if (!IS_ERR(priv->clk))
+               clk_notifier_unregister(priv->clk, &hw->clk_nb);
+diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c
+index 8b0bdd9603b3c5..533d40dae54224 100644
+--- a/drivers/media/rc/img-ir/img-ir-raw.c
++++ b/drivers/media/rc/img-ir/img-ir-raw.c
+@@ -136,6 +136,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv)
+       if (!rdev)
+               return;
++      rc_unregister_device(rdev);
+       /* switch off and disable raw (edge) interrupts */
+       spin_lock_irq(&priv->lock);
+       raw->rdev = NULL;
+@@ -145,7 +146,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv)
+       img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
+       spin_unlock_irq(&priv->lock);
+-      rc_unregister_device(rdev);
++      rc_free_device(rdev);
+       del_timer_sync(&raw->timer);
+ }
+diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
+index ddb1304cb77b82..cb9bd5a6ff54f7 100644
+--- a/drivers/media/rc/imon.c
++++ b/drivers/media/rc/imon.c
+@@ -2546,9 +2546,10 @@ static void imon_disconnect(struct usb_interface *interface)
+       if (ifnum == 0) {
+               ictx->dev_present_intf0 = false;
++              rc_unregister_device(ictx->rdev);
+               usb_kill_urb(ictx->rx_urb_intf0);
+               input_unregister_device(ictx->idev);
+-              rc_unregister_device(ictx->rdev);
++              rc_free_device(ictx->rdev);
+               if (ictx->display_supported) {
+                       if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
+                               usb_deregister_dev(interface, &imon_lcd_class);
+diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
+index de5bb9a08ea4c0..1604679fa2c807 100644
+--- a/drivers/media/rc/ir-hix5hd2.c
++++ b/drivers/media/rc/ir-hix5hd2.c
+@@ -331,7 +331,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
+ regerr:
+       rc_unregister_device(rdev);
+-      rdev = NULL;
+ clkerr:
+       clk_disable_unprepare(priv->clock);
+ err:
+@@ -346,6 +345,7 @@ static void hix5hd2_ir_remove(struct platform_device *pdev)
+       clk_disable_unprepare(priv->clock);
+       rc_unregister_device(priv->rdev);
++      rc_free_device(priv->rdev);
+ }
+ #ifdef CONFIG_PM_SLEEP
+diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c
+index 533faa11751744..e79de56997a426 100644
+--- a/drivers/media/rc/ir_toy.c
++++ b/drivers/media/rc/ir_toy.c
+@@ -536,6 +536,7 @@ static void irtoy_disconnect(struct usb_interface *intf)
+       usb_free_urb(ir->urb_out);
+       usb_kill_urb(ir->urb_in);
+       usb_free_urb(ir->urb_in);
++      rc_free_device(ir->rc);
+       kfree(ir->in);
+       kfree(ir->out);
+       kfree(ir);
+diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
+index 2bacecb022623e..23afbafb557488 100644
+--- a/drivers/media/rc/ite-cir.c
++++ b/drivers/media/rc/ite-cir.c
+@@ -1414,7 +1414,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
+       release_region(itdev->cir_addr, itdev->params->io_region_size);
+ exit_unregister_device:
+       rc_unregister_device(rdev);
+-      rdev = NULL;
+ exit_free_dev_rdev:
+       rc_free_device(rdev);
+       kfree(itdev);
+@@ -1439,6 +1438,7 @@ static void ite_remove(struct pnp_dev *pdev)
+       release_region(dev->cir_addr, dev->params->io_region_size);
+       rc_unregister_device(dev->rdev);
++      rc_free_device(dev->rdev);
+       kfree(dev);
+ }
+diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
+index cd7af4d88b7f77..bf93b94d337fea 100644
+--- a/drivers/media/rc/mceusb.c
++++ b/drivers/media/rc/mceusb.c
+@@ -1851,6 +1851,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf)
+       usb_free_urb(ir->urb_in);
+       usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
+       usb_put_dev(dev);
++      rc_free_device(ir->rc);
+       kfree(ir);
+ }
+diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
+index 16e33d7eaaa2d8..dd70f8ad526642 100644
+--- a/drivers/media/rc/rc-ir-raw.c
++++ b/drivers/media/rc/rc-ir-raw.c
+@@ -647,9 +647,6 @@ int ir_raw_event_register(struct rc_dev *dev)
+ void ir_raw_event_free(struct rc_dev *dev)
+ {
+-      if (!dev)
+-              return;
+-
+       kfree(dev->raw);
+       dev->raw = NULL;
+ }
+@@ -673,8 +670,6 @@ void ir_raw_event_unregister(struct rc_dev *dev)
+       lirc_bpf_free(dev);
+-      ir_raw_event_free(dev);
+-
+       /*
+        * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so
+        * ensure that the raw member is null on unlock; this is how
+diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
+index 8288366f891fc9..a108b057b5fd56 100644
+--- a/drivers/media/rc/rc-loopback.c
++++ b/drivers/media/rc/rc-loopback.c
+@@ -263,6 +263,7 @@ static int __init loop_init(void)
+ static void __exit loop_exit(void)
+ {
+       rc_unregister_device(loopdev.dev);
++      rc_free_device(loopdev.dev);
+ }
+ module_init(loop_init);
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index a4c539b17cf344..a4c0ec06ee6aa3 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -1611,6 +1611,7 @@ static void rc_dev_release(struct device *device)
+ {
+       struct rc_dev *dev = to_rc_dev(device);
++      ir_raw_event_free(dev);
+       kfree(dev);
+ }
+@@ -1773,7 +1774,6 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev,
+       }
+       rc->dev.parent = dev;
+-      rc->managed_alloc = true;
+       *dr = rc;
+       devres_add(dev, dr);
+@@ -2042,11 +2042,7 @@ void rc_unregister_device(struct rc_dev *dev)
+       device_del(&dev->dev);
+       ida_free(&rc_ida, dev->minor);
+-
+-      if (!dev->managed_alloc)
+-              rc_free_device(dev);
+ }
+-
+ EXPORT_SYMBOL_GPL(rc_unregister_device);
+ /*
+diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
+index a49173f54a4d0e..b8289327f6a206 100644
+--- a/drivers/media/rc/redrat3.c
++++ b/drivers/media/rc/redrat3.c
+@@ -1133,11 +1133,13 @@ static void redrat3_dev_disconnect(struct usb_interface *intf)
+ {
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct redrat3_dev *rr3 = usb_get_intfdata(intf);
++      struct rc_dev *rc = rr3->rc;
+       usb_set_intfdata(intf, NULL);
+-      rc_unregister_device(rr3->rc);
++      rc_unregister_device(rc);
+       led_classdev_unregister(&rr3->led);
+       redrat3_delete(rr3, udev);
++      rc_free_device(rc);
+ }
+ static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
+diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
+index fd2f056f287b25..79aad3d7f69f0d 100644
+--- a/drivers/media/rc/st_rc.c
++++ b/drivers/media/rc/st_rc.c
+@@ -203,6 +203,7 @@ static void st_rc_remove(struct platform_device *pdev)
+       device_init_wakeup(&pdev->dev, false);
+       clk_disable_unprepare(rc_dev->sys_clock);
+       rc_unregister_device(rc_dev->rdev);
++      rc_free_device(rc_dev->rdev);
+ }
+ static int st_rc_open(struct rc_dev *rdev)
+@@ -334,7 +335,6 @@ static int st_rc_probe(struct platform_device *pdev)
+       return ret;
+ rcerr:
+       rc_unregister_device(rdev);
+-      rdev = NULL;
+ clkerr:
+       clk_disable_unprepare(rc_dev->sys_clock);
+ err:
+diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
+index 8e9b156e430022..8c85b9f30a3a96 100644
+--- a/drivers/media/rc/streamzap.c
++++ b/drivers/media/rc/streamzap.c
+@@ -392,15 +392,16 @@ static void streamzap_disconnect(struct usb_interface *interface)
+       struct streamzap_ir *sz = usb_get_intfdata(interface);
+       struct usb_device *usbdev = interface_to_usbdev(interface);
+-      usb_set_intfdata(interface, NULL);
+-
+       if (!sz)
+               return;
+-      usb_kill_urb(sz->urb_in);
+       rc_unregister_device(sz->rdev);
++      usb_set_intfdata(interface, NULL);
++
++      usb_kill_urb(sz->urb_in);
+       usb_free_urb(sz->urb_in);
+       usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
++      rc_free_device(sz->rdev);
+       kfree(sz);
+ }
+diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
+index b49df8355e6b39..448d453cfda934 100644
+--- a/drivers/media/rc/sunxi-cir.c
++++ b/drivers/media/rc/sunxi-cir.c
+@@ -371,6 +371,7 @@ static void sunxi_ir_remove(struct platform_device *pdev)
+       struct sunxi_ir *ir = platform_get_drvdata(pdev);
+       rc_unregister_device(ir->rc);
++      rc_free_device(ir->rc);
+       sunxi_ir_hw_exit(&pdev->dev);
+ }
+diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
+index dde446a95eaa93..a670d4b008cb0d 100644
+--- a/drivers/media/rc/ttusbir.c
++++ b/drivers/media/rc/ttusbir.c
+@@ -336,7 +336,6 @@ static int ttusbir_probe(struct usb_interface *intf,
+       return 0;
+ out3:
+       rc_unregister_device(rc);
+-      rc = NULL;
+ out2:
+       led_classdev_unregister(&tt->led);
+ out:
+@@ -378,6 +377,7 @@ static void ttusbir_disconnect(struct usb_interface *intf)
+       usb_kill_urb(tt->bulk_urb);
+       usb_free_urb(tt->bulk_urb);
+       kfree(tt->bulk_buffer);
++      rc_free_device(tt->rc);
+       usb_set_intfdata(intf, NULL);
+       kfree(tt);
+ }
+diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
+index 25884a79985c8a..14d8b58e283980 100644
+--- a/drivers/media/rc/winbond-cir.c
++++ b/drivers/media/rc/winbond-cir.c
+@@ -1132,7 +1132,6 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
+       release_region(data->wbase, WAKEUP_IOMEM_LEN);
+ exit_unregister_device:
+       rc_unregister_device(data->dev);
+-      data->dev = NULL;
+ exit_free_rc:
+       rc_free_device(data->dev);
+ exit_unregister_led:
+@@ -1163,6 +1162,7 @@ wbcir_remove(struct pnp_dev *device)
+       wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+       rc_unregister_device(data->dev);
++      rc_free_device(data->dev);
+       led_classdev_unregister(&data->led);
+diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c
+index 0c9c855ced729c..80b7c247932a8f 100644
+--- a/drivers/media/rc/xbox_remote.c
++++ b/drivers/media/rc/xbox_remote.c
+@@ -283,14 +283,15 @@ static void xbox_remote_disconnect(struct usb_interface *interface)
+       struct xbox_remote *xbox_remote;
+       xbox_remote = usb_get_intfdata(interface);
+-      usb_set_intfdata(interface, NULL);
+       if (!xbox_remote) {
+               dev_warn(&interface->dev, "%s - null device?\n", __func__);
+               return;
+       }
+-      usb_kill_urb(xbox_remote->irq_urb);
+       rc_unregister_device(xbox_remote->rdev);
++      usb_set_intfdata(interface, NULL);
++      usb_kill_urb(xbox_remote->irq_urb);
++      rc_free_device(xbox_remote->rdev);
+       usb_free_urb(xbox_remote->irq_urb);
+       kfree(xbox_remote->inbuf);
+       kfree(xbox_remote);
+diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c
+index 3d3368202cd018..283ad2c6288cd5 100644
+--- a/drivers/media/usb/au0828/au0828-input.c
++++ b/drivers/media/usb/au0828/au0828-input.c
+@@ -357,6 +357,7 @@ void au0828_rc_unregister(struct au0828_dev *dev)
+               return;
+       rc_unregister_device(ir->rc);
++      rc_free_device(ir->rc);
+       /* done */
+       kfree(ir);
+diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+index f1c79f351ec8de..17e8961179d14b 100644
+--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
++++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+@@ -187,6 +187,7 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d)
+       if (d->rc_dev) {
+               cancel_delayed_work_sync(&d->rc_query_work);
+               rc_unregister_device(d->rc_dev);
++              rc_free_device(d->rc_dev);
+               d->rc_dev = NULL;
+       }
+diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+index 65e2c9e2cdc99f..6dc11718dfb985 100644
+--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c
++++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+@@ -347,10 +347,12 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d)
+ {
+       if (d->state & DVB_USB_STATE_REMOTE) {
+               cancel_delayed_work_sync(&d->rc_query_work);
+-              if (d->props.rc.mode == DVB_RC_LEGACY)
++              if (d->props.rc.mode == DVB_RC_LEGACY) {
+                       input_unregister_device(d->input_dev);
+-              else
++              } else {
+                       rc_unregister_device(d->rc_dev);
++                      rc_free_device(d->rc_dev);
++              }
+       }
+       d->state &= ~DVB_USB_STATE_REMOTE;
+       return 0;
+diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
+index 5f3b00869bdbc9..26f333b5be7325 100644
+--- a/drivers/media/usb/em28xx/em28xx-input.c
++++ b/drivers/media/usb/em28xx/em28xx-input.c
+@@ -853,6 +853,7 @@ static int em28xx_ir_fini(struct em28xx *dev)
+               goto ref_put;
+       rc_unregister_device(ir->rc);
++      rc_free_device(ir->rc);
+       kfree(ir->i2c_client);
+diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c
+index 68b3979ba5f20c..fdae467fd7ab81 100644
+--- a/drivers/staging/media/av7110/av7110_ir.c
++++ b/drivers/staging/media/av7110/av7110_ir.c
+@@ -151,6 +151,7 @@ int av7110_ir_init(struct av7110 *av7110)
+ void av7110_ir_exit(struct av7110 *av7110)
+ {
+       rc_unregister_device(av7110->ir.rcdev);
++      rc_free_device(av7110->ir.rcdev);
+ }
+ //MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
+diff --git a/include/media/rc-core.h b/include/media/rc-core.h
+index d095908073ef9d..7a8511d0d4b4a4 100644
+--- a/include/media/rc-core.h
++++ b/include/media/rc-core.h
+@@ -81,7 +81,6 @@ struct lirc_fh {
+ /**
+  * struct rc_dev - represents a remote control device
+  * @dev: driver model's view of this device
+- * @managed_alloc: devm_rc_allocate_device was used to create rc_dev
+  * @sysfs_groups: sysfs attribute groups
+  * @device_name: name of the rc child device
+  * @input_phys: physical path to the input child device
+@@ -156,7 +155,6 @@ struct lirc_fh {
+  */
+ struct rc_dev {
+       struct device                   dev;
+-      bool                            managed_alloc;
+       const struct attribute_group    *sysfs_groups[5];
+       const char                      *device_name;
+       const char                      *input_phys;
+-- 
+2.53.0
+
diff --git a/queue-6.12/media-rc-ttusbir-fix-inverted-error-logic.patch b/queue-6.12/media-rc-ttusbir-fix-inverted-error-logic.patch
new file mode 100644 (file)
index 0000000..f239c47
--- /dev/null
@@ -0,0 +1,38 @@
+From 491f8165e6392cf906f3dc941f01c4afd1c84427 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Apr 2026 23:03:09 +0200
+Subject: media: rc: ttusbir: fix inverted error logic
+
+From: Oliver Neukum <oneukum@suse.com>
+
+[ Upstream commit 646ebdd3105809d84ed04aa9e92e47e89cc44502 ]
+
+We have to report ENOMEM if no buffer is allocated.
+Typo dropped a "!". Restore it.
+
+Fixes: 50acaad3d202 ("media: rc: ttusbir: respect DMA coherency rules")
+Cc: stable@vger.kernel.org
+Signed-off-by: Oliver Neukum <oneukum@suse.com>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/media/rc/ttusbir.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
+index a670d4b008cb0d..3452b5aefd2848 100644
+--- a/drivers/media/rc/ttusbir.c
++++ b/drivers/media/rc/ttusbir.c
+@@ -191,7 +191,7 @@ static int ttusbir_probe(struct usb_interface *intf,
+       tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+       buffer = kzalloc(5, GFP_KERNEL);
+       rc = rc_allocate_device(RC_DRIVER_IR_RAW);
+-      if (!tt || !rc || buffer) {
++      if (!tt || !rc || !buffer) {
+               ret = -ENOMEM;
+               goto out;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.12/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch b/queue-6.12/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch
new file mode 100644 (file)
index 0000000..82e814d
--- /dev/null
@@ -0,0 +1,67 @@
+From adfba1244f2825484fdd8949ef2b4e8217109b61 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:02:30 +0800
+Subject: mm/page_alloc: clear page->private in free_pages_prepare()
+
+From: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
+
+[ Upstream commit ac1ea219590c09572ed5992dc233bbf7bb70fef9 ]
+
+Several subsystems (slub, shmem, ttm, etc.) use page->private but don't
+clear it before freeing pages.  When these pages are later allocated as
+high-order pages and split via split_page(), tail pages retain stale
+page->private values.
+
+This causes a use-after-free in the swap subsystem.  The swap code uses
+page->private to track swap count continuations, assuming freshly
+allocated pages have page->private == 0.  When stale values are present,
+swap_count_continued() incorrectly assumes the continuation list is valid
+and iterates over uninitialized page->lru containing LIST_POISON values,
+causing a crash:
+
+  KASAN: maybe wild-memory-access in range [0xdead000000000100-0xdead000000000107]
+  RIP: 0010:__do_sys_swapoff+0x1151/0x1860
+
+Fix this by clearing page->private in free_pages_prepare(), ensuring all
+freed pages have clean state regardless of previous use.
+
+Link: https://lkml.kernel.org/r/20260207173615.146159-1-mikhail.v.gavrilov@gmail.com
+Fixes: 3b8000ae185c ("mm/vmalloc: huge vmalloc backing pages should be split rather than compound")
+Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
+Suggested-by: Zi Yan <ziy@nvidia.com>
+Acked-by: Zi Yan <ziy@nvidia.com>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: Brendan Jackman <jackmanb@google.com>
+Cc: Chris Li <chrisl@kernel.org>
+Cc: Hugh Dickins <hughd@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Kairui Song <ryncsn@gmail.com>
+Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Nicholas Piggin <npiggin@gmail.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+[backport: context only]
+Signed-off-by: Li Wang <li.wang@windriver.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/page_alloc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/mm/page_alloc.c b/mm/page_alloc.c
+index b1a8abe5005e99..259249a37faf01 100644
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -1158,6 +1158,7 @@ __always_inline bool free_pages_prepare(struct page *page,
+       page_cpupid_reset_last(page);
+       page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP;
++      page->private = 0;
+       reset_page_owner(page, order);
+       page_table_check_free(page, order);
+       pgalloc_tag_sub(page, 1 << order);
+-- 
+2.53.0
+
diff --git a/queue-6.12/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch b/queue-6.12/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch
new file mode 100644 (file)
index 0000000..4427f37
--- /dev/null
@@ -0,0 +1,64 @@
+From f17c5cf4f79bf980e7ed7511f2d03097bda6dfa7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 23:06:57 -0700
+Subject: perf: Fix dangling cgroup pointer in cpuctx
+
+From: Yeoreum Yun <yeoreum.yun@arm.com>
+
+[ Upstream commit 3b7a34aebbdf2a4b7295205bf0c654294283ec82 ]
+
+Commit a3c3c6667("perf/core: Fix child_total_time_enabled accounting
+bug at task exit") moves the event->state update to before
+list_del_event(). This makes the event->state test in list_del_event()
+always false; never calling perf_cgroup_event_disable().
+
+As a result, cpuctx->cgrp won't be cleared properly; causing havoc.
+
+Fixes: a3c3c6667("perf/core: Fix child_total_time_enabled accounting bug at task exit")
+Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Tested-by: David Wang <00107082@163.com>
+Link: https://lore.kernel.org/all/aD2TspKH%2F7yvfYoO@e129823.arm.com/
+Signed-off-by: Ian Klatzco <iklatzco@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/events/core.c | 16 ++++------------
+ 1 file changed, 4 insertions(+), 12 deletions(-)
+
+diff --git a/kernel/events/core.c b/kernel/events/core.c
+index 6fce2bac6dae52..9099c0cc933be2 100644
+--- a/kernel/events/core.c
++++ b/kernel/events/core.c
+@@ -2096,18 +2096,6 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx)
+       if (event->group_leader == event)
+               del_event_from_groups(event, ctx);
+-      /*
+-       * If event was in error state, then keep it
+-       * that way, otherwise bogus counts will be
+-       * returned on read(). The only way to get out
+-       * of error state is by explicit re-enabling
+-       * of the event
+-       */
+-      if (event->state > PERF_EVENT_STATE_OFF) {
+-              perf_cgroup_event_disable(event, ctx);
+-              perf_event_set_state(event, PERF_EVENT_STATE_OFF);
+-      }
+-
+       ctx->generation++;
+       event->pmu_ctx->nr_events--;
+ }
+@@ -2457,6 +2445,10 @@ __perf_remove_from_context(struct perf_event *event,
+               state = PERF_EVENT_STATE_DEAD;
+       }
+       event_sched_out(event, ctx);
++
++      if (event->state > PERF_EVENT_STATE_OFF)
++              perf_cgroup_event_disable(event, ctx);
++
+       perf_event_set_state(event, min(event->state, state));
+       if (flags & DETACH_GROUP)
+               perf_group_detach(event);
+-- 
+2.53.0
+
diff --git a/queue-6.12/s390-cio-restore-gfp_dma-for-chsc-allocation.patch b/queue-6.12/s390-cio-restore-gfp_dma-for-chsc-allocation.patch
new file mode 100644 (file)
index 0000000..a11d3dc
--- /dev/null
@@ -0,0 +1,152 @@
+From 462f5fe7184fef33160dba35c966ea3cc35a26dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 17:48:55 +0200
+Subject: s390/cio: Restore GFP_DMA for CHSC allocation
+
+From: Peter Oberparleiter <oberpar@linux.ibm.com>
+
+[ Upstream commit ea34567db0a6b3a7ce78ba421592344315c8f90e ]
+
+Re-add GFP_DMA when allocating memory for CHSC control blocks.
+On some supported machines, CHSC cannot access memory outside
+the DMA zone, causing CHSC command failures.
+
+Cc: stable@vger.kernel.org
+Fixes: a3a64a4def8d ("s390/cio: remove unneeded DMA zone allocation")
+Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
+Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
+Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
+[ adjusted context to account for missing commit bf4afc53b77ae ]
+Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/cio/chsc.c     |  4 ++--
+ drivers/s390/cio/chsc_sch.c | 20 ++++++++++----------
+ drivers/s390/cio/scm.c      |  2 +-
+ 3 files changed, 13 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
+index dcc1e1c34ca2e1..8fe6658dcfe1a3 100644
+--- a/drivers/s390/cio/chsc.c
++++ b/drivers/s390/cio/chsc.c
+@@ -1153,8 +1153,8 @@ int __init chsc_init(void)
+ {
+       int ret;
+-      sei_page = (void *)get_zeroed_page(GFP_KERNEL);
+-      chsc_page = (void *)get_zeroed_page(GFP_KERNEL);
++      sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
++      chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sei_page || !chsc_page) {
+               ret = -ENOMEM;
+               goto out_err;
+diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
+index 1e58ee3cc87db1..9131ce3af1b8eb 100644
+--- a/drivers/s390/cio/chsc_sch.c
++++ b/drivers/s390/cio/chsc_sch.c
+@@ -293,7 +293,7 @@ static int chsc_ioctl_start(void __user *user_area)
+       if (!css_general_characteristics.dynio)
+               /* It makes no sense to try. */
+               return -EOPNOTSUPP;
+-      chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
++      chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+       if (!chsc_area)
+               return -ENOMEM;
+       request = kzalloc(sizeof(*request), GFP_KERNEL);
+@@ -341,7 +341,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
+               ret = -ENOMEM;
+               goto out_unlock;
+       }
+-      on_close_chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
++      on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+       if (!on_close_chsc_area) {
+               ret = -ENOMEM;
+               goto out_free_request;
+@@ -393,7 +393,7 @@ static int chsc_ioctl_start_sync(void __user *user_area)
+       struct chsc_sync_area *chsc_area;
+       int ret, ccode;
+-      chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
++      chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!chsc_area)
+               return -ENOMEM;
+       if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
+@@ -439,7 +439,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
+               u8 data[PAGE_SIZE - 20];
+       } __attribute__ ((packed)) *scpcd_area;
+-      scpcd_area = (void *)get_zeroed_page(GFP_KERNEL);
++      scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!scpcd_area)
+               return -ENOMEM;
+       cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+@@ -501,7 +501,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
+               u8 data[PAGE_SIZE - 20];
+       } __attribute__ ((packed)) *scucd_area;
+-      scucd_area = (void *)get_zeroed_page(GFP_KERNEL);
++      scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!scucd_area)
+               return -ENOMEM;
+       cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+@@ -564,7 +564,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
+               u8 data[PAGE_SIZE - 20];
+       } __attribute__ ((packed)) *sscud_area;
+-      sscud_area = (void *)get_zeroed_page(GFP_KERNEL);
++      sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sscud_area)
+               return -ENOMEM;
+       cud = kzalloc(sizeof(*cud), GFP_KERNEL);
+@@ -626,7 +626,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
+               u8 data[PAGE_SIZE - 20];
+       } __attribute__ ((packed)) *sci_area;
+-      sci_area = (void *)get_zeroed_page(GFP_KERNEL);
++      sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sci_area)
+               return -ENOMEM;
+       ci = kzalloc(sizeof(*ci), GFP_KERNEL);
+@@ -697,7 +697,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
+               u32 res;
+       } __attribute__ ((packed)) *cssids_parm;
+-      sccl_area = (void *)get_zeroed_page(GFP_KERNEL);
++      sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sccl_area)
+               return -ENOMEM;
+       ccl = kzalloc(sizeof(*ccl), GFP_KERNEL);
+@@ -757,7 +757,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd)
+       int ret;
+       chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
+-      scpd_area = (void *)get_zeroed_page(GFP_KERNEL);
++      scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!scpd_area || !chpd) {
+               ret = -ENOMEM;
+               goto out_free;
+@@ -797,7 +797,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
+               u8 data[PAGE_SIZE - 36];
+       } __attribute__ ((packed)) *sdcal_area;
+-      sdcal_area = (void *)get_zeroed_page(GFP_KERNEL);
++      sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sdcal_area)
+               return -ENOMEM;
+       dcal = kzalloc(sizeof(*dcal), GFP_KERNEL);
+diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
+index c7894d61306d74..375cbfa31b5373 100644
+--- a/drivers/s390/cio/scm.c
++++ b/drivers/s390/cio/scm.c
+@@ -228,7 +228,7 @@ int scm_update_information(void)
+       size_t num;
+       int ret;
+-      scm_info = (void *)__get_free_page(GFP_KERNEL);
++      scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+       if (!scm_info)
+               return -ENOMEM;
+-- 
+2.53.0
+
index b5c21368e93955fb24372535e623ff108b1c62b9..c58c319a725f7340cfb1401db206a59ca08c8f39 100644 (file)
@@ -92,3 +92,29 @@ sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch
 ipv6-fix-possible-infinite-loop-in-rt6_fill_node.patch
 ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch
 net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch
+perf-fix-dangling-cgroup-pointer-in-cpuctx.patch
+batman-adv-v-stop-ogmv2-on-disabled-interface.patch
+batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
+batman-adv-tt-reject-oversized-local-tvlv-buffers.patch
+batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
+batman-adv-tvlv-reject-oversized-tvlv-packets.patch
+batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
+batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch
+s390-cio-restore-gfp_dma-for-chsc-allocation.patch
+batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch
+batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
+batman-adv-tt-avoid-empty-vlan-responses.patch
+batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
+mm-page_alloc-clear-page-private-in-free_pages_prepa.patch
+media-rc-fix-race-between-unregister-and-urb-irq-cal.patch
+media-rc-ttusbir-fix-inverted-error-logic.patch
+inet-frags-add-inet_frag_queue_flush.patch
+inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch
+hid-core-add-printk_ratelimited-variants-to-hid_warn.patch
+hid-pass-the-buffer-size-to-hid_report_raw_event.patch
+hid-core-introduce-hid_safe_input_report.patch
+hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch
+drm-i915-psr-add-defininitions-for-intel_wa_register.patch
+drm-i915-psr-read-intel-dpcd-workaround-register.patch
+drm-dp-add-edp-1.5-bit-definition.patch
+drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
diff --git a/queue-6.18/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-6.18/drm-i915-psr-add-defininitions-for-intel_wa_register.patch
new file mode 100644 (file)
index 0000000..f96557d
--- /dev/null
@@ -0,0 +1,73 @@
+From 29c45fe3bb0ab56bbccb44b120543e7728827716 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:06:11 +0300
+Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD
+ register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream.
+
+EDP specification says:
+
+"If either VSC SDP is unable to be transmitted 100 ns before the SU region,
+the Source device may optionally transmit the VSC SDP during the prior
+video scan line’s HBlank period There is a Intel specific drm dp register
+currently containing bits related how TCON can support PSR2 with SDP on
+prior line."
+
+Unfortunately many panels are having problems in implementing this. So
+there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to
+figure out if this is properly implemented on a panel or if panel doesn't
+require that 100 ns delay before the SU region. Here are the definitions in
+this custom DPCD address:
+
+0 = Panel doesn't support SDP on prior line
+1 = Panel supports SDP on prior line
+2 = Panel doesn't have 100ns requirement
+3 = Reserved
+
+Add definitions for this new register and it's values into new header
+intel_dpcd.h.
+
+v2: add INTEL_DPCD_ prefix to definitions
+
+Bspec: 74741
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com
+(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+ create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h
+
+diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h
+new file mode 100644
+index 00000000000000..4aea5326f2ed48
+--- /dev/null
++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: MIT */
++/*
++ * Copyright © 2026 Intel Corporation
++ */
++
++#ifndef __INTEL_DPCD_H__
++#define __INTEL_DPCD_H__
++
++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS                                     0x3f0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK        REG_GENMASK(1, 0)
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1                  0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE           1
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE                2
++
++#endif /* __INTEL_DPCD_H__ */
+-- 
+2.53.0
+
diff --git a/queue-6.18/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-6.18/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
new file mode 100644 (file)
index 0000000..c806814
--- /dev/null
@@ -0,0 +1,85 @@
+From 96a8f7c9a0997dda535ebf9a2d55d5f02716f609 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:06:13 +0300
+Subject: drm/i915/psr: Apply Intel DPCD workaround when SDP on prior line used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit 4703049f768fc1c1caac754134118bee1a3af189 upstream.
+
+There is Intel specific workaround DPCD address containing workaround for
+case where SDP is on prior line. Apply this workaround according to values
+in the offset.
+
+Fixes: 61e887329e33 ("drm/i915/xelpd: Handle PSR2 SDP indication in the prior scanline")
+Cc: <stable@vger.kernel.org> # v5.15+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com
+(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_psr.c | 28 +++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 802a671b2afaaa..eed281b93cd84d 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -29,6 +29,7 @@
+ #include <drm/drm_vblank.h>
+ #include "i915_reg.h"
++#include "i915_utils.h"
+ #include "intel_alpm.h"
+ #include "intel_atomic.h"
+ #include "intel_crtc.h"
+@@ -1315,6 +1316,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp,
+       return true;
+ }
++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp,
++                                       struct intel_crtc_state *crtc_state)
++{
++      u8 early_scanline_support = intel_dp->intel_wa_dpcd &
++              INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK;
++
++      if (intel_dp->edp_dpcd[0] >= DP_EDP_15)
++              return true;
++
++      switch (early_scanline_support) {
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return false;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE:
++              return true;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return true;
++      default:
++              MISSING_CASE(early_scanline_support);
++              return false;
++      }
++}
++
+ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp,
+                                                       struct intel_crtc_state *crtc_state)
+ {
+@@ -1336,7 +1361,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d
+               return false;
+       crtc_state->req_psr2_sdp_prior_scanline = true;
+-      return true;
++
++      return apply_scanline_indication_wa(intel_dp, crtc_state);
+ }
+ static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp,
+-- 
+2.53.0
+
diff --git a/queue-6.18/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-6.18/drm-i915-psr-read-intel-dpcd-workaround-register.patch
new file mode 100644 (file)
index 0000000..2139667
--- /dev/null
@@ -0,0 +1,67 @@
+From c0493c4811c22fd21f1de77a27f328350822659b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:06:12 +0300
+Subject: drm/i915/psr: Read Intel DPCD workaround register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream.
+
+Read Intel DPCD workaround register and store it into
+intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains
+only PSR workaround for PSR2 SDP on prior scanline implementation.
+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com
+(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_display_types.h | 1 +
+ drivers/gpu/drm/i915/display/intel_psr.c           | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
+index 39dd7389f1a71a..197bdb6592a7f9 100644
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -1710,6 +1710,7 @@ struct intel_dp {
+       u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE];
+       u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE];
+       u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE];
++      u8 intel_wa_dpcd;
+       /* source rates */
+       int num_source_rates;
+       const int *source_rates;
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 1da20065ea7763..802a671b2afaaa 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -41,6 +41,7 @@
+ #include "intel_display_types.h"
+ #include "intel_dmc.h"
+ #include "intel_dp.h"
++#include "intel_dpcd.h"
+ #include "intel_dp_aux.h"
+ #include "intel_dsb.h"
+ #include "intel_frontbuffer.h"
+@@ -679,6 +680,12 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp)
+               drm_dbg_kms(display->drm, "PSR2 %ssupported\n",
+                           intel_dp->psr.sink_psr2_support ? "" : "not ");
+       }
++
++      if (intel_dp->psr.sink_psr2_support)
++              drm_dp_dpcd_read(&intel_dp->aux,
++                               INTEL_DPCD_INTEL_WA_REGISTER_CAPS,
++                               &intel_dp->intel_wa_dpcd,
++                               sizeof(intel_dp->intel_wa_dpcd));
+ }
+ void intel_psr_init_dpcd(struct intel_dp *intel_dp)
+-- 
+2.53.0
+
diff --git a/queue-6.18/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch b/queue-6.18/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch
new file mode 100644 (file)
index 0000000..23dd70e
--- /dev/null
@@ -0,0 +1,100 @@
+From 354c235ba8a454fec4ba58a0d86a2cb09746a428 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 26 May 2026 20:50:43 +0000
+Subject: Revert "x86/fpu: Refine and simplify the magic number check during
+ signal return"
+
+From: Andrei Vagin <avagin@google.com>
+
+[ Upstream commit 44eeff9bc467bc7d1fec34fc3f6001f385fe462c ]
+
+This reverts
+
+  dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return").
+
+The aforementioned commit broke applications that construct signal frames in
+userspace (such as CRIU and gVisor) if the frame's xstate size is smaller than
+the kernel's fpstate->user_size.
+
+Furthermore, this introduces a critical issue for checkpoint/restore tools
+like CRIU. If a process is checkpointed while inside a signal handler, its
+stack contains a signal frame formatted according to the source host's xstate
+capabilities.
+
+If that process is later restored on a destination host with larger xstate
+capabilities (e.g., a newer CPU with more features enabled, resulting in
+a larger fpstate->user_size), the kernel will look for FP_XSTATE_MAGIC2 at the
+destination host's larger user_size offset instead of the offset encoded in
+the frame's fx_sw->xstate_size.
+
+This causes the magic2 check to fail, forcing sigreturn to silently fall back
+to "FX-only" mode. Upon return from the signal handler, the process's extended
+state is reset to initial values instead of being restored, leading to silent
+data corruption.
+
+The aforementioned commit cited
+
+  d877550eaf2d ("x86/fpu: Stop relying on userspace for info to fault in xsave buffer")
+
+as justification to stop relying on userspace for the magic number check.
+
+However, these two changes are fundamentally different. The last one only
+changed how much memory the kernel ensures is paged-in before running XRSTOR
+to prevent an infinite loop. It did not change the signal frame format or how
+the layout is validated.
+
+Reverting this change restores the use of fx_sw->xstate_size for
+locating magic2 and restores the necessary sanity checks, ensuring that
+the signal frame remains self-describing and portable.
+
+  [ bp: Massage commit message. ]
+
+Fixes: dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return")
+Signed-off-by: Andrei Vagin <avagin@google.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Acked-by: Chang S. Bae <chang.seok.bae@intel.com>
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/all/20260429000623.3356606-1-avagin@google.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/fpu/signal.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
+index c3ec2512f2bbe4..20b638c507ca2d 100644
+--- a/arch/x86/kernel/fpu/signal.c
++++ b/arch/x86/kernel/fpu/signal.c
+@@ -27,14 +27,19 @@
+ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf,
+                                           struct _fpx_sw_bytes *fx_sw)
+ {
++      int min_xstate_size = sizeof(struct fxregs_state) +
++                            sizeof(struct xstate_header);
+       void __user *fpstate = fxbuf;
+       unsigned int magic2;
+       if (__copy_from_user(fx_sw, &fxbuf->sw_reserved[0], sizeof(*fx_sw)))
+               return false;
+-      /* Check for the first magic field */
+-      if (fx_sw->magic1 != FP_XSTATE_MAGIC1)
++      /* Check for the first magic field and other error scenarios. */
++      if (fx_sw->magic1 != FP_XSTATE_MAGIC1 ||
++          fx_sw->xstate_size < min_xstate_size ||
++          fx_sw->xstate_size > x86_task_fpu(current)->fpstate->user_size ||
++          fx_sw->xstate_size > fx_sw->extended_size)
+               goto setfx;
+       /*
+@@ -43,7 +48,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf,
+        * fpstate layout with out copying the extended state information
+        * in the memory layout.
+        */
+-      if (__get_user(magic2, (__u32 __user *)(fpstate + x86_task_fpu(current)->fpstate->user_size)))
++      if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size)))
+               return false;
+       if (likely(magic2 == FP_XSTATE_MAGIC2))
+-- 
+2.53.0
+
diff --git a/queue-6.18/s390-cio-restore-gfp_dma-for-chsc-allocation.patch b/queue-6.18/s390-cio-restore-gfp_dma-for-chsc-allocation.patch
new file mode 100644 (file)
index 0000000..b508c54
--- /dev/null
@@ -0,0 +1,152 @@
+From 69d085ec1e03fbcd9c213d08a92a03f1d8bf7fc0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 2 Jun 2026 18:22:21 +0200
+Subject: s390/cio: Restore GFP_DMA for CHSC allocation
+
+From: Peter Oberparleiter <oberpar@linux.ibm.com>
+
+[ Upstream commit ea34567db0a6b3a7ce78ba421592344315c8f90e ]
+
+Re-add GFP_DMA when allocating memory for CHSC control blocks.
+On some supported machines, CHSC cannot access memory outside
+the DMA zone, causing CHSC command failures.
+
+Cc: stable@vger.kernel.org
+Fixes: a3a64a4def8d ("s390/cio: remove unneeded DMA zone allocation")
+Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
+Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
+Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
+[ adjusted context to account for missing commit bf4afc53b77ae ]
+Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/s390/cio/chsc.c     |  4 ++--
+ drivers/s390/cio/chsc_sch.c | 20 ++++++++++----------
+ drivers/s390/cio/scm.c      |  2 +-
+ 3 files changed, 13 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
+index 239c92d4ec11e5..b5f6eb18ebadb2 100644
+--- a/drivers/s390/cio/chsc.c
++++ b/drivers/s390/cio/chsc.c
+@@ -1143,8 +1143,8 @@ int __init chsc_init(void)
+ {
+       int ret;
+-      sei_page = (void *)get_zeroed_page(GFP_KERNEL);
+-      chsc_page = (void *)get_zeroed_page(GFP_KERNEL);
++      sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
++      chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sei_page || !chsc_page) {
+               ret = -ENOMEM;
+               goto out_err;
+diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
+index 1e58ee3cc87db1..9131ce3af1b8eb 100644
+--- a/drivers/s390/cio/chsc_sch.c
++++ b/drivers/s390/cio/chsc_sch.c
+@@ -293,7 +293,7 @@ static int chsc_ioctl_start(void __user *user_area)
+       if (!css_general_characteristics.dynio)
+               /* It makes no sense to try. */
+               return -EOPNOTSUPP;
+-      chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
++      chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+       if (!chsc_area)
+               return -ENOMEM;
+       request = kzalloc(sizeof(*request), GFP_KERNEL);
+@@ -341,7 +341,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
+               ret = -ENOMEM;
+               goto out_unlock;
+       }
+-      on_close_chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
++      on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+       if (!on_close_chsc_area) {
+               ret = -ENOMEM;
+               goto out_free_request;
+@@ -393,7 +393,7 @@ static int chsc_ioctl_start_sync(void __user *user_area)
+       struct chsc_sync_area *chsc_area;
+       int ret, ccode;
+-      chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
++      chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!chsc_area)
+               return -ENOMEM;
+       if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
+@@ -439,7 +439,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
+               u8 data[PAGE_SIZE - 20];
+       } __attribute__ ((packed)) *scpcd_area;
+-      scpcd_area = (void *)get_zeroed_page(GFP_KERNEL);
++      scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!scpcd_area)
+               return -ENOMEM;
+       cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+@@ -501,7 +501,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
+               u8 data[PAGE_SIZE - 20];
+       } __attribute__ ((packed)) *scucd_area;
+-      scucd_area = (void *)get_zeroed_page(GFP_KERNEL);
++      scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!scucd_area)
+               return -ENOMEM;
+       cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+@@ -564,7 +564,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
+               u8 data[PAGE_SIZE - 20];
+       } __attribute__ ((packed)) *sscud_area;
+-      sscud_area = (void *)get_zeroed_page(GFP_KERNEL);
++      sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sscud_area)
+               return -ENOMEM;
+       cud = kzalloc(sizeof(*cud), GFP_KERNEL);
+@@ -626,7 +626,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
+               u8 data[PAGE_SIZE - 20];
+       } __attribute__ ((packed)) *sci_area;
+-      sci_area = (void *)get_zeroed_page(GFP_KERNEL);
++      sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sci_area)
+               return -ENOMEM;
+       ci = kzalloc(sizeof(*ci), GFP_KERNEL);
+@@ -697,7 +697,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
+               u32 res;
+       } __attribute__ ((packed)) *cssids_parm;
+-      sccl_area = (void *)get_zeroed_page(GFP_KERNEL);
++      sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sccl_area)
+               return -ENOMEM;
+       ccl = kzalloc(sizeof(*ccl), GFP_KERNEL);
+@@ -757,7 +757,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd)
+       int ret;
+       chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
+-      scpd_area = (void *)get_zeroed_page(GFP_KERNEL);
++      scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!scpd_area || !chpd) {
+               ret = -ENOMEM;
+               goto out_free;
+@@ -797,7 +797,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
+               u8 data[PAGE_SIZE - 36];
+       } __attribute__ ((packed)) *sdcal_area;
+-      sdcal_area = (void *)get_zeroed_page(GFP_KERNEL);
++      sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sdcal_area)
+               return -ENOMEM;
+       dcal = kzalloc(sizeof(*dcal), GFP_KERNEL);
+diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
+index 9b4da237a0ed52..f4faa38a4b17ef 100644
+--- a/drivers/s390/cio/scm.c
++++ b/drivers/s390/cio/scm.c
+@@ -229,7 +229,7 @@ int scm_update_information(void)
+       size_t num;
+       int ret;
+-      scm_info = (void *)__get_free_page(GFP_KERNEL);
++      scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+       if (!scm_info)
+               return -ENOMEM;
+-- 
+2.53.0
+
index 982c70b6f892f92af811b0274d70e147e00eb91e..24e6004d59f54a08293d1e844b0dace317999c45 100644 (file)
@@ -93,3 +93,9 @@ ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch
 net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch
 media-rc-fix-race-between-unregister-and-urb-irq-cal.patch
 media-rc-ttusbir-fix-inverted-error-logic.patch
+smb-client-validate-the-whole-dacl-before-rewriting-.patch
+revert-x86-fpu-refine-and-simplify-the-magic-number-.patch
+s390-cio-restore-gfp_dma-for-chsc-allocation.patch
+drm-i915-psr-add-defininitions-for-intel_wa_register.patch
+drm-i915-psr-read-intel-dpcd-workaround-register.patch
+drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
diff --git a/queue-6.18/smb-client-validate-the-whole-dacl-before-rewriting-.patch b/queue-6.18/smb-client-validate-the-whole-dacl-before-rewriting-.patch
new file mode 100644 (file)
index 0000000..f3e6462
--- /dev/null
@@ -0,0 +1,229 @@
+From b7395002e94d09ad2a56106bead5544a4cfddba2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Apr 2026 20:11:31 -0400
+Subject: smb: client: validate the whole DACL before rewriting it in cifsacl
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 0a8cf165566ba55a39fd0f4de172119dd646d39a ]
+
+build_sec_desc() and id_mode_to_cifs_acl() derive a DACL pointer from a
+server-supplied dacloffset and then use the incoming ACL to rebuild the
+chmod/chown security descriptor.
+
+The original fix only checked that the struct smb_acl header fits before
+reading dacl_ptr->size or dacl_ptr->num_aces.  That avoids the immediate
+header-field OOB read, but the rewrite helpers still walk ACEs based on
+pdacl->num_aces with no structural validation of the incoming DACL body.
+
+A malicious server can return a truncated DACL that still contains a
+header, claims one or more ACEs, and then drive
+replace_sids_and_copy_aces() or set_chmod_dacl() past the validated
+extent while they compare or copy attacker-controlled ACEs.
+
+Factor the DACL structural checks into validate_dacl(), extend them to
+validate each ACE against the DACL bounds, and use the shared validator
+before the chmod/chown rebuild paths.  parse_dacl() reuses the same
+validator so the read-side parser and write-side rewrite paths agree on
+what constitutes a well-formed incoming DACL.
+
+Fixes: bc3e9dd9d104 ("cifs: Change SIDs in ACEs while transferring file ownership.")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-6
+Assisted-by: Codex:gpt-5-4
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsacl.c | 116 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 85 insertions(+), 31 deletions(-)
+
+diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
+index e2948835587094..d17912beb28ec5 100644
+--- a/fs/smb/client/cifsacl.c
++++ b/fs/smb/client/cifsacl.c
+@@ -757,6 +757,77 @@ static void dump_ace(struct smb_ace *pace, char *end_of_acl)
+ }
+ #endif
++static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl)
++{
++      int i, ace_hdr_size, ace_size, min_ace_size;
++      u16 dacl_size, num_aces;
++      char *acl_base, *end_of_dacl;
++      struct smb_ace *pace;
++
++      if (!pdacl)
++              return 0;
++
++      if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) {
++              cifs_dbg(VFS, "ACL too small to parse DACL\n");
++              return -EINVAL;
++      }
++
++      dacl_size = le16_to_cpu(pdacl->size);
++      if (dacl_size < sizeof(struct smb_acl) ||
++          end_of_acl < (char *)pdacl + dacl_size) {
++              cifs_dbg(VFS, "ACL too small to parse DACL\n");
++              return -EINVAL;
++      }
++
++      num_aces = le16_to_cpu(pdacl->num_aces);
++      if (!num_aces)
++              return 0;
++
++      ace_hdr_size = offsetof(struct smb_ace, sid) +
++              offsetof(struct smb_sid, sub_auth);
++      min_ace_size = ace_hdr_size + sizeof(__le32);
++      if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) {
++              cifs_dbg(VFS, "ACL too small to parse DACL\n");
++              return -EINVAL;
++      }
++
++      end_of_dacl = (char *)pdacl + dacl_size;
++      acl_base = (char *)pdacl;
++      ace_size = sizeof(struct smb_acl);
++
++      for (i = 0; i < num_aces; ++i) {
++              if (end_of_dacl - acl_base < ace_size) {
++                      cifs_dbg(VFS, "ACL too small to parse ACE\n");
++                      return -EINVAL;
++              }
++
++              pace = (struct smb_ace *)(acl_base + ace_size);
++              acl_base = (char *)pace;
++
++              if (end_of_dacl - acl_base < ace_hdr_size ||
++                  pace->sid.num_subauth == 0 ||
++                  pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) {
++                      cifs_dbg(VFS, "ACL too small to parse ACE\n");
++                      return -EINVAL;
++              }
++
++              ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth;
++              if (end_of_dacl - acl_base < ace_size ||
++                  le16_to_cpu(pace->size) < ace_size) {
++                      cifs_dbg(VFS, "ACL too small to parse ACE\n");
++                      return -EINVAL;
++              }
++
++              ace_size = le16_to_cpu(pace->size);
++              if (end_of_dacl - acl_base < ace_size) {
++                      cifs_dbg(VFS, "ACL too small to parse ACE\n");
++                      return -EINVAL;
++              }
++      }
++
++      return 0;
++}
++
+ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
+                      struct smb_sid *pownersid, struct smb_sid *pgrpsid,
+                      struct cifs_fattr *fattr, bool mode_from_special_sid)
+@@ -764,7 +835,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
+       int i;
+       u16 num_aces = 0;
+       int acl_size;
+-      char *acl_base;
++      char *acl_base, *end_of_dacl;
+       struct smb_ace **ppace;
+       /* BB need to add parm so we can store the SID BB */
+@@ -776,12 +847,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
+               return;
+       }
+-      /* validate that we do not go past end of acl */
+-      if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
+-          end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
+-              cifs_dbg(VFS, "ACL too small to parse DACL\n");
++      if (validate_dacl(pdacl, end_of_acl))
+               return;
+-      }
+       cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
+                le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
+@@ -792,6 +859,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
+          user/group/other have no permissions */
+       fattr->cf_mode &= ~(0777);
++      end_of_dacl = (char *)pdacl + le16_to_cpu(pdacl->size);
+       acl_base = (char *)pdacl;
+       acl_size = sizeof(struct smb_acl);
+@@ -799,36 +867,16 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
+       if (num_aces > 0) {
+               umode_t denied_mode = 0;
+-              if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
+-                              (offsetof(struct smb_ace, sid) +
+-                               offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
+-                      return;
+-
+               ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *),
+                                     GFP_KERNEL);
+               if (!ppace)
+                       return;
+               for (i = 0; i < num_aces; ++i) {
+-                      if (end_of_acl - acl_base < acl_size)
+-                              break;
+-
+                       ppace[i] = (struct smb_ace *) (acl_base + acl_size);
+-                      acl_base = (char *)ppace[i];
+-                      acl_size = offsetof(struct smb_ace, sid) +
+-                              offsetof(struct smb_sid, sub_auth);
+-
+-                      if (end_of_acl - acl_base < acl_size ||
+-                          ppace[i]->sid.num_subauth == 0 ||
+-                          ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
+-                          (end_of_acl - acl_base <
+-                           acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
+-                          (le16_to_cpu(ppace[i]->size) <
+-                           acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
+-                              break;
+ #ifdef CONFIG_CIFS_DEBUG2
+-                      dump_ace(ppace[i], end_of_acl);
++                      dump_ace(ppace[i], end_of_dacl);
+ #endif
+                       if (mode_from_special_sid &&
+                           ppace[i]->sid.num_subauth >= 3 &&
+@@ -871,6 +919,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
+                               (void *)ppace[i],
+                               sizeof(struct smb_ace)); */
++                      acl_base = (char *)ppace[i];
+                       acl_size = le16_to_cpu(ppace[i]->size);
+               }
+@@ -1316,10 +1365,9 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
+               }
+               dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
+-              if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
+-                      cifs_dbg(VFS, "Server returned illegal ACL size\n");
+-                      return -EINVAL;
+-              }
++              rc = validate_dacl(dacl_ptr, end_of_acl);
++              if (rc)
++                      return rc;
+       }
+       owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
+@@ -1698,6 +1746,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
+                       }
+                       dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
++                      rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
++                      if (rc) {
++                              kfree(pntsd);
++                              cifs_put_tlink(tlink);
++                              return rc;
++                      }
+                       if (mode_from_sid)
+                               nsecdesclen +=
+                                       le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-6.6/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
new file mode 100644 (file)
index 0000000..c822500
--- /dev/null
@@ -0,0 +1,232 @@
+From 08dda28b5d0acfaa0cc0d1c1b06485908972dd54 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:19:23 +0200
+Subject: batman-adv: bla: avoid double decrement of bla.num_requests
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 83ab69bd12b80f6ea169c8bea6977701b53a043d upstream.
+
+The bla.num_requests is increased when no request_sent was in progress. And
+it is decremented in various places (announcement was received, backbone is
+purged, periodic work). But the check if the request_sent is actually set
+to a specific state and the atomic_dec/_inc are not safe because they are
+not atomic (TOCTOU) and multiple such code portions can run concurrently.
+
+At the same time, it is necessary to modify request_sent (state) and
+bla.num_requests atomically. Otherwise batadv_bla_send_request() might set
+request_sent to 1 and is interrupted.  batadv_handle_announce() can then
+set request_sent back to 0 and decrement num_requests before
+batadv_bla_send_request() incremented it.
+
+The two operations must therefore be locked. And since state (request_sent)
+and wait_periods are only accessed inside this lock, they can be converted
+to simpler datatypes. And to avoid that the bla.num_requests is touched by
+a parallel running context with a valid backbone_gw reference after
+batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to
+correctly signal that a backbone_gw is in the state of being cleaned up.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++--------
+ net/batman-adv/soft-interface.c        |  1 +
+ net/batman-adv/types.h                 | 39 ++++++++++++++++----
+ 3 files changed, 67 insertions(+), 24 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index 76d8c91c156a3b..cfb1eb25c6ac4d 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -516,8 +516,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+       entry->crc = BATADV_BLA_CRC_INIT;
+       entry->bat_priv = bat_priv;
+       spin_lock_init(&entry->crc_lock);
+-      atomic_set(&entry->request_sent, 0);
+-      atomic_set(&entry->wait_periods, 0);
++      entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
++      entry->wait_periods = 0;
+       ether_addr_copy(entry->orig, orig);
+       INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
+       kref_init(&entry->refcount);
+@@ -546,9 +546,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
+               batadv_bla_send_announce(bat_priv, entry);
+               /* this will be decreased in the worker thread */
+-              atomic_inc(&entry->request_sent);
+-              atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
+-              atomic_inc(&bat_priv->bla.num_requests);
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++                      entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
++                      entry->wait_periods = BATADV_BLA_WAIT_PERIODS;
++                      atomic_inc(&bat_priv->bla.num_requests);
++              }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       return entry;
+@@ -651,10 +655,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw)
+                             backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);
+       /* no local broadcasts should be sent or received, for now. */
+-      if (!atomic_read(&backbone_gw->request_sent)) {
++      spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
++      if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++              backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
+               atomic_inc(&backbone_gw->bat_priv->bla.num_requests);
+-              atomic_set(&backbone_gw->request_sent, 1);
+       }
++      spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
+ }
+ /**
+@@ -875,10 +881,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
+               /* if we have sent a request and the crc was OK,
+                * we can allow traffic again.
+                */
+-              if (atomic_read(&backbone_gw->request_sent)) {
++              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) {
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
+               }
++              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+       }
+       batadv_backbone_gw_put(backbone_gw);
+@@ -1257,9 +1265,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
+                               purged = true;
+                               /* don't wait for the pending request anymore */
+-                              if (atomic_read(&backbone_gw->request_sent))
++                              spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                              if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED)
+                                       atomic_dec(&bat_priv->bla.num_requests);
++                              backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
++                              spin_unlock_bh(&bat_priv->bla.num_requests_lock);
++
+                               batadv_bla_del_backbone_claims(backbone_gw);
+                               hlist_del_rcu(&backbone_gw->hash_entry);
+@@ -1510,7 +1522,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                               batadv_bla_send_loopdetect(bat_priv,
+                                                          backbone_gw);
+-                      /* request_sent is only set after creation to avoid
++                      /* state is only set to unsynced after creation to avoid
+                        * problems when we are not yet known as backbone gw
+                        * in the backbone.
+                        *
+@@ -1519,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work)
+                        * some grace time.
+                        */
+-                      if (atomic_read(&backbone_gw->request_sent) == 0)
+-                              continue;
++                      spin_lock_bh(&bat_priv->bla.num_requests_lock);
++                      if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
++                              goto unlock_next;
+-                      if (!atomic_dec_and_test(&backbone_gw->wait_periods))
+-                              continue;
++                      if (backbone_gw->wait_periods > 0)
++                              backbone_gw->wait_periods--;
++
++                      if (backbone_gw->wait_periods > 0)
++                              goto unlock_next;
++                      backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+                       atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+-                      atomic_set(&backbone_gw->request_sent, 0);
++
++unlock_next:
++                      spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+               }
+               rcu_read_unlock();
+       }
+diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
+index 1bf1232a4f7595..680f729b3b0228 100644
+--- a/net/batman-adv/soft-interface.c
++++ b/net/batman-adv/soft-interface.c
+@@ -783,6 +783,7 @@ static int batadv_softif_init_late(struct net_device *dev)
+       atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
+ #ifdef CONFIG_BATMAN_ADV_BLA
+       atomic_set(&bat_priv->bla.num_requests, 0);
++      spin_lock_init(&bat_priv->bla.num_requests_lock);
+ #endif
+       atomic_set(&bat_priv->tp_num, 0);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index d333f9dfec55d0..2dfdc78d4ded99 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1027,6 +1027,12 @@ struct batadv_priv_bla {
+       /** @num_requests: number of bla requests in flight */
+       atomic_t num_requests;
++      /**
++       * @num_requests_lock: locks update num_requests +
++       * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update
++       */
++      spinlock_t num_requests_lock;
++
+       /**
+        * @claim_hash: hash table containing mesh nodes this host has claimed
+        */
+@@ -1755,6 +1761,27 @@ struct batadv_priv {
+ #ifdef CONFIG_BATMAN_ADV_BLA
++enum batadv_bla_backbone_gw_state {
++      /**
++       * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed
++       * and it must not longer work on requests
++       */
++      BATADV_BLA_BACKBONE_GW_STOPPED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out
++       * of sync and a request was send. No traffic is forwarded until the
++       * situation is resolved
++       */
++      BATADV_BLA_BACKBONE_GW_UNSYNCED,
++
++      /**
++       * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in
++       * sync. traffic can be forwarded
++       */
++      BATADV_BLA_BACKBONE_GW_SYNCED,
++};
++
+ /**
+  * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
+  */
+@@ -1780,16 +1807,12 @@ struct batadv_bla_backbone_gw {
+       /**
+        * @wait_periods: grace time for bridge forward delays and bla group
+        *  forming at bootup phase - no bcast traffic is formwared until it has
+-       *  elapsed
++       *  elapsed. Must only be access with num_requests_lock.
+        */
+-      atomic_t wait_periods;
++      u8 wait_periods;
+-      /**
+-       * @request_sent: if this bool is set to true we are out of sync with
+-       *  this backbone gateway - no bcast traffic is formwared until the
+-       *  situation was resolved
+-       */
+-      atomic_t request_sent;
++      /** @state: sync state. Must only be access with num_requests_lock. */
++      enum batadv_bla_backbone_gw_state state;
+       /** @crc: crc16 checksum over all claims */
+       u16 crc;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-6.6/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
new file mode 100644 (file)
index 0000000..df7a471
--- /dev/null
@@ -0,0 +1,53 @@
+From 4674fe10e8cd7155139266fd6797e45a0262fe45 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:20:23 +0200
+Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 upstream.
+
+Without rtnl_lock held, a hardif might be retrieved as primary interface of
+a meshif, but then (while operating on this interface) getting decoupled
+from the mesh interface. In this case, the meshif still exists but the
+pointer from the primary hardif to the meshif is set to NULL.
+
+The mesh_iface must be checked first to be non-NULL before continuing to
+send an ARP request using meshif.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Reported-by: Ido Schimmel <idosch@nvidia.com>
+Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
+index f614709e6cda74..76d8c91c156a3b 100644
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
+              sizeof(local_claim_dest));
+       local_claim_dest.type = claimtype;
+-      soft_iface = primary_if->soft_iface;
++      soft_iface = READ_ONCE(primary_if->soft_iface);
++      if (!soft_iface)
++              goto out;
+       skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
+                        /* IP DST: 0.0.0.0 */
+                        zeroip,
+-                       primary_if->soft_iface,
++                       soft_iface,
+                        /* IP SRC: 0.0.0.0 */
+                        zeroip,
+                        /* Ethernet DST: Broadcast */
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-6.6/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
new file mode 100644 (file)
index 0000000..dd756ab
--- /dev/null
@@ -0,0 +1,230 @@
+From 483fd0f4f26b416d40532d4415f07918adafaec1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:18:57 +0200
+Subject: batman-adv: iv: recover OGM scheduling after forward packet error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit aa3153bd139a6c48667dcd02608d3b2c80bff02c upstream.
+
+When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward
+packet for OGM transmission, the work item that drives periodic OGM
+scheduling is never re-armed. This silently halts transmission of the
+node's own OGMs on the affected interface — only OGMs from other peers
+continue to be aggregated and forwarded.
+
+Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively
+batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet.
+When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing
+a dedicated recovery work item (reschedule_work) that fires after one
+originator interval and calls batadv_iv_ogm_schedule() again.
+
+Cc: stable@kernel.org
+Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++----------
+ net/batman-adv/types.h      |  3 ++
+ 2 files changed, 60 insertions(+), 19 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 42b687c1a76807..b37c9fb178ae50 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -223,6 +223,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
+       hard_iface->bat_iv.ogm_buff = NULL;
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
++
++      cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
+ }
+ static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
+@@ -527,8 +529,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
+  * @if_incoming: interface where the packet was received
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
++static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                                       int packet_len, unsigned long send_time,
+                                       bool direct_link,
+                                       struct batadv_hard_iface *if_incoming,
+@@ -552,13 +556,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+       skb = netdev_alloc_skb_ip_align(NULL, skb_size);
+       if (!skb)
+-              return;
++              return false;
+       forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
+                                                   queue_left, bat_priv, skb);
+       if (!forw_packet_aggr) {
+               kfree_skb(skb);
+-              return;
++              return false;
+       }
+       forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
+@@ -580,6 +584,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+                         batadv_iv_send_outstanding_bat_ogm_packet);
+       batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
++
++      return true;
+ }
+ /* aggregate a new packet into the existing ogm packet */
+@@ -609,8 +615,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
+  * @if_outgoing: interface for which the retransmission should be considered
+  * @own_packet: true if it is a self-generated ogm
+  * @send_time: timestamp (jiffies) when the packet is to be sent
++ *
++ * Return: whether forward packet was scheduled
+  */
+-static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
++static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+                                   unsigned char *packet_buff,
+                                   int packet_len,
+                                   struct batadv_hard_iface *if_incoming,
+@@ -662,14 +670,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+               if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
+                       send_time += max_aggregation_jiffies;
+-              batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
+-                                          send_time, direct_link,
+-                                          if_incoming, if_outgoing,
+-                                          own_packet);
++              return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
++                                                 send_time, direct_link,
++                                                 if_incoming, if_outgoing,
++                                                 own_packet);
+       } else {
+               batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
+                                       packet_len, direct_link);
+               spin_unlock_bh(&bat_priv->forw_bat_list_lock);
++
++              return true;
+       }
+ }
+@@ -781,6 +791,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      bool reschedule = false;
++      bool scheduled;
+       int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -809,11 +821,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                                                      ogm_buff_len,
+                                                      BATADV_OGM_HLEN);
+               if (ret < 0) {
+-                      /* OGMs must be queued even when the buffer allocation for
+-                       * TVLVs failed. just fall back to the non-TVLV version
+-                       */
+-                      ret = 0;
+-                      *ogm_buff_len = BATADV_OGM_HLEN;
++                      reschedule = true;
++                      goto out;
+               }
+               tvlv_len = ret;
+@@ -835,8 +844,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               /* OGMs from secondary interfaces are only scheduled on their
+                * respective interfaces.
+                */
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
+-                                      hard_iface, hard_iface, 1, send_time);
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
++                                                  hard_iface, hard_iface, 1, send_time);
++              if (!scheduled)
++                      reschedule = true;
++
+               goto out;
+       }
+@@ -851,15 +863,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+               if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
+                       continue;
+-              batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
+-                                      *ogm_buff_len, hard_iface,
+-                                      tmp_hard_iface, 1, send_time);
+-
++              scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
++                                                  *ogm_buff_len, hard_iface,
++                                                  tmp_hard_iface, 1, send_time);
+               batadv_hardif_put(tmp_hard_iface);
++
++              if (!scheduled && tmp_hard_iface == hard_iface)
++                      reschedule = true;
+       }
+       rcu_read_unlock();
+ out:
++      if (reschedule) {
++              /* there was a failure scheduling the own forward packet.
++               * as result, the batadv_iv_send_outstanding_bat_ogm_packet()
++               * work item is no longer scheduled. it is therefore necessary
++               * to reschedule it manually
++               */
++              queue_delayed_work(batadv_event_workqueue,
++                                 &hard_iface->bat_iv.reschedule_work,
++                                 msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
++      }
++
+       batadv_hardif_put(primary_if);
+ }
+@@ -874,6 +899,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
+       mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+ }
++static void batadv_iv_ogm_reschedule(struct work_struct *work)
++{
++      struct delayed_work *delayed_work = to_delayed_work(work);
++      struct batadv_hard_iface *hard_iface;
++
++      hard_iface = container_of(delayed_work,
++                                struct batadv_hard_iface,
++                                bat_iv.reschedule_work);
++      batadv_iv_ogm_schedule(hard_iface);
++}
++
+ /**
+  * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
+  * @orig_node: originator which reproadcasted the OGMs directly
+@@ -2277,6 +2313,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
+ static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
+ {
++      INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
++
+       /* begin scheduling originator messages on that interface */
+       batadv_iv_ogm_schedule(hard_iface);
+ }
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 957a70854157dc..d333f9dfec55d0 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv {
+       /** @ogm_seqno: OGM sequence number - used to identify each OGM */
+       atomic_t ogm_seqno;
++      /** @reschedule_work: recover OGM schedule after schedule error */
++      struct delayed_work reschedule_work;
++
+       /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
+       struct mutex ogm_buff_mutex;
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch b/queue-6.6/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch
new file mode 100644 (file)
index 0000000..7c51995
--- /dev/null
@@ -0,0 +1,196 @@
+From 55f0d0967fc724139b51a79017aef58c8a82b36c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:18:27 +0200
+Subject: batman-adv: tp_meter: avoid role confusion in tp_list
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit ff24f2ecfd94c07a2b89bac497433e3b23271cac upstream.
+
+Session lookups in tp_list matched only on destination address (and
+optionally session ID), leaving role validation to the caller. If two
+sessions with the same other_end coexisted (one as sender, one as receiver)
+a lookup could silently return the wrong one, causing the caller's role to
+bail out early, potentially skipping necessary cleanup.
+
+Move the role check into the lookup functions themselves so the correct
+entry is always returned, or none at all. Since batadv_tp_start()
+legitimately needs to detect any active session to a destination regardless
+of role, introduce a dedicated helper for that case rather than bending the
+existing lookup semantics.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++---------------
+ 1 file changed, 36 insertions(+), 23 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index 04a83d6be45bc0..bc3dc377f0bfd0 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
+  * batadv_tp_list_find() - find a tp_vars object in the global list
+  * @bat_priv: the bat priv with all the soft interface information
+  * @dst: the other endpoint MAC address to look for
++ * @role: role of the session
+  *
+  * Look for a tp_vars object matching dst as end_point and return it after
+  * having increment the refcounter. Return NULL is not found
+@@ -262,7 +263,8 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
+  * Return: matching tp_vars or NULL when no tp_vars with @dst was found
+  */
+ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+-                                                const u8 *dst)
++                                                const u8 *dst,
++                                                enum batadv_tp_meter_role role)
+ {
+       struct batadv_tp_vars *pos, *tp_vars = NULL;
+@@ -271,6 +273,9 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+               if (!batadv_compare_eth(pos->other_end, dst))
+                       continue;
++              if (pos->role != role)
++                      continue;
++
+               /* most of the time this function is invoked during the normal
+                * process..it makes sens to pay more when the session is
+                * finished and to speed the process up during the measurement
+@@ -286,12 +291,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+       return tp_vars;
+ }
++/**
++ * batadv_tp_list_active() - check if session from/to destination is ongoing
++ * @bat_priv: the bat priv with all the mesh interface information
++ * @dst: the other endpoint MAC address to look for
++ *
++ * Return: if matching session with @dst was found
++ */
++static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
++      __must_hold(&bat_priv->tp_list_lock)
++{
++      struct batadv_tp_vars *tp_vars;
++
++      hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) {
++              if (batadv_compare_eth(tp_vars->other_end, dst))
++                      return true;
++      }
++
++      return false;
++}
++
+ /**
+  * batadv_tp_list_find_session() - find tp_vars session object in the global
+  *  list
+  * @bat_priv: the bat priv with all the soft interface information
+  * @dst: the other endpoint MAC address to look for
+  * @session: session identifier
++ * @role: role of the session
+  *
+  * Look for a tp_vars object matching dst as end_point, session as tp meter
+  * session and return it after having increment the refcounter. Return NULL
+@@ -301,7 +327,7 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+  */
+ static struct batadv_tp_vars *
+ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
+-                          const u8 *session)
++                          const u8 *session, enum batadv_tp_meter_role role)
+ {
+       struct batadv_tp_vars *pos, *tp_vars = NULL;
+@@ -313,6 +339,9 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
+               if (memcmp(pos->session, session, sizeof(pos->session)) != 0)
+                       continue;
++              if (pos->role != role)
++                      continue;
++
+               /* most of the time this function is invoked during the normal
+                * process..it makes sense to pay more when the session is
+                * finished and to speed the process up during the measurement
+@@ -671,13 +700,10 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+       /* find the tp_vars */
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                            icmp->session);
++                                            icmp->session, BATADV_TP_SENDER);
+       if (unlikely(!tp_vars))
+               return;
+-      if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+-              goto out;
+-
+       if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+               goto out;
+@@ -986,10 +1012,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+               return;
+       }
+-      tp_vars = batadv_tp_list_find(bat_priv, dst);
+-      if (tp_vars) {
++      if (batadv_tp_list_active(bat_priv, dst)) {
+               spin_unlock_bh(&bat_priv->tp_list_lock);
+-              batadv_tp_vars_put(tp_vars);
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                          "Meter: test to or from the same node already ongoing, aborting\n");
+               batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING,
+@@ -1110,18 +1134,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
+       if (!orig_node)
+               return;
+-      tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig);
++      tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER);
+       if (!tp_vars) {
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                          "Meter: trying to interrupt an already over connection\n");
+               goto out_put_orig_node;
+       }
+-      if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+-              goto out_put_tp_vars;
+-
+       batadv_tp_sender_shutdown(tp_vars, return_value);
+-out_put_tp_vars:
+       batadv_tp_vars_put(tp_vars);
+ out_put_orig_node:
+       batadv_orig_node_put(orig_node);
+@@ -1377,7 +1397,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
+               goto out_unlock;
+       tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                            icmp->session);
++                                            icmp->session, BATADV_TP_RECEIVER);
+       if (tp_vars)
+               goto out_unlock;
+@@ -1448,7 +1468,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               }
+       } else {
+               tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+-                                                    icmp->session);
++                                                    icmp->session, BATADV_TP_RECEIVER);
+               if (!tp_vars) {
+                       batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                                  "Unexpected packet from %pM!\n",
+@@ -1457,13 +1477,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+               }
+       }
+-      if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) {
+-              batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+-                         "Meter: dropping packet: not expected (role=%u)\n",
+-                         tp_vars->role);
+-              goto out;
+-      }
+-
+       tp_vars->last_recv_time = jiffies;
+       /* if the packet is a duplicate, it may be the case that an ACK has been
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/queue-6.6/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch
new file mode 100644 (file)
index 0000000..6239950
--- /dev/null
@@ -0,0 +1,60 @@
+From cced89522213d7463f127b96ffb4aaa475453363 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 22:23:16 +0200
+Subject: batman-adv: tp_meter: directly shut down timer on cleanup
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d5487249a81ea658717614009c8f46acc5b7101a upstream.
+
+batadv_tp_sender_cleanup() was calling timer_delete_sync() followed by
+timer_delete() to guard against the timer handler re-arming itself between
+the two calls. This double-deletion hack relied on the sending status being
+set to 0 to suppress re-arming.
+
+Replace both calls with a single timer_shutdown_sync(). This function both
+waits for any running timer callback to complete (like timer_delete_sync())
+and permanently disarms the timer so it cannot be re-armed afterwards,
+making re-arming prevention unconditional and self-documenting.
+
+The re-arming property is also required because otherwise:
+
+1. context 0 (batadv_tp_recv_ack()) checks in
+   batadv_tp_reset_sender_timer() if sending is still 1 -> it is
+2. context 1 changes in batadv_tp_sender_shutdown() sending to 0 and in
+   this process forces the kthread to stop timer in
+   batadv_tp_sender_cleanup()
+3. context 0 continues in batadv_tp_reset_sender_timer() and rearms the
+   timer -> but the reference for it is already gone
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+[ adapt pre-hunk to old del_timer* names ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tp_meter.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
+index bc3dc377f0bfd0..dfc3374549921f 100644
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -430,13 +430,7 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
+       batadv_tp_list_detach(tp_vars);
+       /* kill the timer and remove its reference */
+-      del_timer_sync(&tp_vars->timer);
+-      /* the worker might have rearmed itself therefore we kill it again. Note
+-       * that if the worker should run again before invoking the following
+-       * del_timer(), it would not re-arm itself once again because the status
+-       * is OFF now
+-       */
+-      del_timer(&tp_vars->timer);
++      timer_shutdown_sync(&tp_vars->timer);
+       batadv_tp_vars_put(tp_vars);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-6.6/batman-adv-tt-avoid-empty-vlan-responses.patch
new file mode 100644 (file)
index 0000000..cab2fc5
--- /dev/null
@@ -0,0 +1,91 @@
+From 2bbc0178e9bc0d94a1d1123ba06a4374950eb064 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:15:14 +0200
+Subject: batman-adv: tt: avoid empty VLAN responses
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 upstream.
+
+The commit 16116dac2339 ("batman-adv: prevent TT request storms by not
+sending inconsistent TT TLVLs") added checks to the local (direct) TT
+response code. But the response can also be done indirectly by another node
+using the global TT state. To avoid such inconsistency states reported in
+the original fix, also avoid sending empty VLANs for replies from the
+global TT state.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index d4cebe122e528a..4045ddefc29b47 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+                                  s32 *tt_len)
+ {
+       u16 num_vlan = 0;
+-      u16 num_entries = 0;
+       u16 tvlv_len = 0;
+       unsigned int change_offset;
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_orig_node_vlan *vlan;
++      u16 total_entries = 0;
+       u8 *tt_change_ptr;
++      int vlan_entries;
+       spin_lock_bh(&orig_node->vlan_list_lock);
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              total_entries += vlan_entries;
+               num_vlan++;
+-              num_entries += atomic_read(&vlan->tt.num_entries);
+       }
+       change_offset = sizeof(**tt_data);
+@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       /* if tt_len is negative, allocate the space needed by the full table */
+       if (*tt_len < 0)
+-              *tt_len = batadv_tt_len(num_entries);
++              *tt_len = batadv_tt_len(total_entries);
+       if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) {
+               *tt_len = 0;
+@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++              vlan_entries = atomic_read(&vlan->tt.num_entries);
++              if (vlan_entries < 1)
++                      continue;
++
+               tt_vlan->vid = htons(vlan->vid);
+               tt_vlan->crc = htonl(vlan->tt.crc);
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-6.6/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
new file mode 100644 (file)
index 0000000..47deb33
--- /dev/null
@@ -0,0 +1,78 @@
+From 8cbc0d53ee0c7e4f066543233ea7fee557a01b87 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 22:59:34 +0200
+Subject: batman-adv: tt: fix TOCTOU race for reported vlans
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 94d27005016be15ffc638b2ecbc4d58805ad7b48 upstream.
+
+The local TT based TVLV is generated by first checking the number of VLANs
+which have at least one TT entry. A new buffer with the correct size for
+the VLANs is then allocated. Only then, the list of VLANs s used to fill
+the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock
+is held. But the actual number of TT entries of each VLAN can still
+increase during this time - just not the number of VLANs in the list.
+
+But the prefilter used in the buffer size calculation might still cause an
+increase of the number of VLANs which need to be stored. Simply because a
+VLAN might now suddenly have at least one entry when it had none in the
+pre-alloc check - and then needs to occupy space which was not allocated.
+
+It is better to overestimate the buffer size at the beginning and then fill
+the buffer only with the VLANs which are not empty.
+
+Cc: stable@kernel.org
+Fixes: 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs")
+[ Context, drop flex array dependency ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index 8ffebece03c529..d4cebe122e528a 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+-              if (vlan_entries < 1)
+-                      continue;
+-
+-              num_vlan++;
+               total_entries += vlan_entries;
++              num_vlan++;
+       }
+       change_offset = sizeof(**tt_data);
+@@ -964,6 +961,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       (*tt_data)->num_vlan = htons(num_vlan);
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
++      num_vlan = 0;
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan_entries = atomic_read(&vlan->tt.num_entries);
+               if (vlan_entries < 1)
+@@ -974,8 +972,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+               tt_vlan->reserved = 0;
+               tt_vlan++;
++              num_vlan++;
+       }
++      /* recalculate in case number of VLANs reduced */
++      change_offset = sizeof(**tt_data);
++      change_offset += num_vlan * sizeof(*tt_vlan);
++      tvlv_len = *tt_len + change_offset;
++
++      (*tt_data)->num_vlan = htons(num_vlan);
++
+       tt_change_ptr = (u8 *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch b/queue-6.6/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch
new file mode 100644 (file)
index 0000000..ef3538e
--- /dev/null
@@ -0,0 +1,62 @@
+From 6216a8dca3fbf71cd1d81c6bf1022591cecae8ae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:13:27 +0200
+Subject: batman-adv: tt: reject oversized local TVLV buffers
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 1e9fab756f8395096d5bba7be0c373c4c8f5d165 upstream.
+
+The commit 3a359bf5c61d ("batman-adv: reject oversized global TT response
+buffers") added a check to ensure that a global return buffer size can be
+stored in an u16. The same buffer handling also exists for the local data
+buffer but was not touched.
+
+A similar check should be also be in place for the local TVLV buffer. It
+doesn't have the similar attack surface because it is only generated from
+locally discovered MAC addresses but the dynamic nature could still cause
+temporarily to large buffers.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/translation-table.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index d830ccf016697b..8ffebece03c529 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -924,12 +924,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+ {
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_softif_vlan *vlan;
++      size_t change_offset;
+       u16 num_vlan = 0;
+       u16 vlan_entries = 0;
+       u16 total_entries = 0;
+       u16 tvlv_len;
+       u8 *tt_change_ptr;
+-      int change_offset;
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
+@@ -948,8 +948,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+       if (*tt_len < 0)
+               *tt_len = batadv_tt_len(total_entries);
+-      tvlv_len = *tt_len;
+-      tvlv_len += change_offset;
++      if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) {
++              tvlv_len = 0;
++              goto out;
++      }
+       *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
+       if (!*tt_data) {
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-6.6/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
new file mode 100644 (file)
index 0000000..696ee1e
--- /dev/null
@@ -0,0 +1,196 @@
+From adfa8ec97ffd3a2d2101c893a08e02e6376bd680 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:48:30 +0200
+Subject: batman-adv: tvlv: abort OGM send on tvlv append failure
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 501368506563e151b322c8c3f228b796e615b90d upstream.
+
+batadv_tvlv_container_ogm_append() could fail in two ways: a memory
+allocation failure when resizing the packet buffer, or the tvlv data
+exceeding U16_MAX bytes. In both cases the function previously returned the
+old (now stale) tvlv_value_len rather than signalling an error, causing the
+OGM/OGM2 send path to transmit a packet whose TVLV length field no longer
+matched the actual buffer contents. And because it also didn't fill in the
+new TVLV data, sending either uninitialized or corrupted data on the wire.
+
+All errors in batadv_tvlv_container_ogm_append() must be forwarded to the
+caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N.
+IV, it is currently not allowed to abort the send. The non-TVLV part of the
+OGM must be queued up instead.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 16 +++++++++++++---
+ net/batman-adv/bat_v_ogm.c  | 26 ++++++++++++++------------
+ net/batman-adv/tvlv.c       | 17 ++++++++++++-----
+ net/batman-adv/tvlv.h       |  2 +-
+ 4 files changed, 40 insertions(+), 21 deletions(-)
+
+diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
+index 748188d3b878b2..42b687c1a76807 100644
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -781,6 +781,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+       u32 seqno;
+       u16 tvlv_len = 0;
+       unsigned long send_time;
++      int ret;
+       lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -804,9 +805,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
+                * appended as it may alter the tt tvlv container
+                */
+               batadv_tt_local_commit_changes(bat_priv);
+-              tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+-                                                          ogm_buff_len,
+-                                                          BATADV_OGM_HLEN);
++              ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                                     ogm_buff_len,
++                                                     BATADV_OGM_HLEN);
++              if (ret < 0) {
++                      /* OGMs must be queued even when the buffer allocation for
++                       * TVLVs failed. just fall back to the non-TVLV version
++                       */
++                      ret = 0;
++                      *ogm_buff_len = BATADV_OGM_HLEN;
++              }
++
++              tvlv_len = ret;
+       }
+       batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 310248a5812c49..8cfc3944dcfd52 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -271,9 +271,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       struct batadv_hard_iface *hard_iface;
+       struct batadv_ogm2_packet *ogm_packet;
+       struct sk_buff *skb, *skb_tmp;
+-      unsigned char *ogm_buff;
+-      int ogm_buff_len;
+-      u16 tvlv_len = 0;
++      unsigned char **ogm_buff;
++      int *ogm_buff_len;
++      u16 tvlv_len;
+       int ret;
+       lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
+@@ -281,25 +281,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+       if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
+               goto out;
+-      ogm_buff = bat_priv->bat_v.ogm_buff;
+-      ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
++      ogm_buff = &bat_priv->bat_v.ogm_buff;
++      ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
++
+       /* tt changes have to be committed before the tvlv data is
+        * appended as it may alter the tt tvlv container
+        */
+       batadv_tt_local_commit_changes(bat_priv);
+-      tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
+-                                                  &ogm_buff_len,
+-                                                  BATADV_OGM2_HLEN);
++      ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++                                             ogm_buff_len,
++                                             BATADV_OGM2_HLEN);
++      if (ret < 0)
++              goto reschedule;
+-      bat_priv->bat_v.ogm_buff = ogm_buff;
+-      bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
++      tvlv_len = ret;
+-      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
++      skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len);
+       if (!skb)
+               goto reschedule;
+       skb_reserve(skb, ETH_HLEN);
+-      skb_put_data(skb, ogm_buff, ogm_buff_len);
++      skb_put_data(skb, *ogm_buff, *ogm_buff_len);
+       ogm_packet = (struct batadv_ogm2_packet *)skb->data;
+       ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 2a583215d439bd..76c6e0599694c7 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -8,6 +8,7 @@
+ #include <linux/byteorder/generic.h>
+ #include <linux/container_of.h>
++#include <linux/errno.h>
+ #include <linux/etherdevice.h>
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
+  * The ogm packet might be enlarged or shrunk depending on the current size
+  * and the size of the to-be-appended tvlv containers.
+  *
+- * Return: size of all appended tvlv containers in bytes.
++ * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
++ *  if operation failed
+  */
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len)
+ {
+@@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       u16 tvlv_value_len;
+       void *tvlv_value;
++      int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+@@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-
+-      if (!ret)
++      if (!ret) {
++              tvlv_len_ret = -ENOMEM;
+               goto end;
++      }
++
++      tvlv_len_ret = tvlv_value_len;
+       if (!tvlv_value_len)
+               goto end;
+@@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ end:
+       spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+-      return tvlv_value_len;
++
++      return tvlv_len_ret;
+ }
+ /**
+diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h
+index e5697230d99173..f96f6b3f44a001 100644
+--- a/net/batman-adv/tvlv.h
++++ b/net/batman-adv/tvlv.h
+@@ -16,7 +16,7 @@
+ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+                                   u8 type, u8 version,
+                                   void *tvlv_value, u16 tvlv_value_len);
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                    unsigned char **packet_buff,
+                                    int *packet_buff_len, int packet_min_len);
+ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-6.6/batman-adv-tvlv-reject-oversized-tvlv-packets.patch
new file mode 100644 (file)
index 0000000..4895c2e
--- /dev/null
@@ -0,0 +1,85 @@
+From 3fb3cc389028233ac522449e7fc8f0ecf0c986eb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 20:17:56 +0200
+Subject: batman-adv: tvlv: reject oversized TVLV packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f50487e3566358b2b982b7801945e858c78ad9ab upstream.
+
+batadv_tvlv_container_ogm_append() builds a TVLV packet section from
+the tvlv.container_list. The total size of this section is computed by
+batadv_tvlv_container_list_size(), which sums the sizes of all registered
+containers.
+
+The return type and accumulator in batadv_tvlv_container_list_size() were
+u16. If the accumulated size exceeds U16_MAX, the value wraps around,
+causing the subsequent allocation in batadv_tvlv_container_ogm_append()
+to be undersized. The memcpy-style copy that follows would then write
+beyond the end of the allocated buffer, corrupting kernel memory.
+
+Fix this by widening the return type of batadv_tvlv_container_list_size()
+to size_t. In batadv_tvlv_container_ogm_append(), check the computed length
+against U16_MAX before proceeding, and bail out as if the allocation had
+failed when the limit is exceeded.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/tvlv.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
+index 76c6e0599694c7..8d6b017c433cc9 100644
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -13,6 +13,7 @@
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+ #include <linux/kref.h>
++#include <linux/limits.h>
+ #include <linux/list.h>
+ #include <linux/lockdep.h>
+ #include <linux/netdevice.h>
+@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
+  *
+  * Return: size of all currently registered tvlv containers in bytes.
+  */
+-static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
++static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+ {
+       struct batadv_tvlv_container *tvlv;
+-      u16 tvlv_len = 0;
++      size_t tvlv_len = 0;
+       lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
+@@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ {
+       struct batadv_tvlv_container *tvlv;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+-      u16 tvlv_value_len;
++      size_t tvlv_value_len;
+       void *tvlv_value;
+       int tvlv_len_ret;
+       bool ret;
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+       tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
++      if (tvlv_value_len > U16_MAX) {
++              tvlv_len_ret = -E2BIG;
++              goto end;
++      }
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+-- 
+2.53.0
+
diff --git a/queue-6.6/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-6.6/batman-adv-v-stop-ogmv2-on-disabled-interface.patch
new file mode 100644 (file)
index 0000000..955e051
--- /dev/null
@@ -0,0 +1,149 @@
+From efaac38d454c9d628bf4e6794e3767548c67d249 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:29:56 +0200
+Subject: batman-adv: v: stop OGMv2 on disabled interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f8ce8b8331a1bc44ad4905886a482214d428b253 upstream.
+
+When a batadv_hard_iface is disabled, its mesh_iface pointer is set to
+NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via
+batadv_v_ogm_queue_on_if() for interfaces that have since lost their
+mesh_iface association. This results in a NULL pointer dereference when
+batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the
+now NULL hard_iface->mesh_iface to retrieve the batadv_priv.
+
+It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that
+it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was
+called.
+
+Cc: stable@kernel.org
+Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+[ switch to old "mesh_iface" name "soft_iface" ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
+index 8f89ffe6020ced..310248a5812c49 100644
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
+ /**
+  * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to send
+  * @hard_iface: the interface to use to send the OGM
+  */
+-static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
++static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
++                                  struct sk_buff *skb,
+                                   struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+-
+       if (hard_iface->if_status != BATADV_IF_ACTIVE) {
+               kfree_skb(skb);
+               return;
+@@ -189,6 +189,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+ /**
+  * batadv_v_ogm_aggr_send() - flush & send aggregation queue
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @hard_iface: the interface with the aggregation queue to flush
+  *
+  * Aggregates all OGMv2 packets currently in the aggregation queue into a
+@@ -198,7 +199,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
+  *
+  * Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
+  */
+-static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
++static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
++                                 struct batadv_hard_iface *hard_iface)
+ {
+       unsigned int aggr_len = hard_iface->bat_v.aggr_len;
+       struct sk_buff *skb_aggr;
+@@ -228,27 +230,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
+               consume_skb(skb);
+       }
+-      batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
++      batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ }
+ /**
+  * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+  * @skb: the OGM to queue
+  * @hard_iface: the interface to queue the OGM on
+  */
+-static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
++static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
++                                   struct sk_buff *skb,
+                                    struct batadv_hard_iface *hard_iface)
+ {
+-      struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
++      if (hard_iface->soft_iface != bat_priv->soft_iface) {
++              kfree_skb(skb);
++              return;
++      }
+       if (!atomic_read(&bat_priv->aggregated_ogms)) {
+-              batadv_v_ogm_send_to_if(skb, hard_iface);
++              batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+               return;
+       }
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+       if (!batadv_v_ogm_queue_left(skb, hard_iface))
+-              batadv_v_ogm_aggr_send(hard_iface);
++              batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+       __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
+@@ -347,7 +354,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
+                       break;
+               }
+-              batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
++              batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+               batadv_hardif_put(hard_iface);
+       }
+       rcu_read_unlock();
+@@ -387,12 +394,14 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
+ {
+       struct batadv_hard_iface_bat_v *batv;
+       struct batadv_hard_iface *hard_iface;
++      struct batadv_priv *bat_priv;
+       batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
+       hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
++      bat_priv = netdev_priv(hard_iface->soft_iface);
+       spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+-      batadv_v_ogm_aggr_send(hard_iface);
++      batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+       spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+       batadv_v_ogm_start_queue_timer(hard_iface);
+@@ -582,7 +591,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
+                  if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
+                  ogm_forward->ttl, if_incoming->net_dev->name);
+-      batadv_v_ogm_queue_on_if(skb, if_outgoing);
++      batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+ out:
+       batadv_orig_ifinfo_put(orig_ifinfo);
+-- 
+2.53.0
+
diff --git a/queue-6.6/drm-dp-add-edp-1.5-bit-definition.patch b/queue-6.6/drm-dp-add-edp-1.5-bit-definition.patch
new file mode 100644 (file)
index 0000000..0a19b64
--- /dev/null
@@ -0,0 +1,41 @@
+From 33966379ae9feb7cc7a5b0daa73547275a5dc158 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 12:06:35 +0300
+Subject: drm/dp: Add eDP 1.5 bit definition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Suraj Kandpal <suraj.kandpal@intel.com>
+
+commit 5dfc37a6b77bf6beedbd30d70184b54e1a08ccac upstream.
+
+Add the eDP revision bit value for 1.5.
+
+Spec: eDPv1.5 Table 16-5
+Signed-off-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Reviewed-by: Arun R Murthy <arun.r.murthy@intel.com>
+Tested-by: Ben Kao <ben.kao@intel.com>
+Acked-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250206063253.2827017-2-suraj.kandpal@intel.com
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/drm/display/drm_dp.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h
+index e69cece404b3cf..861527bf9ccb7f 100644
+--- a/include/drm/display/drm_dp.h
++++ b/include/drm/display/drm_dp.h
+@@ -959,6 +959,7 @@
+ # define DP_EDP_14                        0x03
+ # define DP_EDP_14a                         0x04    /* eDP 1.4a */
+ # define DP_EDP_14b                         0x05    /* eDP 1.4b */
++# define DP_EDP_15                        0x06    /* eDP 1.5 */
+ #define DP_EDP_GENERAL_CAP_1              0x701
+ # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP         (1 << 0)
+-- 
+2.53.0
+
diff --git a/queue-6.6/drm-fbcon-vga_switcheroo-avoid-race-condition-in-fbc.patch b/queue-6.6/drm-fbcon-vga_switcheroo-avoid-race-condition-in-fbc.patch
new file mode 100644 (file)
index 0000000..a79aba6
--- /dev/null
@@ -0,0 +1,144 @@
+From 5eaa0004c1a4071fbf281711d29ed9478820f7e6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:18:17 +0800
+Subject: drm, fbcon, vga_switcheroo: Avoid race condition in fbcon setup
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit eb76d0f5553575599561010f24c277cc5b31d003 ]
+
+Protect vga_switcheroo_client_fb_set() with console lock. Avoids OOB
+access in fbcon_remap_all(). Without holding the console lock the call
+races with switching outputs.
+
+VGA switcheroo calls fbcon_remap_all() when switching clients. The fbcon
+function uses struct fb_info.node, which is set by register_framebuffer().
+As the fb-helper code currently sets up VGA switcheroo before registering
+the framebuffer, the value of node is -1 and therefore not a legal value.
+For example, fbcon uses the value within set_con2fb_map() [1] as an index
+into an array.
+
+Moving vga_switcheroo_client_fb_set() after register_framebuffer() can
+result in VGA switching that does not switch fbcon correctly.
+
+Therefore move vga_switcheroo_client_fb_set() under fbcon_fb_registered(),
+which already holds the console lock. Fbdev calls fbcon_fb_registered()
+from within register_framebuffer(). Serializes the helper with VGA
+switcheroo's call to fbcon_remap_all().
+
+Although vga_switcheroo_client_fb_set() takes an instance of struct fb_info
+as parameter, it really only needs the contained fbcon state. Moving the
+call to fbcon initialization is therefore cleaner than before. Only amdgpu,
+i915, nouveau and radeon support vga_switcheroo. For all other drivers,
+this change does nothing.
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Link: https://elixir.bootlin.com/linux/v6.17/source/drivers/video/fbdev/core/fbcon.c#L2942 # [1]
+Fixes: 6a9ee8af344e ("vga_switcheroo: initial implementation (v15)")
+Acked-by: Javier Martinez Canillas <javierm@redhat.com>
+Acked-by: Alex Deucher <alexander.deucher@amd.com>
+Cc: dri-devel@lists.freedesktop.org
+Cc: nouveau@lists.freedesktop.org
+Cc: amd-gfx@lists.freedesktop.org
+Cc: linux-fbdev@vger.kernel.org
+Cc: <stable@vger.kernel.org> # v2.6.34+
+Link: https://patch.msgid.link/20251105161549.98836-1-tzimmermann@suse.de
+[ Minor context conflict resolved. ]
+Signed-off-by: Wenshan Lan <jetlan9@163.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fb_helper.c  | 14 --------------
+ drivers/video/fbdev/core/fbcon.c |  9 +++++++++
+ 2 files changed, 9 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index eee7b56d441f71..9691c93f19a0dd 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -30,9 +30,7 @@
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+ #include <linux/console.h>
+-#include <linux/pci.h>
+ #include <linux/sysrq.h>
+-#include <linux/vga_switcheroo.h>
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_drv.h>
+@@ -575,11 +573,6 @@ EXPORT_SYMBOL(drm_fb_helper_release_info);
+  */
+ void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper)
+ {
+-      struct fb_info *info = fb_helper->info;
+-      struct device *dev = info->device;
+-
+-      if (dev_is_pci(dev))
+-              vga_switcheroo_client_fb_set(to_pci_dev(dev), NULL);
+       unregister_framebuffer(fb_helper->info);
+ }
+ EXPORT_SYMBOL(drm_fb_helper_unregister_info);
+@@ -1673,7 +1666,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
+ {
+       struct drm_client_dev *client = &fb_helper->client;
+       struct drm_fb_helper_surface_size sizes;
+-      struct fb_info *info;
+       int ret;
+       ret = drm_fb_helper_find_sizes(fb_helper, &sizes);
+@@ -1691,12 +1683,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
+       strcpy(fb_helper->fb->comm, "[fbcon]");
+-      info = fb_helper->info;
+-
+-      /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */
+-      if (dev_is_pci(info->device))
+-              vga_switcheroo_client_fb_set(to_pci_dev(info->device), info);
+-
+       return 0;
+ }
+diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
+index 703c4e851612c8..d1ac4e45eea68e 100644
+--- a/drivers/video/fbdev/core/fbcon.c
++++ b/drivers/video/fbdev/core/fbcon.c
+@@ -64,6 +64,7 @@
+ #include <linux/console.h>
+ #include <linux/string.h>
+ #include <linux/kd.h>
++#include <linux/pci.h>
+ #include <linux/slab.h>
+ #include <linux/fb.h>
+ #include <linux/fbcon.h>
+@@ -75,6 +76,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/crc32.h> /* For counting font checksums */
+ #include <linux/uaccess.h>
++#include <linux/vga_switcheroo.h>
+ #include <asm/irq.h>
+ #include "fbcon.h"
+@@ -2914,6 +2916,9 @@ void fbcon_fb_unregistered(struct fb_info *info)
+       console_lock();
++      if (info->device && dev_is_pci(info->device))
++              vga_switcheroo_client_fb_set(to_pci_dev(info->device), NULL);
++
+       fbcon_registered_fb[info->node] = NULL;
+       fbcon_num_registered_fb--;
+@@ -3047,6 +3052,10 @@ static int do_fb_registered(struct fb_info *info)
+               }
+       }
++      /* Set the fb info for vga_switcheroo clients. Does nothing otherwise. */
++      if (info->device && dev_is_pci(info->device))
++              vga_switcheroo_client_fb_set(to_pci_dev(info->device), info);
++
+       return ret;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/drm-fbdev-helper-set-and-clear-vga-switcheroo-client.patch b/queue-6.6/drm-fbdev-helper-set-and-clear-vga-switcheroo-client.patch
new file mode 100644 (file)
index 0000000..de3d982
--- /dev/null
@@ -0,0 +1,89 @@
+From d12b29d39cbc62bb933b2904b7c43cc20a0f6ea3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 21:18:16 +0800
+Subject: drm/fbdev-helper: Set and clear VGA switcheroo client from fb_info
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 02257549daf7ff839e2be6d4f3cac975e522fd7a ]
+
+Call vga_switcheroo_client_fb_set() with the PCI device from the
+instance of struct fb_info. All fbdev clients now run these calls.
+For non-PCI devices or drivers without vga-switcheroo, this does
+nothing. For i915 and radeon, it allows these drivers to use a
+common fbdev client.
+
+The device is the same as the one stored in struct drm_client and
+struct drm_fb_helper, so there is no difference in behavior. Some
+NULL-pointer checks are being removed, where those pointers cannot
+be NULL.
+
+v4:
+- clarify call semantics for drm_fb_helper_unregister_info() (Javier)
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240924071734.98201-3-tzimmermann@suse.de
+[ The variable 'dev' in the function drm_fb_helper_single_fb_probe() is
+unused; remove it in v6.6. ]
+Signed-off-by: Wenshan Lan <jetlan9@163.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fb_helper.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index b507c1c008a3e9..eee7b56d441f71 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -567,7 +567,7 @@ EXPORT_SYMBOL(drm_fb_helper_release_info);
+ /**
+  * drm_fb_helper_unregister_info - unregister fb_info framebuffer device
+- * @fb_helper: driver-allocated fbdev helper, can be NULL
++ * @fb_helper: driver-allocated fbdev helper, must not be NULL
+  *
+  * A wrapper around unregister_framebuffer, to release the fb_info
+  * framebuffer device. This must be called before releasing all resources for
+@@ -575,8 +575,12 @@ EXPORT_SYMBOL(drm_fb_helper_release_info);
+  */
+ void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper)
+ {
+-      if (fb_helper && fb_helper->info)
+-              unregister_framebuffer(fb_helper->info);
++      struct fb_info *info = fb_helper->info;
++      struct device *dev = info->device;
++
++      if (dev_is_pci(dev))
++              vga_switcheroo_client_fb_set(to_pci_dev(dev), NULL);
++      unregister_framebuffer(fb_helper->info);
+ }
+ EXPORT_SYMBOL(drm_fb_helper_unregister_info);
+@@ -1668,8 +1672,8 @@ static int drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
+ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
+ {
+       struct drm_client_dev *client = &fb_helper->client;
+-      struct drm_device *dev = fb_helper->dev;
+       struct drm_fb_helper_surface_size sizes;
++      struct fb_info *info;
+       int ret;
+       ret = drm_fb_helper_find_sizes(fb_helper, &sizes);
+@@ -1687,9 +1691,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
+       strcpy(fb_helper->fb->comm, "[fbcon]");
++      info = fb_helper->info;
++
+       /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */
+-      if (dev_is_pci(dev->dev))
+-              vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), fb_helper->info);
++      if (dev_is_pci(info->device))
++              vga_switcheroo_client_fb_set(to_pci_dev(info->device), info);
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-6.6/drm-i915-psr-add-defininitions-for-intel_wa_register.patch
new file mode 100644 (file)
index 0000000..969cc98
--- /dev/null
@@ -0,0 +1,73 @@
+From fe9c93a9f24bfc9ed637d9fad967d5517753f154 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 12:06:33 +0300
+Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD
+ register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream.
+
+EDP specification says:
+
+"If either VSC SDP is unable to be transmitted 100 ns before the SU region,
+the Source device may optionally transmit the VSC SDP during the prior
+video scan line’s HBlank period There is a Intel specific drm dp register
+currently containing bits related how TCON can support PSR2 with SDP on
+prior line."
+
+Unfortunately many panels are having problems in implementing this. So
+there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to
+figure out if this is properly implemented on a panel or if panel doesn't
+require that 100 ns delay before the SU region. Here are the definitions in
+this custom DPCD address:
+
+0 = Panel doesn't support SDP on prior line
+1 = Panel supports SDP on prior line
+2 = Panel doesn't have 100ns requirement
+3 = Reserved
+
+Add definitions for this new register and it's values into new header
+intel_dpcd.h.
+
+v2: add INTEL_DPCD_ prefix to definitions
+
+Bspec: 74741
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com
+(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+ create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h
+
+diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h
+new file mode 100644
+index 00000000000000..4aea5326f2ed48
+--- /dev/null
++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: MIT */
++/*
++ * Copyright © 2026 Intel Corporation
++ */
++
++#ifndef __INTEL_DPCD_H__
++#define __INTEL_DPCD_H__
++
++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS                                     0x3f0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK        REG_GENMASK(1, 0)
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1                  0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE           1
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE                2
++
++#endif /* __INTEL_DPCD_H__ */
+-- 
+2.53.0
+
diff --git a/queue-6.6/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-6.6/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
new file mode 100644 (file)
index 0000000..e84fbcd
--- /dev/null
@@ -0,0 +1,77 @@
+From d04df89f279f3c07dfec4c47955fe07ad65fea95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 12:06:36 +0300
+Subject: drm/i915/psr: Apply Intel DPCD workaround when SDP on prior line used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit 4703049f768fc1c1caac754134118bee1a3af189 upstream.
+
+There is Intel specific workaround DPCD address containing workaround for
+case where SDP is on prior line. Apply this workaround according to values
+in the offset.
+
+Fixes: 61e887329e33 ("drm/i915/xelpd: Handle PSR2 SDP indication in the prior scanline")
+Cc: <stable@vger.kernel.org> # v5.15+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com
+(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_psr.c | 27 +++++++++++++++++++++++-
+ 1 file changed, 26 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 49842f7877f435..416686a6566bab 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -1007,6 +1007,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp,
+       return true;
+ }
++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp,
++                                       struct intel_crtc_state *crtc_state)
++{
++      u8 early_scanline_support = intel_dp->intel_wa_dpcd &
++              INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK;
++
++      if (intel_dp->edp_dpcd[0] >= DP_EDP_15)
++              return true;
++
++      switch (early_scanline_support) {
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return false;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE:
++              return true;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return true;
++      default:
++              MISSING_CASE(early_scanline_support);
++              return false;
++      }
++}
++
+ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp,
+                                                       struct intel_crtc_state *crtc_state)
+ {
+@@ -1028,7 +1052,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d
+               return false;
+       crtc_state->req_psr2_sdp_prior_scanline = true;
+-      return true;
++
++      return apply_scanline_indication_wa(intel_dp, crtc_state);
+ }
+ static bool _compute_psr2_wake_times(struct intel_dp *intel_dp,
+-- 
+2.53.0
+
diff --git a/queue-6.6/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-6.6/drm-i915-psr-read-intel-dpcd-workaround-register.patch
new file mode 100644 (file)
index 0000000..8af80b9
--- /dev/null
@@ -0,0 +1,67 @@
+From dd0d64dd0c92a33edbb0c7b03ebf6863cd04e8ae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 12:06:34 +0300
+Subject: drm/i915/psr: Read Intel DPCD workaround register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream.
+
+Read Intel DPCD workaround register and store it into
+intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains
+only PSR workaround for PSR2 SDP on prior scanline implementation.
+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com
+(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_display_types.h | 1 +
+ drivers/gpu/drm/i915/display/intel_psr.c           | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
+index 1c23b186aff20c..3a228cfb1550f2 100644
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -1719,6 +1719,7 @@ struct intel_dp {
+       u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE];
+       u8 fec_capable;
+       u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE];
++      u8 intel_wa_dpcd;
+       /* source rates */
+       int num_source_rates;
+       const int *source_rates;
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index b0818dc8480edb..49842f7877f435 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -31,6 +31,7 @@
+ #include "intel_de.h"
+ #include "intel_display_types.h"
+ #include "intel_dp.h"
++#include "intel_dpcd.h"
+ #include "intel_dp_aux.h"
+ #include "intel_hdmi.h"
+ #include "intel_psr.h"
+@@ -528,6 +529,12 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp)
+                       intel_dp_get_su_granularity(intel_dp);
+               }
+       }
++
++      if (intel_dp->psr.sink_psr2_support)
++              drm_dp_dpcd_read(&intel_dp->aux,
++                               INTEL_DPCD_INTEL_WA_REGISTER_CAPS,
++                               &intel_dp->intel_wa_dpcd,
++                               sizeof(intel_dp->intel_wa_dpcd));
+ }
+ static void hsw_psr_setup_aux(struct intel_dp *intel_dp)
+-- 
+2.53.0
+
diff --git a/queue-6.6/inet-frags-add-inet_frag_queue_flush.patch b/queue-6.6/inet-frags-add-inet_frag_queue_flush.patch
new file mode 100644 (file)
index 0000000..d5771da
--- /dev/null
@@ -0,0 +1,98 @@
+From 4193c8738c7345b404b7e306e9011d8a6d70556e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 18:24:08 +0800
+Subject: inet: frags: add inet_frag_queue_flush()
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ Upstream commit 1231eec6994be29d6bb5c303dfa54731ed9fc0e6 ]
+
+Instead of exporting inet_frag_rbtree_purge() which requires that
+caller takes care of memory accounting, add a new helper. We will
+need to call it from a few places in the next patch.
+
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20251207010942.1672972-3-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Rajani Kantha <681739313@139.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/inet_frag.h  |  5 ++---
+ net/ipv4/inet_fragment.c | 15 ++++++++++++---
+ net/ipv4/ip_fragment.c   |  6 +-----
+ 3 files changed, 15 insertions(+), 11 deletions(-)
+
+diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
+index 5af6eb14c5db15..94edc0e130d2c4 100644
+--- a/include/net/inet_frag.h
++++ b/include/net/inet_frag.h
+@@ -141,9 +141,8 @@ void inet_frag_kill(struct inet_frag_queue *q);
+ void inet_frag_destroy(struct inet_frag_queue *q);
+ struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key);
+-/* Free all skbs in the queue; return the sum of their truesizes. */
+-unsigned int inet_frag_rbtree_purge(struct rb_root *root,
+-                                  enum skb_drop_reason reason);
++void inet_frag_queue_flush(struct inet_frag_queue *q,
++                         enum skb_drop_reason reason);
+ static inline void inet_frag_put(struct inet_frag_queue *q)
+ {
+diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
+index 496308c0238485..1e390d24f11440 100644
+--- a/net/ipv4/inet_fragment.c
++++ b/net/ipv4/inet_fragment.c
+@@ -264,8 +264,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head)
+       kmem_cache_free(f->frags_cachep, q);
+ }
+-unsigned int inet_frag_rbtree_purge(struct rb_root *root,
+-                                  enum skb_drop_reason reason)
++static unsigned int
++inet_frag_rbtree_purge(struct rb_root *root, enum skb_drop_reason reason)
+ {
+       struct rb_node *p = rb_first(root);
+       unsigned int sum = 0;
+@@ -285,7 +285,16 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root,
+       }
+       return sum;
+ }
+-EXPORT_SYMBOL(inet_frag_rbtree_purge);
++
++void inet_frag_queue_flush(struct inet_frag_queue *q,
++                         enum skb_drop_reason reason)
++{
++      unsigned int sum;
++
++      sum = inet_frag_rbtree_purge(&q->rb_fragments, reason);
++      sub_frag_mem_limit(q->fqdir, sum);
++}
++EXPORT_SYMBOL(inet_frag_queue_flush);
+ void inet_frag_destroy(struct inet_frag_queue *q)
+ {
+diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
+index 484edc8513e4b7..7214d5bcc647e5 100644
+--- a/net/ipv4/ip_fragment.c
++++ b/net/ipv4/ip_fragment.c
+@@ -253,16 +253,12 @@ static int ip_frag_too_far(struct ipq *qp)
+ static int ip_frag_reinit(struct ipq *qp)
+ {
+-      unsigned int sum_truesize = 0;
+-
+       if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) {
+               refcount_inc(&qp->q.refcnt);
+               return -ETIMEDOUT;
+       }
+-      sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments,
+-                                            SKB_DROP_REASON_FRAG_TOO_FAR);
+-      sub_frag_mem_limit(qp->q.fqdir, sum_truesize);
++      inet_frag_queue_flush(&qp->q, SKB_DROP_REASON_FRAG_TOO_FAR);
+       qp->q.flags = 0;
+       qp->q.len = 0;
+-- 
+2.53.0
+
diff --git a/queue-6.6/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch b/queue-6.6/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch
new file mode 100644 (file)
index 0000000..15a0e4c
--- /dev/null
@@ -0,0 +1,187 @@
+From 6aa360db0b570312fae12f5544c7aa8e58599fd3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 18:24:09 +0800
+Subject: inet: frags: flush pending skbs in fqdir_pre_exit()
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ Upstream commit 006a5035b495dec008805df249f92c22c89c3d2e ]
+
+We have been seeing occasional deadlocks on pernet_ops_rwsem since
+September in NIPA. The stuck task was usually modprobe (often loading
+a driver like ipvlan), trying to take the lock as a Writer.
+lockdep does not track readers for rwsems so the read wasn't obvious
+from the reports.
+
+On closer inspection the Reader holding the lock was conntrack looping
+forever in nf_conntrack_cleanup_net_list(). Based on past experience
+with occasional NIPA crashes I looked thru the tests which run before
+the crash and noticed that the crash follows ip_defrag.sh. An immediate
+red flag. Scouring thru (de)fragmentation queues reveals skbs sitting
+around, holding conntrack references.
+
+The problem is that since conntrack depends on nf_defrag_ipv6,
+nf_defrag_ipv6 will load first. Since nf_defrag_ipv6 loads first its
+netns exit hooks run _after_ conntrack's netns exit hook.
+
+Flush all fragment queue SKBs during fqdir_pre_exit() to release
+conntrack references before conntrack cleanup runs. Also flush
+the queues in timer expiry handlers when they discover fqdir->dead
+is set, in case packet sneaks in while we're running the pre_exit
+flush.
+
+The commit under Fixes is not exactly the culprit, but I think
+previously the timer firing would eventually unblock the spinning
+conntrack.
+
+Fixes: d5dd88794a13 ("inet: fix various use-after-free in defrags units")
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20251207010942.1672972-4-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Rajani Kantha <681739313@139.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/inet_frag.h  | 13 +------------
+ include/net/ipv6_frag.h  |  9 ++++++---
+ net/ipv4/inet_fragment.c | 36 ++++++++++++++++++++++++++++++++++++
+ net/ipv4/ip_fragment.c   | 12 +++++++-----
+ 4 files changed, 50 insertions(+), 20 deletions(-)
+
+diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
+index 94edc0e130d2c4..fcabb34fff35de 100644
+--- a/include/net/inet_frag.h
++++ b/include/net/inet_frag.h
+@@ -123,18 +123,7 @@ void inet_frags_fini(struct inet_frags *);
+ int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net);
+-static inline void fqdir_pre_exit(struct fqdir *fqdir)
+-{
+-      /* Prevent creation of new frags.
+-       * Pairs with READ_ONCE() in inet_frag_find().
+-       */
+-      WRITE_ONCE(fqdir->high_thresh, 0);
+-
+-      /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire()
+-       * and ip6frag_expire_frag_queue().
+-       */
+-      WRITE_ONCE(fqdir->dead, true);
+-}
++void fqdir_pre_exit(struct fqdir *fqdir);
+ void fqdir_exit(struct fqdir *fqdir);
+ void inet_frag_kill(struct inet_frag_queue *q);
+diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h
+index 7321ffe3a108c1..df61b98b521531 100644
+--- a/include/net/ipv6_frag.h
++++ b/include/net/ipv6_frag.h
+@@ -68,9 +68,6 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
+       struct sk_buff *head;
+       rcu_read_lock();
+-      /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */
+-      if (READ_ONCE(fq->q.fqdir->dead))
+-              goto out_rcu_unlock;
+       spin_lock(&fq->q.lock);
+       if (fq->q.flags & INET_FRAG_COMPLETE)
+@@ -79,6 +76,12 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
+       fq->q.flags |= INET_FRAG_DROP;
+       inet_frag_kill(&fq->q);
++      /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */
++      if (READ_ONCE(fq->q.fqdir->dead)) {
++              inet_frag_queue_flush(&fq->q, 0);
++              goto out;
++      }
++
+       dev = dev_get_by_index_rcu(net, fq->iif);
+       if (!dev)
+               goto out;
+diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
+index 1e390d24f11440..8cf88882d65e3e 100644
+--- a/net/ipv4/inet_fragment.c
++++ b/net/ipv4/inet_fragment.c
+@@ -219,6 +219,41 @@ static int __init inet_frag_wq_init(void)
+ pure_initcall(inet_frag_wq_init);
++void fqdir_pre_exit(struct fqdir *fqdir)
++{
++      struct inet_frag_queue *fq;
++      struct rhashtable_iter hti;
++
++      /* Prevent creation of new frags.
++       * Pairs with READ_ONCE() in inet_frag_find().
++       */
++      WRITE_ONCE(fqdir->high_thresh, 0);
++
++      /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire()
++       * and ip6frag_expire_frag_queue().
++       */
++      WRITE_ONCE(fqdir->dead, true);
++
++      rhashtable_walk_enter(&fqdir->rhashtable, &hti);
++      rhashtable_walk_start(&hti);
++
++      while ((fq = rhashtable_walk_next(&hti))) {
++              if (IS_ERR(fq)) {
++                      if (PTR_ERR(fq) != -EAGAIN)
++                              break;
++                      continue;
++              }
++              spin_lock_bh(&fq->lock);
++              if (!(fq->flags & INET_FRAG_COMPLETE))
++                      inet_frag_queue_flush(fq, 0);
++              spin_unlock_bh(&fq->lock);
++      }
++
++      rhashtable_walk_stop(&hti);
++      rhashtable_walk_exit(&hti);
++}
++EXPORT_SYMBOL(fqdir_pre_exit);
++
+ void fqdir_exit(struct fqdir *fqdir)
+ {
+       INIT_WORK(&fqdir->destroy_work, fqdir_work_fn);
+@@ -291,6 +326,7 @@ void inet_frag_queue_flush(struct inet_frag_queue *q,
+ {
+       unsigned int sum;
++      reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT;
+       sum = inet_frag_rbtree_purge(&q->rb_fragments, reason);
+       sub_frag_mem_limit(q->fqdir, sum);
+ }
+diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
+index 7214d5bcc647e5..f5b5aa036cc668 100644
+--- a/net/ipv4/ip_fragment.c
++++ b/net/ipv4/ip_fragment.c
+@@ -148,11 +148,6 @@ static void ip_expire(struct timer_list *t)
+       net = qp->q.fqdir->net;
+       rcu_read_lock();
+-
+-      /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */
+-      if (READ_ONCE(qp->q.fqdir->dead))
+-              goto out_rcu_unlock;
+-
+       spin_lock(&qp->q.lock);
+       if (qp->q.flags & INET_FRAG_COMPLETE)
+@@ -160,6 +155,13 @@ static void ip_expire(struct timer_list *t)
+       qp->q.flags |= INET_FRAG_DROP;
+       ipq_kill(qp);
++
++      /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */
++      if (READ_ONCE(qp->q.fqdir->dead)) {
++              inet_frag_queue_flush(&qp->q, 0);
++              goto out;
++      }
++
+       __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
+       __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT);
+-- 
+2.53.0
+
diff --git a/queue-6.6/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch b/queue-6.6/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch
new file mode 100644 (file)
index 0000000..a31e2e7
--- /dev/null
@@ -0,0 +1,759 @@
+From e715b068fd0d731c4eb71b319158ce50a417e517 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 20 Dec 2025 10:33:26 +0000
+Subject: media: rc: fix race between unregister and urb/irq callbacks
+
+From: Sean Young <sean@mess.org>
+
+[ Upstream commit dccc0c3ddf8f16071736f98a7d6dd46a2d43e037 ]
+
+Some rc device drivers have a race condition between rc_unregister_device()
+and irq or urb callbacks. This is because rc_unregister_device() does two
+things, it marks the device as unregistered so no new commands can be
+issued and then it calls rc_free_device(). This means the driver has no
+chance to cancel any pending urb callbacks or interrupts after the device
+has been marked as unregistered. Those callbacks may access struct rc_dev
+or its members (e.g. struct ir_raw_event_ctrl), which have been freed by
+rc_free_device().
+
+This change removes the implicit call to rc_free_device() from
+rc_unregister_device(). This means that device drivers can call
+rc_unregister_device() in their remove or disconnect function, then cancel
+all the urbs and interrupts before explicitly calling rc_free_device().
+
+Note this is an alternative fix for an issue found by Haotian Zhang, see
+the Closes: tags.
+
+Reported-by: Haotian Zhang <vulab@iscas.ac.cn>
+Closes: https://lore.kernel.org/linux-media/20251114101432.2566-1-vulab@iscas.ac.cn/
+Closes: https://lore.kernel.org/linux-media/20251114101418.2548-1-vulab@iscas.ac.cn/
+Closes: https://lore.kernel.org/linux-media/20251114101346.2530-1-vulab@iscas.ac.cn/
+Closes: https://lore.kernel.org/linux-media/20251114090605.2413-1-vulab@iscas.ac.cn/
+Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Stable-dep-of: 646ebdd31058 ("media: rc: ttusbir: fix inverted error logic")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/bridge/sil-sii8620.c        | 1 +
+ drivers/hid/hid-picolcd_cir.c               | 1 +
+ drivers/media/cec/core/cec-core.c           | 2 +-
+ drivers/media/common/siano/smsir.c          | 1 +
+ drivers/media/i2c/ir-kbd-i2c.c              | 2 ++
+ drivers/media/pci/bt8xx/bttv-input.c        | 3 ++-
+ drivers/media/pci/cx23885/cx23885-input.c   | 1 +
+ drivers/media/pci/cx88/cx88-input.c         | 3 ++-
+ drivers/media/pci/dm1105/dm1105.c           | 1 +
+ drivers/media/pci/mantis/mantis_input.c     | 1 +
+ drivers/media/pci/saa7134/saa7134-input.c   | 1 +
+ drivers/media/pci/smipcie/smipcie-ir.c      | 1 +
+ drivers/media/pci/ttpci/budget-ci.c         | 1 +
+ drivers/media/rc/ati_remote.c               | 6 +++---
+ drivers/media/rc/ene_ir.c                   | 2 +-
+ drivers/media/rc/fintek-cir.c               | 3 ++-
+ drivers/media/rc/igorplugusb.c              | 1 +
+ drivers/media/rc/iguanair.c                 | 1 +
+ drivers/media/rc/img-ir/img-ir-hw.c         | 3 ++-
+ drivers/media/rc/img-ir/img-ir-raw.c        | 3 ++-
+ drivers/media/rc/imon.c                     | 3 ++-
+ drivers/media/rc/ir-hix5hd2.c               | 2 +-
+ drivers/media/rc/ir_toy.c                   | 1 +
+ drivers/media/rc/ite-cir.c                  | 2 +-
+ drivers/media/rc/mceusb.c                   | 1 +
+ drivers/media/rc/rc-ir-raw.c                | 5 -----
+ drivers/media/rc/rc-loopback.c              | 1 +
+ drivers/media/rc/rc-main.c                  | 6 +-----
+ drivers/media/rc/redrat3.c                  | 4 +++-
+ drivers/media/rc/st_rc.c                    | 2 +-
+ drivers/media/rc/streamzap.c                | 7 ++++---
+ drivers/media/rc/sunxi-cir.c                | 1 +
+ drivers/media/rc/ttusbir.c                  | 2 +-
+ drivers/media/rc/winbond-cir.c              | 2 +-
+ drivers/media/rc/xbox_remote.c              | 5 +++--
+ drivers/media/usb/au0828/au0828-input.c     | 1 +
+ drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 1 +
+ drivers/media/usb/dvb-usb/dvb-usb-remote.c  | 6 ++++--
+ drivers/media/usb/em28xx/em28xx-input.c     | 1 +
+ drivers/staging/media/av7110/av7110_ir.c    | 1 +
+ include/media/rc-core.h                     | 2 --
+ 41 files changed, 58 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
+index 599164e3877dbd..93c41c8dedff84 100644
+--- a/drivers/gpu/drm/bridge/sil-sii8620.c
++++ b/drivers/gpu/drm/bridge/sil-sii8620.c
+@@ -2220,6 +2220,7 @@ static void sii8620_detach(struct drm_bridge *bridge)
+               return;
+       rc_unregister_device(ctx->rc_dev);
++      rc_free_device(ctx->rc_dev);
+ }
+ static int sii8620_is_packing_required(struct sii8620 *ctx,
+diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
+index d6faa0e00f95ac..6d4c636e1c9f7e 100644
+--- a/drivers/hid/hid-picolcd_cir.c
++++ b/drivers/hid/hid-picolcd_cir.c
+@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data)
+       data->rc_dev = NULL;
+       rc_unregister_device(rdev);
++      rc_free_device(rdev);
+ }
+diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
+index b4dcc352a3c6f1..74dff5f8d33658 100644
+--- a/drivers/media/cec/core/cec-core.c
++++ b/drivers/media/cec/core/cec-core.c
+@@ -366,8 +366,8 @@ int cec_register_adapter(struct cec_adapter *adap,
+       res = cec_devnode_register(&adap->devnode, adap->owner);
+       if (res) {
+ #ifdef CONFIG_MEDIA_CEC_RC
+-              /* Note: rc_unregister also calls rc_free */
+               rc_unregister_device(adap->rc);
++              rc_free_device(adap->rc);
+               adap->rc = NULL;
+ #endif
+               return res;
+diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
+index d85c78c104b990..5f4c0aa7a0d72a 100644
+--- a/drivers/media/common/siano/smsir.c
++++ b/drivers/media/common/siano/smsir.c
+@@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
+ void sms_ir_exit(struct smscore_device_t *coredev)
+ {
+       rc_unregister_device(coredev->ir.dev);
++      rc_free_device(coredev->ir.dev);
+       pr_debug("\n");
+ }
+diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
+index a8026f0f980f98..943e761c149643 100644
+--- a/drivers/media/i2c/ir-kbd-i2c.c
++++ b/drivers/media/i2c/ir-kbd-i2c.c
+@@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work)
+               mutex_unlock(&ir->lock);
+               if (rc == -ENODEV) {
+                       rc_unregister_device(ir->rc);
++                      rc_free_device(ir->rc);
+                       ir->rc = NULL;
+                       return;
+               }
+@@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client)
+       i2c_unregister_device(ir->tx_c);
+       rc_unregister_device(ir->rc);
++      rc_free_device(ir->rc);
+ }
+ static const struct i2c_device_id ir_kbd_id[] = {
+diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
+index 41226f1d0e5b64..d70e6282c48b2e 100644
+--- a/drivers/media/pci/bt8xx/bttv-input.c
++++ b/drivers/media/pci/bt8xx/bttv-input.c
+@@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv)
+       if (btv->remote == NULL)
+               return;
+-      bttv_ir_stop(btv);
+       rc_unregister_device(btv->remote->dev);
++      bttv_ir_stop(btv);
++      rc_free_device(btv->remote->dev);
+       kfree(btv->remote);
+       btv->remote = NULL;
+ }
+diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
+index d2e84c6457e0ab..722329ef3fd2cc 100644
+--- a/drivers/media/pci/cx23885/cx23885-input.c
++++ b/drivers/media/pci/cx23885/cx23885-input.c
+@@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev)
+       if (dev->kernel_ir == NULL)
+               return;
+       rc_unregister_device(dev->kernel_ir->rc);
++      rc_free_device(dev->kernel_ir->rc);
+       kfree(dev->kernel_ir->phys);
+       kfree(dev->kernel_ir->name);
+       kfree(dev->kernel_ir);
+diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
+index a04a1d33fadb1b..74a8769dd6c79c 100644
+--- a/drivers/media/pci/cx88/cx88-input.c
++++ b/drivers/media/pci/cx88/cx88-input.c
+@@ -510,8 +510,9 @@ int cx88_ir_fini(struct cx88_core *core)
+       if (!ir)
+               return 0;
+-      cx88_ir_stop(core);
+       rc_unregister_device(ir->dev);
++      cx88_ir_stop(core);
++      rc_free_device(ir->dev);
+       kfree(ir);
+       /* done */
+diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
+index 9e9c7c071accce..e1185aa669f480 100644
+--- a/drivers/media/pci/dm1105/dm1105.c
++++ b/drivers/media/pci/dm1105/dm1105.c
+@@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105)
+ static void dm1105_ir_exit(struct dm1105_dev *dm1105)
+ {
+       rc_unregister_device(dm1105->ir.dev);
++      rc_free_device(dm1105->ir.dev);
+ }
+ static int dm1105_hw_init(struct dm1105_dev *dev)
+diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c
+index 34c0d979240fda..edb4cacf55d229 100644
+--- a/drivers/media/pci/mantis/mantis_input.c
++++ b/drivers/media/pci/mantis/mantis_input.c
+@@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init);
+ void mantis_input_exit(struct mantis_pci *mantis)
+ {
+       rc_unregister_device(mantis->rc);
++      rc_free_device(mantis->rc);
+ }
+ EXPORT_SYMBOL_GPL(mantis_input_exit);
+diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
+index 8610eb473b39e1..8a0f26d94d1de8 100644
+--- a/drivers/media/pci/saa7134/saa7134-input.c
++++ b/drivers/media/pci/saa7134/saa7134-input.c
+@@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
+               return;
+       rc_unregister_device(dev->remote->dev);
++      rc_free_device(dev->remote->dev);
+       kfree(dev->remote);
+       dev->remote = NULL;
+ }
+diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c
+index c0604d9c70119a..0bbe4fa2d5a84e 100644
+--- a/drivers/media/pci/smipcie/smipcie-ir.c
++++ b/drivers/media/pci/smipcie/smipcie-ir.c
+@@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev)
+       rc_unregister_device(rc_dev);
+       smi_ir_stop(ir);
++      rc_free_device(rc_dev);
+       ir->rc_dev = NULL;
+ }
+diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
+index 66e1a004ee431c..2ad30f91bcd9ee 100644
+--- a/drivers/media/pci/ttpci/budget-ci.c
++++ b/drivers/media/pci/ttpci/budget-ci.c
+@@ -247,6 +247,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
+       tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
+       rc_unregister_device(budget_ci->ir.dev);
++      rc_free_device(budget_ci->ir.dev);
+ }
+ static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
+index fff4dd48eacad2..1d68834ecd0b15 100644
+--- a/drivers/media/rc/ati_remote.c
++++ b/drivers/media/rc/ati_remote.c
+@@ -921,7 +921,6 @@ static int ati_remote_probe(struct usb_interface *interface,
+       input_free_device(input_dev);
+  exit_unregister_device:
+       rc_unregister_device(rc_dev);
+-      rc_dev = NULL;
+  exit_kill_urbs:
+       usb_kill_urb(ati_remote->irq_urb);
+       usb_kill_urb(ati_remote->out_urb);
+@@ -941,18 +940,19 @@ static void ati_remote_disconnect(struct usb_interface *interface)
+       struct ati_remote *ati_remote;
+       ati_remote = usb_get_intfdata(interface);
+-      usb_set_intfdata(interface, NULL);
+       if (!ati_remote) {
+               dev_warn(&interface->dev, "%s - null device?\n", __func__);
+               return;
+       }
++      rc_unregister_device(ati_remote->rdev);
++      usb_set_intfdata(interface, NULL);
+       usb_kill_urb(ati_remote->irq_urb);
+       usb_kill_urb(ati_remote->out_urb);
+       if (ati_remote->idev)
+               input_unregister_device(ati_remote->idev);
+-      rc_unregister_device(ati_remote->rdev);
+       ati_remote_free_buffers(ati_remote);
++      rc_free_device(ati_remote->rdev);
+       kfree(ati_remote);
+ }
+diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
+index 11ee21a7db8f00..9140e4f28c9955 100644
+--- a/drivers/media/rc/ene_ir.c
++++ b/drivers/media/rc/ene_ir.c
+@@ -1093,7 +1093,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
+       release_region(dev->hw_io, ENE_IO_SIZE);
+ exit_unregister_device:
+       rc_unregister_device(rdev);
+-      rdev = NULL;
+ exit_free_dev_rdev:
+       rc_free_device(rdev);
+       kfree(dev);
+@@ -1113,6 +1112,7 @@ static void ene_remove(struct pnp_dev *pnp_dev)
+       ene_rx_restore_hw_buffer(dev);
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
++      rc_free_device(dev->rdev);
+       free_irq(dev->irq, dev);
+       release_region(dev->hw_io, ENE_IO_SIZE);
+       kfree(dev);
+diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
+index 3fb0968efd57d3..9b789097cdd4c3 100644
+--- a/drivers/media/rc/fintek-cir.c
++++ b/drivers/media/rc/fintek-cir.c
+@@ -568,6 +568,7 @@ static void fintek_remove(struct pnp_dev *pdev)
+       struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+       unsigned long flags;
++      rc_unregister_device(fintek->rdev);
+       spin_lock_irqsave(&fintek->fintek_lock, flags);
+       /* disable CIR */
+       fintek_disable_cir(fintek);
+@@ -580,7 +581,7 @@ static void fintek_remove(struct pnp_dev *pdev)
+       free_irq(fintek->cir_irq, fintek);
+       release_region(fintek->cir_addr, fintek->cir_port_len);
+-      rc_unregister_device(fintek->rdev);
++      rc_free_device(fintek->rdev);
+       kfree(fintek);
+ }
+diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c
+index f3616607d4f52b..8bf059b9a31b45 100644
+--- a/drivers/media/rc/igorplugusb.c
++++ b/drivers/media/rc/igorplugusb.c
+@@ -247,6 +247,7 @@ static void igorplugusb_disconnect(struct usb_interface *intf)
+       usb_set_intfdata(intf, NULL);
+       usb_unpoison_urb(ir->urb);
+       usb_free_urb(ir->urb);
++      rc_free_device(ir->rc);
+       kfree(ir->buf_in);
+       kfree(ir->request);
+ }
+diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
+index 8af94246e5916e..7bd6dd7254157a 100644
+--- a/drivers/media/rc/iguanair.c
++++ b/drivers/media/rc/iguanair.c
+@@ -500,6 +500,7 @@ static void iguanair_disconnect(struct usb_interface *intf)
+       usb_set_intfdata(intf, NULL);
+       usb_kill_urb(ir->urb_in);
+       usb_kill_urb(ir->urb_out);
++      rc_free_device(ir->rc);
+       usb_free_urb(ir->urb_in);
+       usb_free_urb(ir->urb_out);
+       usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
+diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
+index 5da7479c1793b1..07f41372976eaf 100644
+--- a/drivers/media/rc/img-ir/img-ir-hw.c
++++ b/drivers/media/rc/img-ir/img-ir-hw.c
+@@ -1117,9 +1117,10 @@ void img_ir_remove_hw(struct img_ir_priv *priv)
+       struct rc_dev *rdev = hw->rdev;
+       if (!rdev)
+               return;
++      rc_unregister_device(rdev);
+       img_ir_set_decoder(priv, NULL, 0);
+       hw->rdev = NULL;
+-      rc_unregister_device(rdev);
++      rc_free_device(rdev);
+ #ifdef CONFIG_COMMON_CLK
+       if (!IS_ERR(priv->clk))
+               clk_notifier_unregister(priv->clk, &hw->clk_nb);
+diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c
+index 8b0bdd9603b3c5..533d40dae54224 100644
+--- a/drivers/media/rc/img-ir/img-ir-raw.c
++++ b/drivers/media/rc/img-ir/img-ir-raw.c
+@@ -136,6 +136,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv)
+       if (!rdev)
+               return;
++      rc_unregister_device(rdev);
+       /* switch off and disable raw (edge) interrupts */
+       spin_lock_irq(&priv->lock);
+       raw->rdev = NULL;
+@@ -145,7 +146,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv)
+       img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
+       spin_unlock_irq(&priv->lock);
+-      rc_unregister_device(rdev);
++      rc_free_device(rdev);
+       del_timer_sync(&raw->timer);
+ }
+diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
+index bb4aabb08c06ef..69e118863aa67c 100644
+--- a/drivers/media/rc/imon.c
++++ b/drivers/media/rc/imon.c
+@@ -2546,9 +2546,10 @@ static void imon_disconnect(struct usb_interface *interface)
+       if (ifnum == 0) {
+               ictx->dev_present_intf0 = false;
++              rc_unregister_device(ictx->rdev);
+               usb_kill_urb(ictx->rx_urb_intf0);
+               input_unregister_device(ictx->idev);
+-              rc_unregister_device(ictx->rdev);
++              rc_free_device(ictx->rdev);
+               if (ictx->display_supported) {
+                       if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
+                               usb_deregister_dev(interface, &imon_lcd_class);
+diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
+index 0034f615b4660e..df347d2fb0ead3 100644
+--- a/drivers/media/rc/ir-hix5hd2.c
++++ b/drivers/media/rc/ir-hix5hd2.c
+@@ -331,7 +331,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
+ regerr:
+       rc_unregister_device(rdev);
+-      rdev = NULL;
+ clkerr:
+       clk_disable_unprepare(priv->clock);
+ err:
+@@ -346,6 +345,7 @@ static void hix5hd2_ir_remove(struct platform_device *pdev)
+       clk_disable_unprepare(priv->clock);
+       rc_unregister_device(priv->rdev);
++      rc_free_device(priv->rdev);
+ }
+ #ifdef CONFIG_PM_SLEEP
+diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c
+index 69e630d85262f6..490de29b7ffbef 100644
+--- a/drivers/media/rc/ir_toy.c
++++ b/drivers/media/rc/ir_toy.c
+@@ -536,6 +536,7 @@ static void irtoy_disconnect(struct usb_interface *intf)
+       usb_free_urb(ir->urb_out);
+       usb_kill_urb(ir->urb_in);
+       usb_free_urb(ir->urb_in);
++      rc_free_device(ir->rc);
+       kfree(ir->in);
+       kfree(ir->out);
+       kfree(ir);
+diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
+index fcfadd7ea31cf3..c584dc6bfd7b48 100644
+--- a/drivers/media/rc/ite-cir.c
++++ b/drivers/media/rc/ite-cir.c
+@@ -1415,7 +1415,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
+       release_region(itdev->cir_addr, itdev->params->io_region_size);
+ exit_unregister_device:
+       rc_unregister_device(rdev);
+-      rdev = NULL;
+ exit_free_dev_rdev:
+       rc_free_device(rdev);
+       kfree(itdev);
+@@ -1440,6 +1439,7 @@ static void ite_remove(struct pnp_dev *pdev)
+       release_region(dev->cir_addr, dev->params->io_region_size);
+       rc_unregister_device(dev->rdev);
++      rc_free_device(dev->rdev);
+       kfree(dev);
+ }
+diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
+index c76ba24c1f5595..4115a46929c473 100644
+--- a/drivers/media/rc/mceusb.c
++++ b/drivers/media/rc/mceusb.c
+@@ -1858,6 +1858,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf)
+       usb_free_urb(ir->urb_in);
+       usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
+       usb_put_dev(dev);
++      rc_free_device(ir->rc);
+       kfree(ir);
+ }
+diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
+index 16e33d7eaaa2d8..dd70f8ad526642 100644
+--- a/drivers/media/rc/rc-ir-raw.c
++++ b/drivers/media/rc/rc-ir-raw.c
+@@ -647,9 +647,6 @@ int ir_raw_event_register(struct rc_dev *dev)
+ void ir_raw_event_free(struct rc_dev *dev)
+ {
+-      if (!dev)
+-              return;
+-
+       kfree(dev->raw);
+       dev->raw = NULL;
+ }
+@@ -673,8 +670,6 @@ void ir_raw_event_unregister(struct rc_dev *dev)
+       lirc_bpf_free(dev);
+-      ir_raw_event_free(dev);
+-
+       /*
+        * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so
+        * ensure that the raw member is null on unlock; this is how
+diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
+index b356041c5c00e7..79073aaabd9175 100644
+--- a/drivers/media/rc/rc-loopback.c
++++ b/drivers/media/rc/rc-loopback.c
+@@ -264,6 +264,7 @@ static int __init loop_init(void)
+ static void __exit loop_exit(void)
+ {
+       rc_unregister_device(loopdev.dev);
++      rc_free_device(loopdev.dev);
+ }
+ module_init(loop_init);
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index 6bdad6341844f5..10464f7942304e 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -1611,6 +1611,7 @@ static void rc_dev_release(struct device *device)
+ {
+       struct rc_dev *dev = to_rc_dev(device);
++      ir_raw_event_free(dev);
+       kfree(dev);
+ }
+@@ -1773,7 +1774,6 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev,
+       }
+       rc->dev.parent = dev;
+-      rc->managed_alloc = true;
+       *dr = rc;
+       devres_add(dev, dr);
+@@ -2042,11 +2042,7 @@ void rc_unregister_device(struct rc_dev *dev)
+       device_del(&dev->dev);
+       ida_free(&rc_ida, dev->minor);
+-
+-      if (!dev->managed_alloc)
+-              rc_free_device(dev);
+ }
+-
+ EXPORT_SYMBOL_GPL(rc_unregister_device);
+ /*
+diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
+index 880981e1c507e1..d47f6e983ec67d 100644
+--- a/drivers/media/rc/redrat3.c
++++ b/drivers/media/rc/redrat3.c
+@@ -1133,11 +1133,13 @@ static void redrat3_dev_disconnect(struct usb_interface *intf)
+ {
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct redrat3_dev *rr3 = usb_get_intfdata(intf);
++      struct rc_dev *rc = rr3->rc;
+       usb_set_intfdata(intf, NULL);
+-      rc_unregister_device(rr3->rc);
++      rc_unregister_device(rc);
+       led_classdev_unregister(&rr3->led);
+       redrat3_delete(rr3, udev);
++      rc_free_device(rc);
+ }
+ static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
+diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
+index 8a9f31e15b0c01..bac1ac4e0f3a22 100644
+--- a/drivers/media/rc/st_rc.c
++++ b/drivers/media/rc/st_rc.c
+@@ -202,6 +202,7 @@ static void st_rc_remove(struct platform_device *pdev)
+       device_init_wakeup(&pdev->dev, false);
+       clk_disable_unprepare(rc_dev->sys_clock);
+       rc_unregister_device(rc_dev->rdev);
++      rc_free_device(rc_dev->rdev);
+ }
+ static int st_rc_open(struct rc_dev *rdev)
+@@ -333,7 +334,6 @@ static int st_rc_probe(struct platform_device *pdev)
+       return ret;
+ rcerr:
+       rc_unregister_device(rdev);
+-      rdev = NULL;
+ clkerr:
+       clk_disable_unprepare(rc_dev->sys_clock);
+ err:
+diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
+index 8e9b156e430022..8c85b9f30a3a96 100644
+--- a/drivers/media/rc/streamzap.c
++++ b/drivers/media/rc/streamzap.c
+@@ -392,15 +392,16 @@ static void streamzap_disconnect(struct usb_interface *interface)
+       struct streamzap_ir *sz = usb_get_intfdata(interface);
+       struct usb_device *usbdev = interface_to_usbdev(interface);
+-      usb_set_intfdata(interface, NULL);
+-
+       if (!sz)
+               return;
+-      usb_kill_urb(sz->urb_in);
+       rc_unregister_device(sz->rdev);
++      usb_set_intfdata(interface, NULL);
++
++      usb_kill_urb(sz->urb_in);
+       usb_free_urb(sz->urb_in);
+       usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
++      rc_free_device(sz->rdev);
+       kfree(sz);
+ }
+diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
+index bf58c965ead8cf..e2477e724637fa 100644
+--- a/drivers/media/rc/sunxi-cir.c
++++ b/drivers/media/rc/sunxi-cir.c
+@@ -370,6 +370,7 @@ static void sunxi_ir_remove(struct platform_device *pdev)
+       struct sunxi_ir *ir = platform_get_drvdata(pdev);
+       rc_unregister_device(ir->rc);
++      rc_free_device(ir->rc);
+       sunxi_ir_hw_exit(&pdev->dev);
+ }
+diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
+index dde446a95eaa93..a670d4b008cb0d 100644
+--- a/drivers/media/rc/ttusbir.c
++++ b/drivers/media/rc/ttusbir.c
+@@ -336,7 +336,6 @@ static int ttusbir_probe(struct usb_interface *intf,
+       return 0;
+ out3:
+       rc_unregister_device(rc);
+-      rc = NULL;
+ out2:
+       led_classdev_unregister(&tt->led);
+ out:
+@@ -378,6 +377,7 @@ static void ttusbir_disconnect(struct usb_interface *intf)
+       usb_kill_urb(tt->bulk_urb);
+       usb_free_urb(tt->bulk_urb);
+       kfree(tt->bulk_buffer);
++      rc_free_device(tt->rc);
+       usb_set_intfdata(intf, NULL);
+       kfree(tt);
+ }
+diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
+index 25884a79985c8a..14d8b58e283980 100644
+--- a/drivers/media/rc/winbond-cir.c
++++ b/drivers/media/rc/winbond-cir.c
+@@ -1132,7 +1132,6 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
+       release_region(data->wbase, WAKEUP_IOMEM_LEN);
+ exit_unregister_device:
+       rc_unregister_device(data->dev);
+-      data->dev = NULL;
+ exit_free_rc:
+       rc_free_device(data->dev);
+ exit_unregister_led:
+@@ -1163,6 +1162,7 @@ wbcir_remove(struct pnp_dev *device)
+       wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+       rc_unregister_device(data->dev);
++      rc_free_device(data->dev);
+       led_classdev_unregister(&data->led);
+diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c
+index 0c9c855ced729c..80b7c247932a8f 100644
+--- a/drivers/media/rc/xbox_remote.c
++++ b/drivers/media/rc/xbox_remote.c
+@@ -283,14 +283,15 @@ static void xbox_remote_disconnect(struct usb_interface *interface)
+       struct xbox_remote *xbox_remote;
+       xbox_remote = usb_get_intfdata(interface);
+-      usb_set_intfdata(interface, NULL);
+       if (!xbox_remote) {
+               dev_warn(&interface->dev, "%s - null device?\n", __func__);
+               return;
+       }
+-      usb_kill_urb(xbox_remote->irq_urb);
+       rc_unregister_device(xbox_remote->rdev);
++      usb_set_intfdata(interface, NULL);
++      usb_kill_urb(xbox_remote->irq_urb);
++      rc_free_device(xbox_remote->rdev);
+       usb_free_urb(xbox_remote->irq_urb);
+       kfree(xbox_remote->inbuf);
+       kfree(xbox_remote);
+diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c
+index 3d3368202cd018..283ad2c6288cd5 100644
+--- a/drivers/media/usb/au0828/au0828-input.c
++++ b/drivers/media/usb/au0828/au0828-input.c
+@@ -357,6 +357,7 @@ void au0828_rc_unregister(struct au0828_dev *dev)
+               return;
+       rc_unregister_device(ir->rc);
++      rc_free_device(ir->rc);
+       /* done */
+       kfree(ir);
+diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+index f1c79f351ec8de..17e8961179d14b 100644
+--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
++++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+@@ -187,6 +187,7 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d)
+       if (d->rc_dev) {
+               cancel_delayed_work_sync(&d->rc_query_work);
+               rc_unregister_device(d->rc_dev);
++              rc_free_device(d->rc_dev);
+               d->rc_dev = NULL;
+       }
+diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+index 65e2c9e2cdc99f..6dc11718dfb985 100644
+--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c
++++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+@@ -347,10 +347,12 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d)
+ {
+       if (d->state & DVB_USB_STATE_REMOTE) {
+               cancel_delayed_work_sync(&d->rc_query_work);
+-              if (d->props.rc.mode == DVB_RC_LEGACY)
++              if (d->props.rc.mode == DVB_RC_LEGACY) {
+                       input_unregister_device(d->input_dev);
+-              else
++              } else {
+                       rc_unregister_device(d->rc_dev);
++                      rc_free_device(d->rc_dev);
++              }
+       }
+       d->state &= ~DVB_USB_STATE_REMOTE;
+       return 0;
+diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
+index 5f3b00869bdbc9..26f333b5be7325 100644
+--- a/drivers/media/usb/em28xx/em28xx-input.c
++++ b/drivers/media/usb/em28xx/em28xx-input.c
+@@ -853,6 +853,7 @@ static int em28xx_ir_fini(struct em28xx *dev)
+               goto ref_put;
+       rc_unregister_device(ir->rc);
++      rc_free_device(ir->rc);
+       kfree(ir->i2c_client);
+diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c
+index a851ba328e4a3e..c0a32325e2cc98 100644
+--- a/drivers/staging/media/av7110/av7110_ir.c
++++ b/drivers/staging/media/av7110/av7110_ir.c
+@@ -152,6 +152,7 @@ int av7110_ir_init(struct av7110 *av7110)
+ void av7110_ir_exit(struct av7110 *av7110)
+ {
+       rc_unregister_device(av7110->ir.rcdev);
++      rc_free_device(av7110->ir.rcdev);
+ }
+ //MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
+diff --git a/include/media/rc-core.h b/include/media/rc-core.h
+index 803349599c272b..b487b0d5ffd1d0 100644
+--- a/include/media/rc-core.h
++++ b/include/media/rc-core.h
+@@ -81,7 +81,6 @@ struct lirc_fh {
+ /**
+  * struct rc_dev - represents a remote control device
+  * @dev: driver model's view of this device
+- * @managed_alloc: devm_rc_allocate_device was used to create rc_dev
+  * @sysfs_groups: sysfs attribute groups
+  * @device_name: name of the rc child device
+  * @input_phys: physical path to the input child device
+@@ -157,7 +156,6 @@ struct lirc_fh {
+  */
+ struct rc_dev {
+       struct device                   dev;
+-      bool                            managed_alloc;
+       const struct attribute_group    *sysfs_groups[5];
+       const char                      *device_name;
+       const char                      *input_phys;
+-- 
+2.53.0
+
diff --git a/queue-6.6/media-rc-ttusbir-fix-inverted-error-logic.patch b/queue-6.6/media-rc-ttusbir-fix-inverted-error-logic.patch
new file mode 100644 (file)
index 0000000..055c9e7
--- /dev/null
@@ -0,0 +1,38 @@
+From c4dab51a93d185c810698f405fe6816d9077e4f3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Apr 2026 23:03:09 +0200
+Subject: media: rc: ttusbir: fix inverted error logic
+
+From: Oliver Neukum <oneukum@suse.com>
+
+[ Upstream commit 646ebdd3105809d84ed04aa9e92e47e89cc44502 ]
+
+We have to report ENOMEM if no buffer is allocated.
+Typo dropped a "!". Restore it.
+
+Fixes: 50acaad3d202 ("media: rc: ttusbir: respect DMA coherency rules")
+Cc: stable@vger.kernel.org
+Signed-off-by: Oliver Neukum <oneukum@suse.com>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/media/rc/ttusbir.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
+index a670d4b008cb0d..3452b5aefd2848 100644
+--- a/drivers/media/rc/ttusbir.c
++++ b/drivers/media/rc/ttusbir.c
+@@ -191,7 +191,7 @@ static int ttusbir_probe(struct usb_interface *intf,
+       tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+       buffer = kzalloc(5, GFP_KERNEL);
+       rc = rc_allocate_device(RC_DRIVER_IR_RAW);
+-      if (!tt || !rc || buffer) {
++      if (!tt || !rc || !buffer) {
+               ret = -ENOMEM;
+               goto out;
+       }
+-- 
+2.53.0
+
diff --git a/queue-6.6/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch b/queue-6.6/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch
new file mode 100644 (file)
index 0000000..befa501
--- /dev/null
@@ -0,0 +1,67 @@
+From 295402a6f7657051ac4775609e6dcb5b7803f8e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 13:02:30 +0800
+Subject: mm/page_alloc: clear page->private in free_pages_prepare()
+
+From: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
+
+[ Upstream commit ac1ea219590c09572ed5992dc233bbf7bb70fef9 ]
+
+Several subsystems (slub, shmem, ttm, etc.) use page->private but don't
+clear it before freeing pages.  When these pages are later allocated as
+high-order pages and split via split_page(), tail pages retain stale
+page->private values.
+
+This causes a use-after-free in the swap subsystem.  The swap code uses
+page->private to track swap count continuations, assuming freshly
+allocated pages have page->private == 0.  When stale values are present,
+swap_count_continued() incorrectly assumes the continuation list is valid
+and iterates over uninitialized page->lru containing LIST_POISON values,
+causing a crash:
+
+  KASAN: maybe wild-memory-access in range [0xdead000000000100-0xdead000000000107]
+  RIP: 0010:__do_sys_swapoff+0x1151/0x1860
+
+Fix this by clearing page->private in free_pages_prepare(), ensuring all
+freed pages have clean state regardless of previous use.
+
+Link: https://lkml.kernel.org/r/20260207173615.146159-1-mikhail.v.gavrilov@gmail.com
+Fixes: 3b8000ae185c ("mm/vmalloc: huge vmalloc backing pages should be split rather than compound")
+Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
+Suggested-by: Zi Yan <ziy@nvidia.com>
+Acked-by: Zi Yan <ziy@nvidia.com>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: Brendan Jackman <jackmanb@google.com>
+Cc: Chris Li <chrisl@kernel.org>
+Cc: Hugh Dickins <hughd@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Kairui Song <ryncsn@gmail.com>
+Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Nicholas Piggin <npiggin@gmail.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+[backport: context only]
+Signed-off-by: Li Wang <li.wang@windriver.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/page_alloc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/mm/page_alloc.c b/mm/page_alloc.c
+index b617fb364b15d1..ffdc71103c2ad2 100644
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -1178,6 +1178,7 @@ static __always_inline bool free_pages_prepare(struct page *page,
+       page_cpupid_reset_last(page);
+       page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP;
++      page->private = 0;
+       reset_page_owner(page, order);
+       page_table_check_free(page, order);
+-- 
+2.53.0
+
diff --git a/queue-6.6/net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch b/queue-6.6/net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch
new file mode 100644 (file)
index 0000000..4712da5
--- /dev/null
@@ -0,0 +1,146 @@
+From 3245660a79bf938f31ad7ec45fe4a97ff0500401 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 14:09:45 +0800
+Subject: net: af_key: zero aligned sockaddr tail in PF_KEY exports
+
+From: Zhengchuan Liang <zcliangcn@gmail.com>
+
+[ Upstream commit 426c355742f02cf743b347d9d7dbdc1bfbfa31ef ]
+
+PF_KEY export paths use `pfkey_sockaddr_size()` when reserving sockaddr
+payload space, so IPv6 addresses occupy 32 bytes on the wire. However,
+`pfkey_sockaddr_fill()` initializes only the first 28 bytes of
+`struct sockaddr_in6`, leaving the final 4 aligned bytes uninitialized.
+
+Not every PF_KEY message is affected. The state and policy dump builders
+already zero the whole message buffer before filling the sockaddr
+payloads. Keep the fix to the export paths that still append aligned
+sockaddr payloads with plain `skb_put()`:
+
+  - `SADB_ACQUIRE`
+  - `SADB_X_NAT_T_NEW_MAPPING`
+  - `SADB_X_MIGRATE`
+
+Fix those paths by clearing only the aligned sockaddr tail after
+`pfkey_sockaddr_fill()`.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Fixes: 08de61beab8a ("[PFKEYV2]: Extension for dynamic update of endpoint address(es)")
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Co-developed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Yuan Tan <yuantan098@gmail.com>
+Suggested-by: Xin Liu <bird@lzu.edu.cn>
+Tested-by: Xiao Liu <lx24@stu.ynu.edu.cn>
+Signed-off-by: Zhengchuan Liang <zcliangcn@gmail.com>
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Miles Wang <13621186580@139.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/key/af_key.c | 52 +++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 34 insertions(+), 18 deletions(-)
+
+diff --git a/net/key/af_key.c b/net/key/af_key.c
+index 4849407da86d6e..486c9a2e1f52f3 100644
+--- a/net/key/af_key.c
++++ b/net/key/af_key.c
+@@ -757,6 +757,22 @@ static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port
+       return 0;
+ }
++static unsigned int pfkey_sockaddr_fill_zero_tail(const xfrm_address_t *xaddr,
++                                                __be16 port,
++                                                struct sockaddr *sa,
++                                                unsigned short family)
++{
++      unsigned int prefixlen;
++      int sockaddr_len = pfkey_sockaddr_len(family);
++      int sockaddr_size = pfkey_sockaddr_size(family);
++
++      prefixlen = pfkey_sockaddr_fill(xaddr, port, sa, family);
++      if (sockaddr_size > sockaddr_len)
++              memset((u8 *)sa + sockaddr_len, 0, sockaddr_size - sockaddr_len);
++
++      return prefixlen;
++}
++
+ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
+                                             int add_keys, int hsc)
+ {
+@@ -3205,9 +3221,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
+       addr->sadb_address_proto = 0;
+       addr->sadb_address_reserved = 0;
+       addr->sadb_address_prefixlen =
+-              pfkey_sockaddr_fill(&x->props.saddr, 0,
+-                                  (struct sockaddr *) (addr + 1),
+-                                  x->props.family);
++              pfkey_sockaddr_fill_zero_tail(&x->props.saddr, 0,
++                                            (struct sockaddr *)(addr + 1),
++                                            x->props.family);
+       if (!addr->sadb_address_prefixlen)
+               BUG();
+@@ -3220,9 +3236,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
+       addr->sadb_address_proto = 0;
+       addr->sadb_address_reserved = 0;
+       addr->sadb_address_prefixlen =
+-              pfkey_sockaddr_fill(&x->id.daddr, 0,
+-                                  (struct sockaddr *) (addr + 1),
+-                                  x->props.family);
++              pfkey_sockaddr_fill_zero_tail(&x->id.daddr, 0,
++                                            (struct sockaddr *)(addr + 1),
++                                            x->props.family);
+       if (!addr->sadb_address_prefixlen)
+               BUG();
+@@ -3420,9 +3436,9 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
+       addr->sadb_address_proto = 0;
+       addr->sadb_address_reserved = 0;
+       addr->sadb_address_prefixlen =
+-              pfkey_sockaddr_fill(&x->props.saddr, 0,
+-                                  (struct sockaddr *) (addr + 1),
+-                                  x->props.family);
++              pfkey_sockaddr_fill_zero_tail(&x->props.saddr, 0,
++                                            (struct sockaddr *)(addr + 1),
++                                            x->props.family);
+       if (!addr->sadb_address_prefixlen)
+               BUG();
+@@ -3442,9 +3458,9 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
+       addr->sadb_address_proto = 0;
+       addr->sadb_address_reserved = 0;
+       addr->sadb_address_prefixlen =
+-              pfkey_sockaddr_fill(ipaddr, 0,
+-                                  (struct sockaddr *) (addr + 1),
+-                                  x->props.family);
++              pfkey_sockaddr_fill_zero_tail(ipaddr, 0,
++                                            (struct sockaddr *)(addr + 1),
++                                            x->props.family);
+       if (!addr->sadb_address_prefixlen)
+               BUG();
+@@ -3473,15 +3489,15 @@ static int set_sadb_address(struct sk_buff *skb, int sasize, int type,
+       switch (type) {
+       case SADB_EXT_ADDRESS_SRC:
+               addr->sadb_address_prefixlen = sel->prefixlen_s;
+-              pfkey_sockaddr_fill(&sel->saddr, 0,
+-                                  (struct sockaddr *)(addr + 1),
+-                                  sel->family);
++              pfkey_sockaddr_fill_zero_tail(&sel->saddr, 0,
++                                            (struct sockaddr *)(addr + 1),
++                                            sel->family);
+               break;
+       case SADB_EXT_ADDRESS_DST:
+               addr->sadb_address_prefixlen = sel->prefixlen_d;
+-              pfkey_sockaddr_fill(&sel->daddr, 0,
+-                                  (struct sockaddr *)(addr + 1),
+-                                  sel->family);
++              pfkey_sockaddr_fill_zero_tail(&sel->daddr, 0,
++                                            (struct sockaddr *)(addr + 1),
++                                            sel->family);
+               break;
+       default:
+               return -EINVAL;
+-- 
+2.53.0
+
diff --git a/queue-6.6/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch b/queue-6.6/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch
new file mode 100644 (file)
index 0000000..477080c
--- /dev/null
@@ -0,0 +1,64 @@
+From 8372446a216ced29b6cee5b08567fe4d3519f67f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 May 2026 23:06:58 -0700
+Subject: perf: Fix dangling cgroup pointer in cpuctx
+
+From: Yeoreum Yun <yeoreum.yun@arm.com>
+
+[ Upstream commit 3b7a34aebbdf2a4b7295205bf0c654294283ec82 ]
+
+Commit a3c3c6667("perf/core: Fix child_total_time_enabled accounting
+bug at task exit") moves the event->state update to before
+list_del_event(). This makes the event->state test in list_del_event()
+always false; never calling perf_cgroup_event_disable().
+
+As a result, cpuctx->cgrp won't be cleared properly; causing havoc.
+
+Fixes: a3c3c6667("perf/core: Fix child_total_time_enabled accounting bug at task exit")
+Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Tested-by: David Wang <00107082@163.com>
+Link: https://lore.kernel.org/all/aD2TspKH%2F7yvfYoO@e129823.arm.com/
+Signed-off-by: Ian Klatzco <iklatzco@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/events/core.c | 16 ++++------------
+ 1 file changed, 4 insertions(+), 12 deletions(-)
+
+diff --git a/kernel/events/core.c b/kernel/events/core.c
+index eba5eb6fcb8762..a4187dea6402a7 100644
+--- a/kernel/events/core.c
++++ b/kernel/events/core.c
+@@ -2056,18 +2056,6 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx)
+       if (event->group_leader == event)
+               del_event_from_groups(event, ctx);
+-      /*
+-       * If event was in error state, then keep it
+-       * that way, otherwise bogus counts will be
+-       * returned on read(). The only way to get out
+-       * of error state is by explicit re-enabling
+-       * of the event
+-       */
+-      if (event->state > PERF_EVENT_STATE_OFF) {
+-              perf_cgroup_event_disable(event, ctx);
+-              perf_event_set_state(event, PERF_EVENT_STATE_OFF);
+-      }
+-
+       ctx->generation++;
+       event->pmu_ctx->nr_events--;
+ }
+@@ -2401,6 +2389,10 @@ __perf_remove_from_context(struct perf_event *event,
+               state = PERF_EVENT_STATE_DEAD;
+       }
+       event_sched_out(event, ctx);
++
++      if (event->state > PERF_EVENT_STATE_OFF)
++              perf_cgroup_event_disable(event, ctx);
++
+       perf_event_set_state(event, min(event->state, state));
+       if (flags & DETACH_GROUP)
+               perf_group_detach(event);
+-- 
+2.53.0
+
index f28fade55c48dad6694c40316d92031ccb975ea8..b89bedefbf45e2a2979dca9461158a5ebb8747f1 100644 (file)
@@ -46,3 +46,27 @@ sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch
 ipv6-fix-possible-infinite-loop-in-rt6_fill_node.patch
 ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch
 net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch
+perf-fix-dangling-cgroup-pointer-in-cpuctx.patch
+batman-adv-v-stop-ogmv2-on-disabled-interface.patch
+batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch
+batman-adv-tt-reject-oversized-local-tvlv-buffers.patch
+batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch
+batman-adv-tvlv-reject-oversized-tvlv-packets.patch
+batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch
+batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch
+net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch
+batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch
+batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
+batman-adv-tt-avoid-empty-vlan-responses.patch
+batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch
+mm-page_alloc-clear-page-private-in-free_pages_prepa.patch
+media-rc-fix-race-between-unregister-and-urb-irq-cal.patch
+media-rc-ttusbir-fix-inverted-error-logic.patch
+drm-fbdev-helper-set-and-clear-vga-switcheroo-client.patch
+drm-fbcon-vga_switcheroo-avoid-race-condition-in-fbc.patch
+inet-frags-add-inet_frag_queue_flush.patch
+inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch
+drm-i915-psr-add-defininitions-for-intel_wa_register.patch
+drm-i915-psr-read-intel-dpcd-workaround-register.patch
+drm-dp-add-edp-1.5-bit-definition.patch
+drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
diff --git a/queue-7.0/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-7.0/drm-i915-psr-add-defininitions-for-intel_wa_register.patch
new file mode 100644 (file)
index 0000000..37537a5
--- /dev/null
@@ -0,0 +1,73 @@
+From 8d7a860d1781bcc6bc2ff36ea8c3c19078ae5bf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 12:38:35 +0300
+Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD
+ register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream.
+
+EDP specification says:
+
+"If either VSC SDP is unable to be transmitted 100 ns before the SU region,
+the Source device may optionally transmit the VSC SDP during the prior
+video scan line’s HBlank period There is a Intel specific drm dp register
+currently containing bits related how TCON can support PSR2 with SDP on
+prior line."
+
+Unfortunately many panels are having problems in implementing this. So
+there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to
+figure out if this is properly implemented on a panel or if panel doesn't
+require that 100 ns delay before the SU region. Here are the definitions in
+this custom DPCD address:
+
+0 = Panel doesn't support SDP on prior line
+1 = Panel supports SDP on prior line
+2 = Panel doesn't have 100ns requirement
+3 = Reserved
+
+Add definitions for this new register and it's values into new header
+intel_dpcd.h.
+
+v2: add INTEL_DPCD_ prefix to definitions
+
+Bspec: 74741
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com
+(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+ create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h
+
+diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h
+new file mode 100644
+index 00000000000000..4aea5326f2ed48
+--- /dev/null
++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: MIT */
++/*
++ * Copyright © 2026 Intel Corporation
++ */
++
++#ifndef __INTEL_DPCD_H__
++#define __INTEL_DPCD_H__
++
++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS                                     0x3f0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK        REG_GENMASK(1, 0)
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1                  0
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE           1
++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE                2
++
++#endif /* __INTEL_DPCD_H__ */
+-- 
+2.53.0
+
diff --git a/queue-7.0/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-7.0/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch
new file mode 100644 (file)
index 0000000..6d09554
--- /dev/null
@@ -0,0 +1,92 @@
+From 6dddf4c53e8a54295d7b483c05bd72f8f196d7d3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 12:38:37 +0300
+Subject: drm/i915/psr: Apply Intel DPCD workaround when SDP on prior line used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit 4703049f768fc1c1caac754134118bee1a3af189 upstream.
+
+There is Intel specific workaround DPCD address containing workaround for
+case where SDP is on prior line. Apply this workaround according to values
+in the offset.
+
+Fixes: 61e887329e33 ("drm/i915/xelpd: Handle PSR2 SDP indication in the prior scanline")
+Cc: <stable@vger.kernel.org> # v5.15+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com
+(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_psr.c | 35 +++++++++++++++++++++---
+ 1 file changed, 31 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index aa2ef49afa67a9..6709c434beb990 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -1357,9 +1357,35 @@ static bool psr2_granularity_check(struct intel_crtc_state *crtc_state,
+       return true;
+ }
+-static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp,
+-                                                      struct intel_crtc_state *crtc_state)
++static bool apply_scanline_indication_wa(struct intel_crtc_state *crtc_state,
++                                       struct intel_connector *connector)
+ {
++      struct intel_dp *intel_dp = intel_attached_dp(connector);
++      u8 early_scanline_support = connector->dp.psr_caps.intel_wa_dpcd &
++              INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK;
++
++      if (intel_dp->edp_dpcd[0] >= DP_EDP_15)
++              return true;
++
++      switch (early_scanline_support) {
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return false;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE:
++              return true;
++      case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE:
++              crtc_state->req_psr2_sdp_prior_scanline = false;
++              return true;
++      default:
++              MISSING_CASE(early_scanline_support);
++              return false;
++      }
++}
++
++static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_crtc_state *crtc_state,
++                                                      struct intel_connector *connector)
++{
++      struct intel_dp *intel_dp = intel_attached_dp(connector);
+       struct intel_display *display = to_intel_display(intel_dp);
+       const struct drm_display_mode *adjusted_mode = &crtc_state->uapi.adjusted_mode;
+       u32 hblank_total, hblank_ns, req_ns;
+@@ -1378,7 +1404,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d
+               return false;
+       crtc_state->req_psr2_sdp_prior_scanline = true;
+-      return true;
++
++      return apply_scanline_indication_wa(crtc_state, connector);
+ }
+ static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp,
+@@ -1660,7 +1687,7 @@ static bool intel_sel_update_config_valid(struct intel_crtc_state *crtc_state,
+                                                                     conn_state))
+               goto unsupported;
+-      if (!_compute_psr2_sdp_prior_scanline_indication(intel_dp, crtc_state)) {
++      if (!_compute_psr2_sdp_prior_scanline_indication(crtc_state, connector)) {
+               drm_dbg_kms(display->drm,
+                           "Selective update not enabled, SDP indication do not fit in hblank\n");
+               goto unsupported;
+-- 
+2.53.0
+
diff --git a/queue-7.0/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-7.0/drm-i915-psr-read-intel-dpcd-workaround-register.patch
new file mode 100644 (file)
index 0000000..9478abc
--- /dev/null
@@ -0,0 +1,70 @@
+From baa645c232de6d5e84293966ab6c28cb861f8fa2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 May 2026 12:38:36 +0300
+Subject: drm/i915/psr: Read Intel DPCD workaround register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream.
+
+Read Intel DPCD workaround register and store it into
+intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains
+only PSR workaround for PSR2 SDP on prior scanline implementation.
+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com
+(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/i915/display/intel_display_types.h | 1 +
+ drivers/gpu/drm/i915/display/intel_psr.c           | 9 ++++++++-
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
+index ced0e5a5989b85..3596ed0ff151bf 100644
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -583,6 +583,7 @@ struct intel_connector {
+               struct {
+                       u8 dpcd[EDP_PSR_RECEIVER_CAP_SIZE];
++                      u8 intel_wa_dpcd;
+                       bool support;
+                       bool su_support;
+diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
+index 8a7075c4a2480f..aa2ef49afa67a9 100644
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -43,6 +43,7 @@
+ #include "intel_display_utils.h"
+ #include "intel_dmc.h"
+ #include "intel_dp.h"
++#include "intel_dpcd.h"
+ #include "intel_dp_aux.h"
+ #include "intel_dsb.h"
+ #include "intel_frontbuffer.h"
+@@ -708,8 +709,14 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *co
+                           connector->dp.psr_caps.su_support ? "" : "not ");
+       }
+-      if (connector->dp.psr_caps.su_support)
++      if (connector->dp.psr_caps.su_support) {
++              ret = drm_dp_dpcd_read_byte(&intel_dp->aux,
++                                          INTEL_DPCD_INTEL_WA_REGISTER_CAPS,
++                                          &connector->dp.psr_caps.intel_wa_dpcd);
++              if (ret < 0)
++                      return;
+               _psr_compute_su_granularity(intel_dp, connector);
++      }
+ }
+ void intel_psr_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector)
+-- 
+2.53.0
+
diff --git a/queue-7.0/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch b/queue-7.0/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch
new file mode 100644 (file)
index 0000000..bb41a0b
--- /dev/null
@@ -0,0 +1,100 @@
+From 932b19138ce89cb0bb2f4e94f26f022114d0a2bd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 26 May 2026 20:50:43 +0000
+Subject: Revert "x86/fpu: Refine and simplify the magic number check during
+ signal return"
+
+From: Andrei Vagin <avagin@google.com>
+
+[ Upstream commit 44eeff9bc467bc7d1fec34fc3f6001f385fe462c ]
+
+This reverts
+
+  dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return").
+
+The aforementioned commit broke applications that construct signal frames in
+userspace (such as CRIU and gVisor) if the frame's xstate size is smaller than
+the kernel's fpstate->user_size.
+
+Furthermore, this introduces a critical issue for checkpoint/restore tools
+like CRIU. If a process is checkpointed while inside a signal handler, its
+stack contains a signal frame formatted according to the source host's xstate
+capabilities.
+
+If that process is later restored on a destination host with larger xstate
+capabilities (e.g., a newer CPU with more features enabled, resulting in
+a larger fpstate->user_size), the kernel will look for FP_XSTATE_MAGIC2 at the
+destination host's larger user_size offset instead of the offset encoded in
+the frame's fx_sw->xstate_size.
+
+This causes the magic2 check to fail, forcing sigreturn to silently fall back
+to "FX-only" mode. Upon return from the signal handler, the process's extended
+state is reset to initial values instead of being restored, leading to silent
+data corruption.
+
+The aforementioned commit cited
+
+  d877550eaf2d ("x86/fpu: Stop relying on userspace for info to fault in xsave buffer")
+
+as justification to stop relying on userspace for the magic number check.
+
+However, these two changes are fundamentally different. The last one only
+changed how much memory the kernel ensures is paged-in before running XRSTOR
+to prevent an infinite loop. It did not change the signal frame format or how
+the layout is validated.
+
+Reverting this change restores the use of fx_sw->xstate_size for
+locating magic2 and restores the necessary sanity checks, ensuring that
+the signal frame remains self-describing and portable.
+
+  [ bp: Massage commit message. ]
+
+Fixes: dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return")
+Signed-off-by: Andrei Vagin <avagin@google.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Acked-by: Chang S. Bae <chang.seok.bae@intel.com>
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/all/20260429000623.3356606-1-avagin@google.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/fpu/signal.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
+index c3ec2512f2bbe4..20b638c507ca2d 100644
+--- a/arch/x86/kernel/fpu/signal.c
++++ b/arch/x86/kernel/fpu/signal.c
+@@ -27,14 +27,19 @@
+ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf,
+                                           struct _fpx_sw_bytes *fx_sw)
+ {
++      int min_xstate_size = sizeof(struct fxregs_state) +
++                            sizeof(struct xstate_header);
+       void __user *fpstate = fxbuf;
+       unsigned int magic2;
+       if (__copy_from_user(fx_sw, &fxbuf->sw_reserved[0], sizeof(*fx_sw)))
+               return false;
+-      /* Check for the first magic field */
+-      if (fx_sw->magic1 != FP_XSTATE_MAGIC1)
++      /* Check for the first magic field and other error scenarios. */
++      if (fx_sw->magic1 != FP_XSTATE_MAGIC1 ||
++          fx_sw->xstate_size < min_xstate_size ||
++          fx_sw->xstate_size > x86_task_fpu(current)->fpstate->user_size ||
++          fx_sw->xstate_size > fx_sw->extended_size)
+               goto setfx;
+       /*
+@@ -43,7 +48,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf,
+        * fpstate layout with out copying the extended state information
+        * in the memory layout.
+        */
+-      if (__get_user(magic2, (__u32 __user *)(fpstate + x86_task_fpu(current)->fpstate->user_size)))
++      if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size)))
+               return false;
+       if (likely(magic2 == FP_XSTATE_MAGIC2))
+-- 
+2.53.0
+
index 00227d5d0b964bb5b19319d22579bdb18fdd7e5a..2a7f2629da6bf94735aadefe68a22e3d39011e91 100644 (file)
@@ -110,3 +110,7 @@ ipv6-fix-possible-infinite-loop-in-rt6_fill_node.patch
 ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch
 net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch
 revert-ipv6-preserve-insertion-order-for-same-scope-.patch
+revert-x86-fpu-refine-and-simplify-the-magic-number-.patch
+drm-i915-psr-add-defininitions-for-intel_wa_register.patch
+drm-i915-psr-read-intel-dpcd-workaround-register.patch
+drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch