+++ /dev/null
-From stable+bounces-289595-greg=kroah.com@vger.kernel.org Mon Jul 27 17:52:10 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 27 Jul 2026 11:41:01 -0400
-Subject: bpf: Refactor {acquire,release}_reference_state
-To: stable@vger.kernel.org
-Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>, Eduard Zingerman <eddyz87@gmail.com>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260727154103.1515347-1-sashal@kernel.org>
-
-From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
-
-[ Upstream commit 769b0f1c821455ab29baf42491e1ea1d726451fa ]
-
-In preparation for introducing support for more reference types which
-have to add and remove reference state, refactor the
-acquire_reference_state and release_reference_state functions to share
-common logic.
-
-The acquire_reference_state function simply handles growing the acquired
-refs and returning the pointer to the new uninitialized element, which
-can be filled in by the caller.
-
-The release_reference_state function simply erases a reference state
-entry in the acquired_refs array and shrinks it. The callers are
-responsible for finding the suitable element by matching on various
-fields of the reference state and requesting deletion through this
-function. It is not supposed to be called directly.
-
-Existing callers of release_reference_state were using it to find and
-remove state for a given ref_obj_id without scrubbing the associated
-registers in the verifier state. Introduce release_reference_nomark to
-provide this functionality and convert callers. We now use this new
-release_reference_nomark function within release_reference as well.
-It needs to operate on a verifier state instead of taking verifier env
-as mark_ptr_or_null_regs requires operating on verifier state of the
-two branches of a NULL condition check, therefore env->cur_state cannot
-be used directly.
-
-Acked-by: Eduard Zingerman <eddyz87@gmail.com>
-Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
-Link: https://lore.kernel.org/r/20241204030400.208005-3-memxor@gmail.com
-Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-Stable-dep-of: 5e0b273e0a62 ("bpf: Reset register bounds before narrowing retval range in check_mem_access()")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- kernel/bpf/verifier.c | 28 +++++++++++++++-------------
- 1 file changed, 15 insertions(+), 13 deletions(-)
-
---- a/kernel/bpf/verifier.c
-+++ b/kernel/bpf/verifier.c
-@@ -7207,11 +7207,12 @@ static int check_mem_access(struct bpf_v
- if (!err && value_regno >= 0 && (t == BPF_READ || rdonly_mem))
- mark_reg_unknown(env, regs, value_regno);
- } else if (reg->type == PTR_TO_CTX) {
-- bool is_retval = false;
-+ struct bpf_insn_access_aux info = {
-+ .reg_type = SCALAR_VALUE,
-+ .is_ldsx = is_ldsx,
-+ .log = &env->log,
-+ };
- struct bpf_retval_range range;
-- enum bpf_reg_type reg_type = SCALAR_VALUE;
-- struct btf *btf = NULL;
-- u32 btf_id = 0;
-
- if (t == BPF_WRITE && value_regno >= 0 &&
- is_pointer_value(env, value_regno)) {
-@@ -7223,8 +7224,9 @@ static int check_mem_access(struct bpf_v
- if (err < 0)
- return err;
-
-- err = check_ctx_access(env, insn_idx, off, size, t, ®_type, &btf,
-- &btf_id, &is_retval, is_ldsx);
-+ err = check_ctx_access(env, insn_idx, off, size, t, &info.reg_type,
-+ &info.btf, &info.btf_id, &info.is_retval,
-+ info.is_ldsx);
- if (err)
- verbose_linfo(env, insn_idx, "; ");
- if (!err && t == BPF_READ && value_regno >= 0) {
-@@ -7232,8 +7234,8 @@ static int check_mem_access(struct bpf_v
- * PTR_TO_PACKET[_META,_END]. In the latter
- * case, we know the offset is zero.
- */
-- if (reg_type == SCALAR_VALUE) {
-- if (is_retval && get_func_retval_range(env->prog, &range)) {
-+ if (info.reg_type == SCALAR_VALUE) {
-+ if (info.is_retval && get_func_retval_range(env->prog, &range)) {
- err = __mark_reg_s32_range(env, regs, value_regno,
- range.minval, range.maxval);
- if (err)
-@@ -7244,7 +7246,7 @@ static int check_mem_access(struct bpf_v
- } else {
- mark_reg_known_zero(env, regs,
- value_regno);
-- if (type_may_be_null(reg_type))
-+ if (type_may_be_null(info.reg_type))
- regs[value_regno].id = ++env->id_gen;
- /* A load of ctx field could have different
- * actual load size with the one encoded in the
-@@ -7252,12 +7254,12 @@ static int check_mem_access(struct bpf_v
- * a sub-register.
- */
- regs[value_regno].subreg_def = DEF_NOT_SUBREG;
-- if (base_type(reg_type) == PTR_TO_BTF_ID) {
-- regs[value_regno].btf = btf;
-- regs[value_regno].btf_id = btf_id;
-+ if (base_type(info.reg_type) == PTR_TO_BTF_ID) {
-+ regs[value_regno].btf = info.btf;
-+ regs[value_regno].btf_id = info.btf_id;
- }
- }
-- regs[value_regno].type = reg_type;
-+ regs[value_regno].type = info.reg_type;
- }
-
- } else if (reg->type == PTR_TO_STACK) {
+++ /dev/null
-From stable+bounces-289596-greg=kroah.com@vger.kernel.org Mon Jul 27 17:49:26 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 27 Jul 2026 11:41:02 -0400
-Subject: bpf: Refactor check_ctx_access()
-To: stable@vger.kernel.org
-Cc: Amery Hung <ameryhung@gmail.com>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260727154103.1515347-2-sashal@kernel.org>
-
-From: Amery Hung <ameryhung@gmail.com>
-
-[ Upstream commit 201b62ccc83153d2925d310a2afe762905e0c455 ]
-
-Reduce the variable passing madness surrounding check_ctx_access().
-Currently, check_mem_access() passes many pointers to local variables to
-check_ctx_access(). They are used to initialize "struct
-bpf_insn_access_aux info" in check_ctx_access() and then passed to
-is_valid_access(). Then, check_ctx_access() takes the data our from
-info and write them back the pointers to pass them back. This can be
-simpilified by moving info up to check_mem_access().
-
-No functional change.
-
-Signed-off-by: Amery Hung <ameryhung@gmail.com>
-Link: https://lore.kernel.org/r/20250221175644.1822383-1-ameryhung@gmail.com
-Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-Stable-dep-of: 5e0b273e0a62 ("bpf: Reset register bounds before narrowing retval range in check_mem_access()")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- kernel/bpf/verifier.c | 27 +++++----------------------
- 1 file changed, 5 insertions(+), 22 deletions(-)
-
---- a/kernel/bpf/verifier.c
-+++ b/kernel/bpf/verifier.c
-@@ -5829,18 +5829,10 @@ static int check_packet_access(struct bp
-
- /* check access to 'struct bpf_context' fields. Supports fixed offsets only */
- static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size,
-- enum bpf_access_type t, enum bpf_reg_type *reg_type,
-- struct btf **btf, u32 *btf_id, bool *is_retval, bool is_ldsx)
-+ enum bpf_access_type t, struct bpf_insn_access_aux *info)
- {
-- struct bpf_insn_access_aux info = {
-- .reg_type = *reg_type,
-- .log = &env->log,
-- .is_retval = false,
-- .is_ldsx = is_ldsx,
-- };
--
- if (env->ops->is_valid_access &&
-- env->ops->is_valid_access(off, size, t, env->prog, &info)) {
-+ env->ops->is_valid_access(off, size, t, env->prog, info)) {
- /* A non zero info.ctx_field_size indicates that this field is a
- * candidate for later verifier transformation to load the whole
- * field and then apply a mask when accessed with a narrower
-@@ -5848,15 +5840,8 @@ static int check_ctx_access(struct bpf_v
- * will only allow for whole field access and rejects any other
- * type of narrower access.
- */
-- *reg_type = info.reg_type;
-- *is_retval = info.is_retval;
--
-- if (base_type(*reg_type) == PTR_TO_BTF_ID) {
-- *btf = info.btf;
-- *btf_id = info.btf_id;
-- } else {
-- env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size;
-- }
-+ if (base_type(info->reg_type) != PTR_TO_BTF_ID)
-+ env->insn_aux_data[insn_idx].ctx_field_size = info->ctx_field_size;
- /* remember the offset of last byte accessed in ctx */
- if (env->prog->aux->max_ctx_offset < off + size)
- env->prog->aux->max_ctx_offset = off + size;
-@@ -7224,9 +7209,7 @@ static int check_mem_access(struct bpf_v
- if (err < 0)
- return err;
-
-- err = check_ctx_access(env, insn_idx, off, size, t, &info.reg_type,
-- &info.btf, &info.btf_id, &info.is_retval,
-- info.is_ldsx);
-+ err = check_ctx_access(env, insn_idx, off, size, t, &info);
- if (err)
- verbose_linfo(env, insn_idx, "; ");
- if (!err && t == BPF_READ && value_regno >= 0) {
+++ /dev/null
-From stable+bounces-289597-greg=kroah.com@vger.kernel.org Mon Jul 27 17:52:29 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 27 Jul 2026 11:41:03 -0400
-Subject: bpf: Reset register bounds before narrowing retval range in check_mem_access()
-To: stable@vger.kernel.org
-Cc: Tristan Madani <tristan@talencesecurity.com>, Eduard Zingerman <eddyz87@gmail.com>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260727154103.1515347-3-sashal@kernel.org>
-
-From: Tristan Madani <tristan@talencesecurity.com>
-
-[ Upstream commit 5e0b273e0a62cc04ec338c7b502797c66c2ed42a ]
-
-When the BPF verifier processes a context load of an LSM hook return
-value, it calls __mark_reg_s32_range() to narrow the register to the
-hook's valid range. However, __mark_reg_s32_range() intersects the new
-range with the register's existing bounds using max_t()/min_t() rather
-than replacing them.
-
-If the destination register carries stale bounds from a prior instruction
-(e.g. BPF_MOV64_IMM), the intersection can produce a range narrower than
-reality. The verifier then believes it knows the register's exact value,
-while at runtime the actual hook return value is loaded, creating a
-verifier/runtime mismatch that can be used to bypass BPF memory safety
-checks.
-
-The else branch already calls mark_reg_unknown() to reset register state
-before any narrowing. Apply the same reset in the is_retval path so
-stale bounds are cleared before __mark_reg_s32_range() intersects.
-
-Fixes: 5d99e198be27 ("bpf, lsm: Add check for BPF LSM return value")
-Cc: stable@vger.kernel.org
-Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
-Acked-by: Eduard Zingerman <eddyz87@gmail.com>
-Link: https://lore.kernel.org/r/20260622230123.3695446-2-tristmd@gmail.com
-Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- kernel/bpf/verifier.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/kernel/bpf/verifier.c
-+++ b/kernel/bpf/verifier.c
-@@ -7219,6 +7219,7 @@ static int check_mem_access(struct bpf_v
- */
- if (info.reg_type == SCALAR_VALUE) {
- if (info.is_retval && get_func_retval_range(env->prog, &range)) {
-+ mark_reg_unknown(env, regs, value_regno);
- err = __mark_reg_s32_range(env, regs, value_regno,
- range.minval, range.maxval);
- if (err)
mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch
mm-prepare-to-move-subsection_map_init-to-mm-sparse-vmemmap.c.patch
mm-sparse-vmemmap-fix-dax-vmemmap-accounting-with-optimization.patch
-thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch
-thunderbolt-remove-service-debugfs-entries-during-unregister.patch
-thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch
-thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch
dma-dw-edma-fix-build-warning-in-dw_edma_pcie_probe.patch
dmaengine-dw-edma-fix-confusing-cleanup.h-syntax.patch
dmaengine-dw-edma-pcie-reject-devices-without-driver-data.patch
nvmet-introduce-nvmet_req_transfer_len.patch
nvmet-auth-reject-short-auth_receive-buffers.patch
ovl-use-linked-upper-dentry-in-copy-up-tmpfile.patch
-bpf-refactor-acquire-release-_reference_state.patch
-bpf-refactor-check_ctx_access.patch
-bpf-reset-register-bounds-before-narrowing-retval-range-in-check_mem_access.patch
block-add-helper-add_disk_final.patch
block-remove-redundant-gd_need_part_scan-in-add_disk_final.patch
dm-integrity-fix-leaking-uninitialized-kernel-memory.patch
+++ /dev/null
-From stable+bounces-289260-greg=kroah.com@vger.kernel.org Sun Jul 26 14:36:19 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 08:36:10 -0400
-Subject: thunderbolt: Keep XDomain reference during the lifetime of a service
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726123613.37376-1-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit 8b4060998637f06975fceee9b73845d8672d411e ]
-
-This is needed because we release the service ID in tb_service_release()
-and the ID array is owned by the parent XDomain.
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1008,6 +1008,7 @@ static void tb_service_release(struct de
- ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
-+ tb_xdomain_put(xd);
- }
-
- const struct device_type tb_service_type = {
-@@ -1116,7 +1117,7 @@ static void enumerate_services(struct tb
- svc->id = id;
- svc->dev.bus = &tb_bus_type;
- svc->dev.type = &tb_service_type;
-- svc->dev.parent = &xd->dev;
-+ svc->dev.parent = get_device(&xd->dev);
- dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id);
-
- tb_service_debugfs_init(svc);
+++ /dev/null
-From stable+bounces-289263-greg=kroah.com@vger.kernel.org Sun Jul 26 14:36:20 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 08:36:13 -0400
-Subject: thunderbolt: Prevent XDomain delayed work use-after-free on disconnect
-To: stable@vger.kernel.org
-Cc: Michael Bommarito <michael.bommarito@gmail.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726123613.37376-4-sashal@kernel.org>
-
-From: Michael Bommarito <michael.bommarito@gmail.com>
-
-[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
-
-tb_xdp_handle_request() runs on system_wq and queues
-xd->state_work via queue_delayed_work() in three request handlers:
-PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
-and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
-xd->properties_changed_work when local properties change.
-
-Concurrently, tb_xdomain_remove() calls stop_handshake() which does
-cancel_delayed_work_sync() on both delayed works. Later,
-tb_xdomain_unregister() calls device_unregister() which eventually
-frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
-tb_xdp_handle_request() in system workqueue") moved the request
-handler off tb->wq, the handler and the remove path are no longer
-serialized. If queue_delayed_work() executes after
-cancel_delayed_work_sync() but before the xdomain is freed, the
-delayed work fires on a freed object.
-
-Add xd->removing that tb_xdomain_remove() sets under xd->lock
-before calling stop_handshake(). Each external queue site holds
-the same lock and checks removing before calling
-queue_delayed_work(). This provides the mutual exclusion needed:
-either the queue site acquires the lock first and queues work that
-the subsequent cancel will see, or the remove path acquires the
-lock first and the queue site observes removing == true and skips
-the queue.
-
-Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
-Cc: stable@vger.kernel.org
-Assisted-by: Claude:claude-opus-4-7
-Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 26 +++++++++++++-------------
- 1 file changed, 13 insertions(+), 13 deletions(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -905,6 +905,19 @@ void tb_unregister_service_driver(struct
- }
- EXPORT_SYMBOL_GPL(tb_unregister_service_driver);
-
-+static int update_xdomain(struct device *dev, void *data)
-+{
-+ struct tb_xdomain *xd;
-+
-+ xd = tb_to_xdomain(dev);
-+ if (xd) {
-+ queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-+ msecs_to_jiffies(50));
-+ }
-+
-+ return 0;
-+}
-+
- static ssize_t key_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
-@@ -2480,19 +2493,6 @@ bool tb_xdomain_handle_request(struct tb
- return ret > 0;
- }
-
--static int update_xdomain(struct device *dev, void *data)
--{
-- struct tb_xdomain *xd;
--
-- xd = tb_to_xdomain(dev);
-- if (xd) {
-- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-- msecs_to_jiffies(50));
-- }
--
-- return 0;
--}
--
- static void update_all_xdomains(void)
- {
- bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain);
+++ /dev/null
-From stable+bounces-289261-greg=kroah.com@vger.kernel.org Sun Jul 26 14:36:19 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 08:36:11 -0400
-Subject: thunderbolt: Remove service debugfs entries during unregister
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726123613.37376-2-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit 4d5fc3f4068568dfcb8cbe2852b4adc56394aa26 ]
-
-We add them as part of the register path so to keep it symmetric remove
-them as part of the unregister path. This also removes them even if the
-service itself is not yet released (but is unregistered), thus allowing
-new register with the same service name to happen.
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 14 +++++++++++---
- 1 file changed, 11 insertions(+), 3 deletions(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1004,7 +1004,6 @@ static void tb_service_release(struct de
- struct tb_service *svc = container_of(dev, struct tb_service, dev);
- struct tb_xdomain *xd = tb_service_parent(svc);
-
-- tb_service_debugfs_remove(svc);
- ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
-@@ -1019,6 +1018,14 @@ const struct device_type tb_service_type
- };
- EXPORT_SYMBOL_GPL(tb_service_type);
-
-+static void __unregister_service(struct device *dev)
-+{
-+ struct tb_service *svc = tb_to_service(dev);
-+
-+ tb_service_debugfs_remove(svc);
-+ device_unregister(&svc->dev);
-+}
-+
- static int remove_missing_service(struct device *dev, void *data)
- {
- struct tb_xdomain *xd = data;
-@@ -1030,7 +1037,7 @@ static int remove_missing_service(struct
-
- if (!tb_property_find(xd->remote_properties, svc->key,
- TB_PROPERTY_TYPE_DIRECTORY))
-- device_unregister(dev);
-+ __unregister_service(dev);
-
- return 0;
- }
-@@ -1123,6 +1130,7 @@ static void enumerate_services(struct tb
- tb_service_debugfs_init(svc);
-
- if (device_register(&svc->dev)) {
-+ tb_service_debugfs_remove(svc);
- put_device(&svc->dev);
- break;
- }
-@@ -2053,7 +2061,7 @@ void tb_xdomain_add(struct tb_xdomain *x
-
- static int unregister_service(struct device *dev, void *data)
- {
-- device_unregister(dev);
-+ __unregister_service(dev);
- return 0;
- }
-
+++ /dev/null
-From stable+bounces-289262-greg=kroah.com@vger.kernel.org Sun Jul 26 14:36:20 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 08:36:12 -0400
-Subject: thunderbolt: Remove XDomain from the bus without holding tb->lock
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726123613.37376-3-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit a8937f35cf39c39c64325aa84d0463d866850857 ]
-
-Currently we call device_unregister() for services and the XDomain
-itself with tb->lock held. This prevents the service drivers from
-calling any functions that may take it. For this reason separate
-removing the XDomain from the topology data structures (where we need
-the lock) from unregistering the device from the bus (where remove
-callbacks of the drivers are being called).
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/debugfs.c | 2 +
- drivers/thunderbolt/domain.c | 30 +++++++++++++++++++++
- drivers/thunderbolt/icm.c | 5 +++
- drivers/thunderbolt/switch.c | 14 +++++++++
- drivers/thunderbolt/tb.c | 59 ++++++++++++++++++++----------------------
- drivers/thunderbolt/tb.h | 2 +
- drivers/thunderbolt/xdomain.c | 53 +++++++++++++++++++++++--------------
- 7 files changed, 115 insertions(+), 50 deletions(-)
-
---- a/drivers/thunderbolt/debugfs.c
-+++ b/drivers/thunderbolt/debugfs.c
-@@ -1508,6 +1508,8 @@ static void margining_port_remove(struct
-
- if (!port->usb4)
- return;
-+ if (!port->usb4->margining)
-+ return;
-
- snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
- parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
---- a/drivers/thunderbolt/domain.c
-+++ b/drivers/thunderbolt/domain.c
-@@ -871,6 +871,36 @@ int tb_domain_disconnect_all_paths(struc
- return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain);
- }
-
-+struct unregister_context {
-+ const struct tb *tb;
-+ int n;
-+};
-+
-+static int unregister_unplugged_xdomain(struct device *dev, void *data)
-+{
-+ struct unregister_context *ctx = data;
-+ struct tb_xdomain *xd;
-+
-+ xd = tb_to_xdomain(dev);
-+ if (xd && xd->tb == ctx->tb && xd->is_unplugged) {
-+ tb_xdomain_unregister(xd);
-+ ctx->n++;
-+ }
-+ return 0;
-+}
-+
-+int tb_domain_unregister_unplugged_xdomains(struct tb *tb)
-+{
-+ struct unregister_context ctx;
-+
-+ ctx.tb = tb_domain_get(tb);
-+ ctx.n = 0;
-+ bus_for_each_dev(&tb_bus_type, NULL, &ctx, unregister_unplugged_xdomain);
-+ tb_domain_put(tb);
-+
-+ return ctx.n;
-+}
-+
- int tb_domain_init(void)
- {
- int ret;
---- a/drivers/thunderbolt/icm.c
-+++ b/drivers/thunderbolt/icm.c
-@@ -713,6 +713,7 @@ static void remove_xdomain(struct tb_xdo
-
- sw = tb_to_switch(xd->dev.parent);
- tb_port_at(xd->route, sw)->xdomain = NULL;
-+ xd->is_unplugged = true;
- tb_xdomain_remove(xd);
- }
-
-@@ -1728,6 +1729,8 @@ static void icm_handle_notification(stru
-
- kfree(n->pkg);
- kfree(n);
-+
-+ tb_domain_unregister_unplugged_xdomains(tb);
- }
-
- static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
-@@ -2078,6 +2081,8 @@ static void icm_rescan_work(struct work_
- if (tb->root_switch)
- icm_free_unplugged_children(tb->root_switch);
- mutex_unlock(&tb->lock);
-+
-+ tb_domain_unregister_unplugged_xdomains(tb);
- }
-
- static void icm_complete(struct tb *tb)
---- a/drivers/thunderbolt/switch.c
-+++ b/drivers/thunderbolt/switch.c
-@@ -3554,6 +3554,20 @@ int tb_switch_resume(struct tb_switch *s
- tb_port_warn(port,
- "lost during suspend, disconnecting\n");
- tb_sw_set_unplugged(port->remote->sw);
-+ } else if (port->xdomain) {
-+ /*
-+ * If the user replaced the XDomain with
-+ * another router, this will succeed in
-+ * which case we must remove the XDomain
-+ * before adding the new router.
-+ */
-+ err = tb_cfg_get_upstream_port(sw->tb->ctl,
-+ port->xdomain->route);
-+ if (err > 0) {
-+ tb_port_warn(port,
-+ "XDomain was disconnected\n");
-+ port->xdomain->is_unplugged = true;
-+ }
- }
- }
- }
---- a/drivers/thunderbolt/tb.c
-+++ b/drivers/thunderbolt/tb.c
-@@ -2450,6 +2450,8 @@ put_sw:
- out:
- mutex_unlock(&tb->lock);
-
-+ tb_domain_unregister_unplugged_xdomains(tb);
-+
- pm_runtime_mark_last_busy(&tb->dev);
- pm_runtime_put_autosuspend(&tb->dev);
-
-@@ -3008,6 +3010,24 @@ static void tb_restore_children(struct t
- }
- }
-
-+static void tb_free_unplugged_xdomains(struct tb_switch *sw)
-+{
-+ struct tb_port *port;
-+
-+ tb_switch_for_each_port(sw, port) {
-+ if (tb_is_upstream_port(port))
-+ continue;
-+ if (port->xdomain && port->xdomain->is_unplugged) {
-+ tb_retimer_remove_all(port);
-+ tb_xdomain_remove(port->xdomain);
-+ tb_port_unconfigure_xdomain(port);
-+ port->xdomain = NULL;
-+ } else if (port->remote) {
-+ tb_free_unplugged_xdomains(port->remote->sw);
-+ }
-+ }
-+}
-+
- static int tb_resume_noirq(struct tb *tb)
- {
- struct tb_cm *tcm = tb_priv(tb);
-@@ -3027,6 +3047,7 @@ static int tb_resume_noirq(struct tb *tb
- tb_switch_resume(tb->root_switch, false);
- tb_free_invalid_tunnels(tb);
- tb_free_unplugged_children(tb->root_switch);
-+ tb_free_unplugged_xdomains(tb->root_switch);
- tb_restore_children(tb->root_switch);
-
- /*
-@@ -3069,28 +3090,6 @@ static int tb_resume_noirq(struct tb *tb
- return 0;
- }
-
--static int tb_free_unplugged_xdomains(struct tb_switch *sw)
--{
-- struct tb_port *port;
-- int ret = 0;
--
-- tb_switch_for_each_port(sw, port) {
-- if (tb_is_upstream_port(port))
-- continue;
-- if (port->xdomain && port->xdomain->is_unplugged) {
-- tb_retimer_remove_all(port);
-- tb_xdomain_remove(port->xdomain);
-- tb_port_unconfigure_xdomain(port);
-- port->xdomain = NULL;
-- ret++;
-- } else if (port->remote) {
-- ret += tb_free_unplugged_xdomains(port->remote->sw);
-- }
-- }
--
-- return ret;
--}
--
- static int tb_freeze_noirq(struct tb *tb)
- {
- struct tb_cm *tcm = tb_priv(tb);
-@@ -3110,14 +3109,14 @@ static int tb_thaw_noirq(struct tb *tb)
- static void tb_complete(struct tb *tb)
- {
- /*
-- * Release any unplugged XDomains and if there is a case where
-+ * Unregister unplugged XDomains and if there is a case where
- * another domain is swapped in place of unplugged XDomain we
- * need to run another rescan.
- */
-- mutex_lock(&tb->lock);
-- if (tb_free_unplugged_xdomains(tb->root_switch))
-- tb_scan_switch(tb->root_switch);
-- mutex_unlock(&tb->lock);
-+ if (tb_domain_unregister_unplugged_xdomains(tb)) {
-+ scoped_guard(mutex, &tb->lock)
-+ tb_scan_switch(tb->root_switch);
-+ }
- }
-
- static int tb_runtime_suspend(struct tb *tb)
-@@ -3144,11 +3143,11 @@ static void tb_remove_work(struct work_s
- struct tb *tb = tcm_to_tb(tcm);
-
- mutex_lock(&tb->lock);
-- if (tb->root_switch) {
-+ if (tb->root_switch)
- tb_free_unplugged_children(tb->root_switch);
-- tb_free_unplugged_xdomains(tb->root_switch);
-- }
- mutex_unlock(&tb->lock);
-+
-+ tb_free_unplugged_xdomains(tb->root_switch);
- }
-
- static int tb_runtime_resume(struct tb *tb)
---- a/drivers/thunderbolt/tb.h
-+++ b/drivers/thunderbolt/tb.h
-@@ -786,6 +786,7 @@ int tb_domain_disconnect_xdomain_paths(s
- int transmit_path, int transmit_ring,
- int receive_path, int receive_ring);
- int tb_domain_disconnect_all_paths(struct tb *tb);
-+int tb_domain_unregister_unplugged_xdomains(struct tb *tb);
-
- static inline struct tb *tb_domain_get(struct tb *tb)
- {
-@@ -1233,6 +1234,7 @@ struct tb_xdomain *tb_xdomain_alloc(stru
- const uuid_t *remote_uuid);
- void tb_xdomain_add(struct tb_xdomain *xd);
- void tb_xdomain_remove(struct tb_xdomain *xd);
-+void tb_xdomain_unregister(struct tb_xdomain *xd);
- struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
- u8 depth);
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -2066,41 +2066,54 @@ static int unregister_service(struct dev
- }
-
- /**
-- * tb_xdomain_remove() - Remove XDomain from the bus
-+ * tb_xdomain_remove() - Remove XDomain
- * @xd: XDomain to remove
- *
-- * This will stop all ongoing configuration work and remove the XDomain
-- * along with any services from the bus. When the last reference to @xd
-- * is released the object will be released as well.
-+ * This will stop all ongoing configuration work. XDomain is not removed
-+ * from the bus if it was added. That needs to be done separately by
-+ * calling tb_xdomain_unregister().
-+ *
-+ * Called with @tb->lock held.
- */
- void tb_xdomain_remove(struct tb_xdomain *xd)
- {
- tb_xdomain_debugfs_remove(xd);
--
- stop_handshake(xd);
--
-- device_for_each_child_reverse(&xd->dev, xd, unregister_service);
--
- tb_xdomain_link_exit(xd);
-
-- /*
-- * Undo runtime PM here explicitly because it is possible that
-- * the XDomain was never added to the bus and thus device_del()
-- * is not called for it (device_del() would handle this otherwise).
-- */
-- pm_runtime_disable(&xd->dev);
-- pm_runtime_put_noidle(&xd->dev);
-- pm_runtime_set_suspended(&xd->dev);
--
- if (!device_is_registered(&xd->dev)) {
-+ /*
-+ * Undo runtime PM here explicitly because it is
-+ * possible that the XDomain was never added to the bus
-+ * and thus device_del() is not called for it
-+ * (device_del() would handle this otherwise).
-+ */
-+ pm_runtime_disable(&xd->dev);
-+ pm_runtime_put_noidle(&xd->dev);
-+ pm_runtime_set_suspended(&xd->dev);
- put_device(&xd->dev);
-- } else {
-- dev_info(&xd->dev, "host disconnected\n");
-- device_unregister(&xd->dev);
- }
- }
-
- /**
-+ * tb_xdomain_unregister() - Unregister XDomain
-+ * @xd: XDomain to unregister
-+ *
-+ * This will unregister the XDomain along with any services from the
-+ * bus. When the last reference to @xd is released the object will be
-+ * released as well.
-+ */
-+void tb_xdomain_unregister(struct tb_xdomain *xd)
-+{
-+ lockdep_assert_not_held(&xd->tb->lock);
-+
-+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
-+
-+ dev_info(&xd->dev, "host disconnected\n");
-+ device_unregister(&xd->dev);
-+}
-+
-+/**
- * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain
- * @xd: XDomain connection
- *
mm-sparse-vmemmap-fix-vmemmap-accounting-underflow.patch
kho-make-sure-scratch-size-is-always-aligned-by-cma_min_alignment_bytes.patch
mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch
-thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch
-thunderbolt-remove-service-debugfs-entries-during-unregister.patch
-thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch
-thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch
dmaengine-dw-edma-fix-confusing-cleanup.h-syntax.patch
dmaengine-dw-edma-pcie-reject-devices-without-driver-data.patch
ovl-use-linked-upper-dentry-in-copy-up-tmpfile.patch
+++ /dev/null
-From stable+bounces-289230-greg=kroah.com@vger.kernel.org Sun Jul 26 14:00:55 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 08:00:47 -0400
-Subject: thunderbolt: Keep XDomain reference during the lifetime of a service
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726120050.4138299-1-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit 8b4060998637f06975fceee9b73845d8672d411e ]
-
-This is needed because we release the service ID in tb_service_release()
-and the ID array is owned by the parent XDomain.
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1012,6 +1012,7 @@ static void tb_service_release(struct de
- ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
-+ tb_xdomain_put(xd);
- }
-
- const struct device_type tb_service_type = {
-@@ -1120,7 +1121,7 @@ static void enumerate_services(struct tb
- svc->id = id;
- svc->dev.bus = &tb_bus_type;
- svc->dev.type = &tb_service_type;
-- svc->dev.parent = &xd->dev;
-+ svc->dev.parent = get_device(&xd->dev);
- dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id);
-
- tb_service_debugfs_init(svc);
+++ /dev/null
-From stable+bounces-289233-greg=kroah.com@vger.kernel.org Sun Jul 26 14:03:26 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 08:00:50 -0400
-Subject: thunderbolt: Prevent XDomain delayed work use-after-free on disconnect
-To: stable@vger.kernel.org
-Cc: Michael Bommarito <michael.bommarito@gmail.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726120050.4138299-4-sashal@kernel.org>
-
-From: Michael Bommarito <michael.bommarito@gmail.com>
-
-[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
-
-tb_xdp_handle_request() runs on system_wq and queues
-xd->state_work via queue_delayed_work() in three request handlers:
-PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
-and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
-xd->properties_changed_work when local properties change.
-
-Concurrently, tb_xdomain_remove() calls stop_handshake() which does
-cancel_delayed_work_sync() on both delayed works. Later,
-tb_xdomain_unregister() calls device_unregister() which eventually
-frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
-tb_xdp_handle_request() in system workqueue") moved the request
-handler off tb->wq, the handler and the remove path are no longer
-serialized. If queue_delayed_work() executes after
-cancel_delayed_work_sync() but before the xdomain is freed, the
-delayed work fires on a freed object.
-
-Add xd->removing that tb_xdomain_remove() sets under xd->lock
-before calling stop_handshake(). Each external queue site holds
-the same lock and checks removing before calling
-queue_delayed_work(). This provides the mutual exclusion needed:
-either the queue site acquires the lock first and queues work that
-the subsequent cancel will see, or the remove path acquires the
-lock first and the queue site observes removing == true and skips
-the queue.
-
-Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
-Cc: stable@vger.kernel.org
-Assisted-by: Claude:claude-opus-4-7
-Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 26 +++++++++++++-------------
- 1 file changed, 13 insertions(+), 13 deletions(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -909,6 +909,19 @@ void tb_unregister_service_driver(struct
- }
- EXPORT_SYMBOL_GPL(tb_unregister_service_driver);
-
-+static int update_xdomain(struct device *dev, void *data)
-+{
-+ struct tb_xdomain *xd;
-+
-+ xd = tb_to_xdomain(dev);
-+ if (xd) {
-+ queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-+ msecs_to_jiffies(50));
-+ }
-+
-+ return 0;
-+}
-+
- static ssize_t key_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
-@@ -2500,19 +2513,6 @@ bool tb_xdomain_handle_request(struct tb
- return ret > 0;
- }
-
--static int update_xdomain(struct device *dev, void *data)
--{
-- struct tb_xdomain *xd;
--
-- xd = tb_to_xdomain(dev);
-- if (xd) {
-- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-- msecs_to_jiffies(50));
-- }
--
-- return 0;
--}
--
- static void update_all_xdomains(void)
- {
- bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain);
+++ /dev/null
-From stable+bounces-289231-greg=kroah.com@vger.kernel.org Sun Jul 26 14:00:57 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 08:00:48 -0400
-Subject: thunderbolt: Remove service debugfs entries during unregister
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726120050.4138299-2-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit 4d5fc3f4068568dfcb8cbe2852b4adc56394aa26 ]
-
-We add them as part of the register path so to keep it symmetric remove
-them as part of the unregister path. This also removes them even if the
-service itself is not yet released (but is unregistered), thus allowing
-new register with the same service name to happen.
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 14 +++++++++++---
- 1 file changed, 11 insertions(+), 3 deletions(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1008,7 +1008,6 @@ static void tb_service_release(struct de
- struct tb_service *svc = container_of(dev, struct tb_service, dev);
- struct tb_xdomain *xd = tb_service_parent(svc);
-
-- tb_service_debugfs_remove(svc);
- ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
-@@ -1023,6 +1022,14 @@ const struct device_type tb_service_type
- };
- EXPORT_SYMBOL_GPL(tb_service_type);
-
-+static void __unregister_service(struct device *dev)
-+{
-+ struct tb_service *svc = tb_to_service(dev);
-+
-+ tb_service_debugfs_remove(svc);
-+ device_unregister(&svc->dev);
-+}
-+
- static int remove_missing_service(struct device *dev, void *data)
- {
- struct tb_xdomain *xd = data;
-@@ -1034,7 +1041,7 @@ static int remove_missing_service(struct
-
- if (!tb_property_find(xd->remote_properties, svc->key,
- TB_PROPERTY_TYPE_DIRECTORY))
-- device_unregister(dev);
-+ __unregister_service(dev);
-
- return 0;
- }
-@@ -1127,6 +1134,7 @@ static void enumerate_services(struct tb
- tb_service_debugfs_init(svc);
-
- if (device_register(&svc->dev)) {
-+ tb_service_debugfs_remove(svc);
- put_device(&svc->dev);
- break;
- }
-@@ -2059,7 +2067,7 @@ void tb_xdomain_add(struct tb_xdomain *x
-
- static int unregister_service(struct device *dev, void *data)
- {
-- device_unregister(dev);
-+ __unregister_service(dev);
- return 0;
- }
-
+++ /dev/null
-From stable+bounces-289232-greg=kroah.com@vger.kernel.org Sun Jul 26 14:00:59 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 08:00:49 -0400
-Subject: thunderbolt: Remove XDomain from the bus without holding tb->lock
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726120050.4138299-3-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit a8937f35cf39c39c64325aa84d0463d866850857 ]
-
-Currently we call device_unregister() for services and the XDomain
-itself with tb->lock held. This prevents the service drivers from
-calling any functions that may take it. For this reason separate
-removing the XDomain from the topology data structures (where we need
-the lock) from unregistering the device from the bus (where remove
-callbacks of the drivers are being called).
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/debugfs.c | 2 +
- drivers/thunderbolt/domain.c | 30 +++++++++++++++++++++
- drivers/thunderbolt/icm.c | 5 +++
- drivers/thunderbolt/switch.c | 14 +++++++++
- drivers/thunderbolt/tb.c | 59 ++++++++++++++++++++----------------------
- drivers/thunderbolt/tb.h | 2 +
- drivers/thunderbolt/xdomain.c | 53 +++++++++++++++++++++++--------------
- 7 files changed, 115 insertions(+), 50 deletions(-)
-
---- a/drivers/thunderbolt/debugfs.c
-+++ b/drivers/thunderbolt/debugfs.c
-@@ -1786,6 +1786,8 @@ static void margining_port_remove(struct
-
- if (!port->usb4)
- return;
-+ if (!port->usb4->margining)
-+ return;
-
- snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
- parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
---- a/drivers/thunderbolt/domain.c
-+++ b/drivers/thunderbolt/domain.c
-@@ -850,6 +850,36 @@ int tb_domain_disconnect_all_paths(struc
- return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain);
- }
-
-+struct unregister_context {
-+ const struct tb *tb;
-+ int n;
-+};
-+
-+static int unregister_unplugged_xdomain(struct device *dev, void *data)
-+{
-+ struct unregister_context *ctx = data;
-+ struct tb_xdomain *xd;
-+
-+ xd = tb_to_xdomain(dev);
-+ if (xd && xd->tb == ctx->tb && xd->is_unplugged) {
-+ tb_xdomain_unregister(xd);
-+ ctx->n++;
-+ }
-+ return 0;
-+}
-+
-+int tb_domain_unregister_unplugged_xdomains(struct tb *tb)
-+{
-+ struct unregister_context ctx;
-+
-+ ctx.tb = tb_domain_get(tb);
-+ ctx.n = 0;
-+ bus_for_each_dev(&tb_bus_type, NULL, &ctx, unregister_unplugged_xdomain);
-+ tb_domain_put(tb);
-+
-+ return ctx.n;
-+}
-+
- int tb_domain_init(void)
- {
- int ret;
---- a/drivers/thunderbolt/icm.c
-+++ b/drivers/thunderbolt/icm.c
-@@ -738,6 +738,7 @@ static void remove_xdomain(struct tb_xdo
-
- sw = tb_to_switch(xd->dev.parent);
- tb_port_at(xd->route, sw)->xdomain = NULL;
-+ xd->is_unplugged = true;
- tb_xdomain_remove(xd);
- }
-
-@@ -1762,6 +1763,8 @@ static void icm_handle_notification(stru
-
- kfree(n->pkg);
- kfree(n);
-+
-+ tb_domain_unregister_unplugged_xdomains(tb);
- }
-
- static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
-@@ -2112,6 +2115,8 @@ static void icm_rescan_work(struct work_
- if (tb->root_switch)
- icm_free_unplugged_children(tb->root_switch);
- mutex_unlock(&tb->lock);
-+
-+ tb_domain_unregister_unplugged_xdomains(tb);
- }
-
- static void icm_complete(struct tb *tb)
---- a/drivers/thunderbolt/switch.c
-+++ b/drivers/thunderbolt/switch.c
-@@ -3603,6 +3603,20 @@ int tb_switch_resume(struct tb_switch *s
- tb_port_warn(port,
- "lost during suspend, disconnecting\n");
- tb_sw_set_unplugged(port->remote->sw);
-+ } else if (port->xdomain) {
-+ /*
-+ * If the user replaced the XDomain with
-+ * another router, this will succeed in
-+ * which case we must remove the XDomain
-+ * before adding the new router.
-+ */
-+ err = tb_cfg_get_upstream_port(sw->tb->ctl,
-+ port->xdomain->route);
-+ if (err > 0) {
-+ tb_port_warn(port,
-+ "XDomain was disconnected\n");
-+ port->xdomain->is_unplugged = true;
-+ }
- }
- }
- }
---- a/drivers/thunderbolt/tb.c
-+++ b/drivers/thunderbolt/tb.c
-@@ -2524,6 +2524,8 @@ put_sw:
- out:
- mutex_unlock(&tb->lock);
-
-+ tb_domain_unregister_unplugged_xdomains(tb);
-+
- pm_runtime_mark_last_busy(&tb->dev);
- pm_runtime_put_autosuspend(&tb->dev);
-
-@@ -3110,6 +3112,24 @@ static void tb_restore_children(struct t
- }
- }
-
-+static void tb_free_unplugged_xdomains(struct tb_switch *sw)
-+{
-+ struct tb_port *port;
-+
-+ tb_switch_for_each_port(sw, port) {
-+ if (tb_is_upstream_port(port))
-+ continue;
-+ if (port->xdomain && port->xdomain->is_unplugged) {
-+ tb_retimer_remove_all(port);
-+ tb_xdomain_remove(port->xdomain);
-+ tb_port_unconfigure_xdomain(port);
-+ port->xdomain = NULL;
-+ } else if (port->remote) {
-+ tb_free_unplugged_xdomains(port->remote->sw);
-+ }
-+ }
-+}
-+
- static int tb_resume_noirq(struct tb *tb)
- {
- struct tb_cm *tcm = tb_priv(tb);
-@@ -3129,6 +3149,7 @@ static int tb_resume_noirq(struct tb *tb
- tb_switch_resume(tb->root_switch, false);
- tb_free_invalid_tunnels(tb);
- tb_free_unplugged_children(tb->root_switch);
-+ tb_free_unplugged_xdomains(tb->root_switch);
- tb_restore_children(tb->root_switch);
-
- /*
-@@ -3171,28 +3192,6 @@ static int tb_resume_noirq(struct tb *tb
- return 0;
- }
-
--static int tb_free_unplugged_xdomains(struct tb_switch *sw)
--{
-- struct tb_port *port;
-- int ret = 0;
--
-- tb_switch_for_each_port(sw, port) {
-- if (tb_is_upstream_port(port))
-- continue;
-- if (port->xdomain && port->xdomain->is_unplugged) {
-- tb_retimer_remove_all(port);
-- tb_xdomain_remove(port->xdomain);
-- tb_port_unconfigure_xdomain(port);
-- port->xdomain = NULL;
-- ret++;
-- } else if (port->remote) {
-- ret += tb_free_unplugged_xdomains(port->remote->sw);
-- }
-- }
--
-- return ret;
--}
--
- static int tb_freeze_noirq(struct tb *tb)
- {
- struct tb_cm *tcm = tb_priv(tb);
-@@ -3212,14 +3211,14 @@ static int tb_thaw_noirq(struct tb *tb)
- static void tb_complete(struct tb *tb)
- {
- /*
-- * Release any unplugged XDomains and if there is a case where
-+ * Unregister unplugged XDomains and if there is a case where
- * another domain is swapped in place of unplugged XDomain we
- * need to run another rescan.
- */
-- mutex_lock(&tb->lock);
-- if (tb_free_unplugged_xdomains(tb->root_switch))
-- tb_scan_switch(tb->root_switch);
-- mutex_unlock(&tb->lock);
-+ if (tb_domain_unregister_unplugged_xdomains(tb)) {
-+ scoped_guard(mutex, &tb->lock)
-+ tb_scan_switch(tb->root_switch);
-+ }
- }
-
- static int tb_runtime_suspend(struct tb *tb)
-@@ -3246,11 +3245,11 @@ static void tb_remove_work(struct work_s
- struct tb *tb = tcm_to_tb(tcm);
-
- mutex_lock(&tb->lock);
-- if (tb->root_switch) {
-+ if (tb->root_switch)
- tb_free_unplugged_children(tb->root_switch);
-- tb_free_unplugged_xdomains(tb->root_switch);
-- }
- mutex_unlock(&tb->lock);
-+
-+ tb_free_unplugged_xdomains(tb->root_switch);
- }
-
- static int tb_runtime_resume(struct tb *tb)
---- a/drivers/thunderbolt/tb.h
-+++ b/drivers/thunderbolt/tb.h
-@@ -792,6 +792,7 @@ int tb_domain_disconnect_xdomain_paths(s
- int transmit_path, int transmit_ring,
- int receive_path, int receive_ring);
- int tb_domain_disconnect_all_paths(struct tb *tb);
-+int tb_domain_unregister_unplugged_xdomains(struct tb *tb);
-
- static inline struct tb *tb_domain_get(struct tb *tb)
- {
-@@ -1262,6 +1263,7 @@ struct tb_xdomain *tb_xdomain_alloc(stru
- const uuid_t *remote_uuid);
- void tb_xdomain_add(struct tb_xdomain *xd);
- void tb_xdomain_remove(struct tb_xdomain *xd);
-+void tb_xdomain_unregister(struct tb_xdomain *xd);
- struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
- u8 depth);
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -2072,41 +2072,54 @@ static int unregister_service(struct dev
- }
-
- /**
-- * tb_xdomain_remove() - Remove XDomain from the bus
-+ * tb_xdomain_remove() - Remove XDomain
- * @xd: XDomain to remove
- *
-- * This will stop all ongoing configuration work and remove the XDomain
-- * along with any services from the bus. When the last reference to @xd
-- * is released the object will be released as well.
-+ * This will stop all ongoing configuration work. XDomain is not removed
-+ * from the bus if it was added. That needs to be done separately by
-+ * calling tb_xdomain_unregister().
-+ *
-+ * Called with @tb->lock held.
- */
- void tb_xdomain_remove(struct tb_xdomain *xd)
- {
- tb_xdomain_debugfs_remove(xd);
--
- stop_handshake(xd);
--
-- device_for_each_child_reverse(&xd->dev, xd, unregister_service);
--
- tb_xdomain_link_exit(xd);
-
-- /*
-- * Undo runtime PM here explicitly because it is possible that
-- * the XDomain was never added to the bus and thus device_del()
-- * is not called for it (device_del() would handle this otherwise).
-- */
-- pm_runtime_disable(&xd->dev);
-- pm_runtime_put_noidle(&xd->dev);
-- pm_runtime_set_suspended(&xd->dev);
--
- if (!device_is_registered(&xd->dev)) {
-+ /*
-+ * Undo runtime PM here explicitly because it is
-+ * possible that the XDomain was never added to the bus
-+ * and thus device_del() is not called for it
-+ * (device_del() would handle this otherwise).
-+ */
-+ pm_runtime_disable(&xd->dev);
-+ pm_runtime_put_noidle(&xd->dev);
-+ pm_runtime_set_suspended(&xd->dev);
- put_device(&xd->dev);
-- } else {
-- dev_info(&xd->dev, "host disconnected\n");
-- device_unregister(&xd->dev);
- }
- }
-
- /**
-+ * tb_xdomain_unregister() - Unregister XDomain
-+ * @xd: XDomain to unregister
-+ *
-+ * This will unregister the XDomain along with any services from the
-+ * bus. When the last reference to @xd is released the object will be
-+ * released as well.
-+ */
-+void tb_xdomain_unregister(struct tb_xdomain *xd)
-+{
-+ lockdep_assert_not_held(&xd->tb->lock);
-+
-+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
-+
-+ dev_info(&xd->dev, "host disconnected\n");
-+ device_unregister(&xd->dev);
-+}
-+
-+/**
- * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain
- * @xd: XDomain connection
- *
dma-dw-edma-fix-build-warning-in-dw_edma_pcie_probe.patch
dmaengine-dw-edma-fix-confusing-cleanup.h-syntax.patch
dmaengine-dw-edma-pcie-reject-devices-without-driver-data.patch
-thunderbolt-handle-lane-bonding-of-gen-4-xdomain-links-properly.patch
-thunderbolt-remove-usage-of-the-deprecated-ida_simple_xx-api.patch
-thunderbolt-update-property.c-function-documentation.patch
-thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch
-thunderbolt-remove-service-debugfs-entries-during-unregister.patch
-thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch
-thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch
platform-x86-dell-smbios-move-request-functions-for-reuse.patch
platform-x86-dell-laptop-fix-missing-cleanups-in-init-error-path.patch
i2c-imx-separate-atomic-dma-and-non-dma-use-case.patch
+++ /dev/null
-From stable+bounces-289294-greg=kroah.com@vger.kernel.org Sun Jul 26 16:01:51 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 09:59:49 -0400
-Subject: thunderbolt: Handle lane bonding of Gen 4 XDomain links properly
-To: stable@vger.kernel.org
-Cc: Gil Fine <gil.fine@linux.intel.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726135955.668115-1-sashal@kernel.org>
-
-From: Gil Fine <gil.fine@linux.intel.com>
-
-[ Upstream commit 36b6ad6ad0350554e611a8cb754ccd40857416a8 ]
-
-Gen 4 links come up as bonded already so we are not supposed to initiate
-lane bonding on them. However, we should still update the port
-structures accordingly. Split these into their own functions to make it
-easier to follow.
-
-Signed-off-by: Gil Fine <gil.fine@linux.intel.com>
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/tb.c | 2 -
- drivers/thunderbolt/xdomain.c | 49 +++++++++++++++++++++++++++++++++++++++++-
- 2 files changed, 48 insertions(+), 3 deletions(-)
-
---- a/drivers/thunderbolt/tb.c
-+++ b/drivers/thunderbolt/tb.c
-@@ -539,8 +539,6 @@ static void tb_port_unconfigure_xdomain(
- usb4_port_unconfigure_xdomain(port);
- else
- tb_lc_unconfigure_xdomain(port);
--
-- tb_port_enable(port->dual_link_port);
- }
-
- static void tb_scan_xdomain(struct tb_port *port)
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1903,6 +1903,50 @@ struct device_type tb_xdomain_type = {
- };
- EXPORT_SYMBOL_GPL(tb_xdomain_type);
-
-+static void tb_xdomain_link_init(struct tb_xdomain *xd, struct tb_port *down)
-+{
-+ if (!down->dual_link_port)
-+ return;
-+
-+ /*
-+ * Gen 4 links come up already as bonded so only update the port
-+ * structures here.
-+ */
-+ if (tb_port_get_link_generation(down) >= 4) {
-+ down->bonded = true;
-+ down->dual_link_port->bonded = true;
-+ } else {
-+ xd->bonding_possible = true;
-+ }
-+}
-+
-+static void tb_xdomain_link_exit(struct tb_xdomain *xd)
-+{
-+ struct tb_port *down = tb_xdomain_downstream_port(xd);
-+
-+ if (!down->dual_link_port)
-+ return;
-+
-+ if (tb_port_get_link_generation(down) >= 4) {
-+ down->bonded = false;
-+ down->dual_link_port->bonded = false;
-+ } else if (xd->link_width > TB_LINK_WIDTH_SINGLE) {
-+ /*
-+ * Just return port structures back to way they were and
-+ * update credits. No need to update userspace because
-+ * the XDomain is removed soon anyway.
-+ */
-+ tb_port_lane_bonding_disable(down);
-+ tb_port_update_credits(down);
-+ } else if (down->dual_link_port) {
-+ /*
-+ * Re-enable the lane 1 adapter we disabled at the end
-+ * of tb_xdomain_get_properties().
-+ */
-+ tb_port_enable(down->dual_link_port);
-+ }
-+}
-+
- /**
- * tb_xdomain_alloc() - Allocate new XDomain object
- * @tb: Domain where the XDomain belongs
-@@ -1953,7 +1997,8 @@ struct tb_xdomain *tb_xdomain_alloc(stru
- goto err_free_local_uuid;
- } else {
- xd->needs_uuid = true;
-- xd->bonding_possible = !!down->dual_link_port;
-+
-+ tb_xdomain_link_init(xd, down);
- }
-
- device_initialize(&xd->dev);
-@@ -2022,6 +2067,8 @@ void tb_xdomain_remove(struct tb_xdomain
-
- device_for_each_child_reverse(&xd->dev, xd, unregister_service);
-
-+ tb_xdomain_link_exit(xd);
-+
- /*
- * Undo runtime PM here explicitly because it is possible that
- * the XDomain was never added to the bus and thus device_del()
+++ /dev/null
-From stable+bounces-289297-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:04 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 09:59:52 -0400
-Subject: thunderbolt: Keep XDomain reference during the lifetime of a service
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726135955.668115-4-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit 8b4060998637f06975fceee9b73845d8672d411e ]
-
-This is needed because we release the service ID in tb_service_release()
-and the ID array is owned by the parent XDomain.
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1008,6 +1008,7 @@ static void tb_service_release(struct de
- ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
-+ tb_xdomain_put(xd);
- }
-
- struct device_type tb_service_type = {
-@@ -1116,7 +1117,7 @@ static void enumerate_services(struct tb
- svc->id = id;
- svc->dev.bus = &tb_bus_type;
- svc->dev.type = &tb_service_type;
-- svc->dev.parent = &xd->dev;
-+ svc->dev.parent = get_device(&xd->dev);
- dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id);
-
- tb_service_debugfs_init(svc);
+++ /dev/null
-From stable+bounces-289300-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:09 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 09:59:55 -0400
-Subject: thunderbolt: Prevent XDomain delayed work use-after-free on disconnect
-To: stable@vger.kernel.org
-Cc: Michael Bommarito <michael.bommarito@gmail.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726135955.668115-7-sashal@kernel.org>
-
-From: Michael Bommarito <michael.bommarito@gmail.com>
-
-[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
-
-tb_xdp_handle_request() runs on system_wq and queues
-xd->state_work via queue_delayed_work() in three request handlers:
-PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
-and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
-xd->properties_changed_work when local properties change.
-
-Concurrently, tb_xdomain_remove() calls stop_handshake() which does
-cancel_delayed_work_sync() on both delayed works. Later,
-tb_xdomain_unregister() calls device_unregister() which eventually
-frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
-tb_xdp_handle_request() in system workqueue") moved the request
-handler off tb->wq, the handler and the remove path are no longer
-serialized. If queue_delayed_work() executes after
-cancel_delayed_work_sync() but before the xdomain is freed, the
-delayed work fires on a freed object.
-
-Add xd->removing that tb_xdomain_remove() sets under xd->lock
-before calling stop_handshake(). Each external queue site holds
-the same lock and checks removing before calling
-queue_delayed_work(). This provides the mutual exclusion needed:
-either the queue site acquires the lock first and queues work that
-the subsequent cancel will see, or the remove path acquires the
-lock first and the queue site observes removing == true and skips
-the queue.
-
-Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
-Cc: stable@vger.kernel.org
-Assisted-by: Claude:claude-opus-4-7
-Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 26 +++++++++++++-------------
- 1 file changed, 13 insertions(+), 13 deletions(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -905,6 +905,19 @@ void tb_unregister_service_driver(struct
- }
- EXPORT_SYMBOL_GPL(tb_unregister_service_driver);
-
-+static int update_xdomain(struct device *dev, void *data)
-+{
-+ struct tb_xdomain *xd;
-+
-+ xd = tb_to_xdomain(dev);
-+ if (xd) {
-+ queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-+ msecs_to_jiffies(50));
-+ }
-+
-+ return 0;
-+}
-+
- static ssize_t key_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
-@@ -2475,19 +2488,6 @@ bool tb_xdomain_handle_request(struct tb
- return ret > 0;
- }
-
--static int update_xdomain(struct device *dev, void *data)
--{
-- struct tb_xdomain *xd;
--
-- xd = tb_to_xdomain(dev);
-- if (xd) {
-- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-- msecs_to_jiffies(50));
-- }
--
-- return 0;
--}
--
- static void update_all_xdomains(void)
- {
- bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain);
+++ /dev/null
-From stable+bounces-289298-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:05 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 09:59:53 -0400
-Subject: thunderbolt: Remove service debugfs entries during unregister
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726135955.668115-5-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit 4d5fc3f4068568dfcb8cbe2852b4adc56394aa26 ]
-
-We add them as part of the register path so to keep it symmetric remove
-them as part of the unregister path. This also removes them even if the
-service itself is not yet released (but is unregistered), thus allowing
-new register with the same service name to happen.
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 14 +++++++++++---
- 1 file changed, 11 insertions(+), 3 deletions(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1004,7 +1004,6 @@ static void tb_service_release(struct de
- struct tb_service *svc = container_of(dev, struct tb_service, dev);
- struct tb_xdomain *xd = tb_service_parent(svc);
-
-- tb_service_debugfs_remove(svc);
- ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
-@@ -1019,6 +1018,14 @@ struct device_type tb_service_type = {
- };
- EXPORT_SYMBOL_GPL(tb_service_type);
-
-+static void __unregister_service(struct device *dev)
-+{
-+ struct tb_service *svc = tb_to_service(dev);
-+
-+ tb_service_debugfs_remove(svc);
-+ device_unregister(&svc->dev);
-+}
-+
- static int remove_missing_service(struct device *dev, void *data)
- {
- struct tb_xdomain *xd = data;
-@@ -1030,7 +1037,7 @@ static int remove_missing_service(struct
-
- if (!tb_property_find(xd->remote_properties, svc->key,
- TB_PROPERTY_TYPE_DIRECTORY))
-- device_unregister(dev);
-+ __unregister_service(dev);
-
- return 0;
- }
-@@ -1123,6 +1130,7 @@ static void enumerate_services(struct tb
- tb_service_debugfs_init(svc);
-
- if (device_register(&svc->dev)) {
-+ tb_service_debugfs_remove(svc);
- put_device(&svc->dev);
- break;
- }
-@@ -2048,7 +2056,7 @@ void tb_xdomain_add(struct tb_xdomain *x
-
- static int unregister_service(struct device *dev, void *data)
- {
-- device_unregister(dev);
-+ __unregister_service(dev);
- return 0;
- }
-
+++ /dev/null
-From stable+bounces-289295-greg=kroah.com@vger.kernel.org Sun Jul 26 16:02:04 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 09:59:50 -0400
-Subject: thunderbolt: Remove usage of the deprecated ida_simple_xx() API
-To: stable@vger.kernel.org
-Cc: Christophe JAILLET <christophe.jaillet@wanadoo.fr>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726135955.668115-2-sashal@kernel.org>
-
-From: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
-
-[ Upstream commit dec6a613574cd3dea799170b7aaa8fd76e22f176 ]
-
-ida_alloc() and ida_free() should be preferred to the deprecated
-ida_simple_get() and ida_simple_remove().
-
-Note that the upper limit of ida_simple_get() is exclusive, but the one of
-ida_alloc_range()/ida_alloc_max() is inclusive. So a -1 has been added
-when needed.
-
-Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/domain.c | 6 +++---
- drivers/thunderbolt/nhi.c | 6 +++---
- drivers/thunderbolt/nvm.c | 4 ++--
- drivers/thunderbolt/switch.c | 6 +++---
- drivers/thunderbolt/xdomain.c | 4 ++--
- 5 files changed, 13 insertions(+), 13 deletions(-)
-
---- a/drivers/thunderbolt/domain.c
-+++ b/drivers/thunderbolt/domain.c
-@@ -321,7 +321,7 @@ static void tb_domain_release(struct dev
-
- tb_ctl_free(tb->ctl);
- destroy_workqueue(tb->wq);
-- ida_simple_remove(&tb_domain_ida, tb->index);
-+ ida_free(&tb_domain_ida, tb->index);
- mutex_destroy(&tb->lock);
- kfree(tb);
- }
-@@ -389,7 +389,7 @@ struct tb *tb_domain_alloc(struct tb_nhi
- tb->nhi = nhi;
- mutex_init(&tb->lock);
-
-- tb->index = ida_simple_get(&tb_domain_ida, 0, 0, GFP_KERNEL);
-+ tb->index = ida_alloc(&tb_domain_ida, GFP_KERNEL);
- if (tb->index < 0)
- goto err_free;
-
-@@ -413,7 +413,7 @@ struct tb *tb_domain_alloc(struct tb_nhi
- err_destroy_wq:
- destroy_workqueue(tb->wq);
- err_remove_ida:
-- ida_simple_remove(&tb_domain_ida, tb->index);
-+ ida_free(&tb_domain_ida, tb->index);
- err_free:
- kfree(tb);
-
---- a/drivers/thunderbolt/nhi.c
-+++ b/drivers/thunderbolt/nhi.c
-@@ -465,7 +465,7 @@ static int ring_request_msix(struct tb_r
- if (!nhi->pdev->msix_enabled)
- return 0;
-
-- ret = ida_simple_get(&nhi->msix_ida, 0, MSIX_MAX_VECS, GFP_KERNEL);
-+ ret = ida_alloc_max(&nhi->msix_ida, MSIX_MAX_VECS - 1, GFP_KERNEL);
- if (ret < 0)
- return ret;
-
-@@ -485,7 +485,7 @@ static int ring_request_msix(struct tb_r
- return 0;
-
- err_ida_remove:
-- ida_simple_remove(&nhi->msix_ida, ring->vector);
-+ ida_free(&nhi->msix_ida, ring->vector);
-
- return ret;
- }
-@@ -496,7 +496,7 @@ static void ring_release_msix(struct tb_
- return;
-
- free_irq(ring->irq, ring);
-- ida_simple_remove(&ring->nhi->msix_ida, ring->vector);
-+ ida_free(&ring->nhi->msix_ida, ring->vector);
- ring->vector = 0;
- ring->irq = 0;
- }
---- a/drivers/thunderbolt/nvm.c
-+++ b/drivers/thunderbolt/nvm.c
-@@ -330,7 +330,7 @@ struct tb_nvm *tb_nvm_alloc(struct devic
- if (!nvm)
- return ERR_PTR(-ENOMEM);
-
-- ret = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL);
-+ ret = ida_alloc(&nvm_ida, GFP_KERNEL);
- if (ret < 0) {
- kfree(nvm);
- return ERR_PTR(ret);
-@@ -528,7 +528,7 @@ void tb_nvm_free(struct tb_nvm *nvm)
- nvmem_unregister(nvm->non_active);
- nvmem_unregister(nvm->active);
- vfree(nvm->buf);
-- ida_simple_remove(&nvm_ida, nvm->id);
-+ ida_free(&nvm_ida, nvm->id);
- }
- kfree(nvm);
- }
---- a/drivers/thunderbolt/switch.c
-+++ b/drivers/thunderbolt/switch.c
-@@ -777,7 +777,7 @@ static int tb_port_alloc_hopid(struct tb
- if (max_hopid < 0 || max_hopid > port_max_hopid)
- max_hopid = port_max_hopid;
-
-- return ida_simple_get(ida, min_hopid, max_hopid + 1, GFP_KERNEL);
-+ return ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL);
- }
-
- /**
-@@ -815,7 +815,7 @@ int tb_port_alloc_out_hopid(struct tb_po
- */
- void tb_port_release_in_hopid(struct tb_port *port, int hopid)
- {
-- ida_simple_remove(&port->in_hopids, hopid);
-+ ida_free(&port->in_hopids, hopid);
- }
-
- /**
-@@ -825,7 +825,7 @@ void tb_port_release_in_hopid(struct tb_
- */
- void tb_port_release_out_hopid(struct tb_port *port, int hopid)
- {
-- ida_simple_remove(&port->out_hopids, hopid);
-+ ida_free(&port->out_hopids, hopid);
- }
-
- static inline bool tb_switch_is_reachable(const struct tb_switch *parent,
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1005,7 +1005,7 @@ static void tb_service_release(struct de
- struct tb_xdomain *xd = tb_service_parent(svc);
-
- tb_service_debugfs_remove(svc);
-- ida_simple_remove(&xd->service_ids, svc->id);
-+ ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
- }
-@@ -1107,7 +1107,7 @@ static void enumerate_services(struct tb
- break;
- }
-
-- id = ida_simple_get(&xd->service_ids, 0, 0, GFP_KERNEL);
-+ id = ida_alloc(&xd->service_ids, GFP_KERNEL);
- if (id < 0) {
- kfree(svc->key);
- kfree(svc);
+++ /dev/null
-From stable+bounces-289299-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:09 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 09:59:54 -0400
-Subject: thunderbolt: Remove XDomain from the bus without holding tb->lock
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726135955.668115-6-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit a8937f35cf39c39c64325aa84d0463d866850857 ]
-
-Currently we call device_unregister() for services and the XDomain
-itself with tb->lock held. This prevents the service drivers from
-calling any functions that may take it. For this reason separate
-removing the XDomain from the topology data structures (where we need
-the lock) from unregistering the device from the bus (where remove
-callbacks of the drivers are being called).
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/debugfs.c | 2 +
- drivers/thunderbolt/domain.c | 30 +++++++++++++++++++++
- drivers/thunderbolt/icm.c | 5 +++
- drivers/thunderbolt/switch.c | 14 +++++++++
- drivers/thunderbolt/tb.c | 59 ++++++++++++++++++++----------------------
- drivers/thunderbolt/tb.h | 2 +
- drivers/thunderbolt/xdomain.c | 53 +++++++++++++++++++++++--------------
- 7 files changed, 115 insertions(+), 50 deletions(-)
-
---- a/drivers/thunderbolt/debugfs.c
-+++ b/drivers/thunderbolt/debugfs.c
-@@ -956,6 +956,8 @@ static void margining_port_remove(struct
-
- if (!port->usb4)
- return;
-+ if (!port->usb4->margining)
-+ return;
-
- snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
- parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
---- a/drivers/thunderbolt/domain.c
-+++ b/drivers/thunderbolt/domain.c
-@@ -867,6 +867,36 @@ int tb_domain_disconnect_all_paths(struc
- return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain);
- }
-
-+struct unregister_context {
-+ const struct tb *tb;
-+ int n;
-+};
-+
-+static int unregister_unplugged_xdomain(struct device *dev, void *data)
-+{
-+ struct unregister_context *ctx = data;
-+ struct tb_xdomain *xd;
-+
-+ xd = tb_to_xdomain(dev);
-+ if (xd && xd->tb == ctx->tb && xd->is_unplugged) {
-+ tb_xdomain_unregister(xd);
-+ ctx->n++;
-+ }
-+ return 0;
-+}
-+
-+int tb_domain_unregister_unplugged_xdomains(struct tb *tb)
-+{
-+ struct unregister_context ctx;
-+
-+ ctx.tb = tb_domain_get(tb);
-+ ctx.n = 0;
-+ bus_for_each_dev(&tb_bus_type, NULL, &ctx, unregister_unplugged_xdomain);
-+ tb_domain_put(tb);
-+
-+ return ctx.n;
-+}
-+
- int tb_domain_init(void)
- {
- int ret;
---- a/drivers/thunderbolt/icm.c
-+++ b/drivers/thunderbolt/icm.c
-@@ -713,6 +713,7 @@ static void remove_xdomain(struct tb_xdo
-
- sw = tb_to_switch(xd->dev.parent);
- tb_port_at(xd->route, sw)->xdomain = NULL;
-+ xd->is_unplugged = true;
- tb_xdomain_remove(xd);
- }
-
-@@ -1728,6 +1729,8 @@ static void icm_handle_notification(stru
-
- kfree(n->pkg);
- kfree(n);
-+
-+ tb_domain_unregister_unplugged_xdomains(tb);
- }
-
- static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
-@@ -2078,6 +2081,8 @@ static void icm_rescan_work(struct work_
- if (tb->root_switch)
- icm_free_unplugged_children(tb->root_switch);
- mutex_unlock(&tb->lock);
-+
-+ tb_domain_unregister_unplugged_xdomains(tb);
- }
-
- static void icm_complete(struct tb *tb)
---- a/drivers/thunderbolt/switch.c
-+++ b/drivers/thunderbolt/switch.c
-@@ -3556,6 +3556,20 @@ int tb_switch_resume(struct tb_switch *s
- tb_port_warn(port,
- "lost during suspend, disconnecting\n");
- tb_sw_set_unplugged(port->remote->sw);
-+ } else if (port->xdomain) {
-+ /*
-+ * If the user replaced the XDomain with
-+ * another router, this will succeed in
-+ * which case we must remove the XDomain
-+ * before adding the new router.
-+ */
-+ err = tb_cfg_get_upstream_port(sw->tb->ctl,
-+ port->xdomain->route);
-+ if (err > 0) {
-+ tb_port_warn(port,
-+ "XDomain was disconnected\n");
-+ port->xdomain->is_unplugged = true;
-+ }
- }
- }
- }
---- a/drivers/thunderbolt/tb.c
-+++ b/drivers/thunderbolt/tb.c
-@@ -2319,6 +2319,8 @@ put_sw:
- out:
- mutex_unlock(&tb->lock);
-
-+ tb_domain_unregister_unplugged_xdomains(tb);
-+
- pm_runtime_mark_last_busy(&tb->dev);
- pm_runtime_put_autosuspend(&tb->dev);
-
-@@ -2787,6 +2789,24 @@ static void tb_restore_children(struct t
- }
- }
-
-+static void tb_free_unplugged_xdomains(struct tb_switch *sw)
-+{
-+ struct tb_port *port;
-+
-+ tb_switch_for_each_port(sw, port) {
-+ if (tb_is_upstream_port(port))
-+ continue;
-+ if (port->xdomain && port->xdomain->is_unplugged) {
-+ tb_retimer_remove_all(port);
-+ tb_xdomain_remove(port->xdomain);
-+ tb_port_unconfigure_xdomain(port);
-+ port->xdomain = NULL;
-+ } else if (port->remote) {
-+ tb_free_unplugged_xdomains(port->remote->sw);
-+ }
-+ }
-+}
-+
- static int tb_resume_noirq(struct tb *tb)
- {
- struct tb_cm *tcm = tb_priv(tb);
-@@ -2806,6 +2826,7 @@ static int tb_resume_noirq(struct tb *tb
- tb_switch_resume(tb->root_switch, false);
- tb_free_invalid_tunnels(tb);
- tb_free_unplugged_children(tb->root_switch);
-+ tb_free_unplugged_xdomains(tb->root_switch);
- tb_restore_children(tb->root_switch);
-
- /*
-@@ -2848,28 +2869,6 @@ static int tb_resume_noirq(struct tb *tb
- return 0;
- }
-
--static int tb_free_unplugged_xdomains(struct tb_switch *sw)
--{
-- struct tb_port *port;
-- int ret = 0;
--
-- tb_switch_for_each_port(sw, port) {
-- if (tb_is_upstream_port(port))
-- continue;
-- if (port->xdomain && port->xdomain->is_unplugged) {
-- tb_retimer_remove_all(port);
-- tb_xdomain_remove(port->xdomain);
-- tb_port_unconfigure_xdomain(port);
-- port->xdomain = NULL;
-- ret++;
-- } else if (port->remote) {
-- ret += tb_free_unplugged_xdomains(port->remote->sw);
-- }
-- }
--
-- return ret;
--}
--
- static int tb_freeze_noirq(struct tb *tb)
- {
- struct tb_cm *tcm = tb_priv(tb);
-@@ -2889,14 +2888,14 @@ static int tb_thaw_noirq(struct tb *tb)
- static void tb_complete(struct tb *tb)
- {
- /*
-- * Release any unplugged XDomains and if there is a case where
-+ * Unregister unplugged XDomains and if there is a case where
- * another domain is swapped in place of unplugged XDomain we
- * need to run another rescan.
- */
-- mutex_lock(&tb->lock);
-- if (tb_free_unplugged_xdomains(tb->root_switch))
-- tb_scan_switch(tb->root_switch);
-- mutex_unlock(&tb->lock);
-+ if (tb_domain_unregister_unplugged_xdomains(tb)) {
-+ scoped_guard(mutex, &tb->lock)
-+ tb_scan_switch(tb->root_switch);
-+ }
- }
-
- static int tb_runtime_suspend(struct tb *tb)
-@@ -2923,11 +2922,11 @@ static void tb_remove_work(struct work_s
- struct tb *tb = tcm_to_tb(tcm);
-
- mutex_lock(&tb->lock);
-- if (tb->root_switch) {
-+ if (tb->root_switch)
- tb_free_unplugged_children(tb->root_switch);
-- tb_free_unplugged_xdomains(tb->root_switch);
-- }
- mutex_unlock(&tb->lock);
-+
-+ tb_free_unplugged_xdomains(tb->root_switch);
- }
-
- static int tb_runtime_resume(struct tb *tb)
---- a/drivers/thunderbolt/tb.h
-+++ b/drivers/thunderbolt/tb.h
-@@ -752,6 +752,7 @@ int tb_domain_disconnect_xdomain_paths(s
- int transmit_path, int transmit_ring,
- int receive_path, int receive_ring);
- int tb_domain_disconnect_all_paths(struct tb *tb);
-+int tb_domain_unregister_unplugged_xdomains(struct tb *tb);
-
- static inline struct tb *tb_domain_get(struct tb *tb)
- {
-@@ -1200,6 +1201,7 @@ struct tb_xdomain *tb_xdomain_alloc(stru
- const uuid_t *remote_uuid);
- void tb_xdomain_add(struct tb_xdomain *xd);
- void tb_xdomain_remove(struct tb_xdomain *xd);
-+void tb_xdomain_unregister(struct tb_xdomain *xd);
- struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
- u8 depth);
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -2061,41 +2061,54 @@ static int unregister_service(struct dev
- }
-
- /**
-- * tb_xdomain_remove() - Remove XDomain from the bus
-+ * tb_xdomain_remove() - Remove XDomain
- * @xd: XDomain to remove
- *
-- * This will stop all ongoing configuration work and remove the XDomain
-- * along with any services from the bus. When the last reference to @xd
-- * is released the object will be released as well.
-+ * This will stop all ongoing configuration work. XDomain is not removed
-+ * from the bus if it was added. That needs to be done separately by
-+ * calling tb_xdomain_unregister().
-+ *
-+ * Called with @tb->lock held.
- */
- void tb_xdomain_remove(struct tb_xdomain *xd)
- {
- tb_xdomain_debugfs_remove(xd);
--
- stop_handshake(xd);
--
-- device_for_each_child_reverse(&xd->dev, xd, unregister_service);
--
- tb_xdomain_link_exit(xd);
-
-- /*
-- * Undo runtime PM here explicitly because it is possible that
-- * the XDomain was never added to the bus and thus device_del()
-- * is not called for it (device_del() would handle this otherwise).
-- */
-- pm_runtime_disable(&xd->dev);
-- pm_runtime_put_noidle(&xd->dev);
-- pm_runtime_set_suspended(&xd->dev);
--
- if (!device_is_registered(&xd->dev)) {
-+ /*
-+ * Undo runtime PM here explicitly because it is
-+ * possible that the XDomain was never added to the bus
-+ * and thus device_del() is not called for it
-+ * (device_del() would handle this otherwise).
-+ */
-+ pm_runtime_disable(&xd->dev);
-+ pm_runtime_put_noidle(&xd->dev);
-+ pm_runtime_set_suspended(&xd->dev);
- put_device(&xd->dev);
-- } else {
-- dev_info(&xd->dev, "host disconnected\n");
-- device_unregister(&xd->dev);
- }
- }
-
- /**
-+ * tb_xdomain_unregister() - Unregister XDomain
-+ * @xd: XDomain to unregister
-+ *
-+ * This will unregister the XDomain along with any services from the
-+ * bus. When the last reference to @xd is released the object will be
-+ * released as well.
-+ */
-+void tb_xdomain_unregister(struct tb_xdomain *xd)
-+{
-+ lockdep_assert_not_held(&xd->tb->lock);
-+
-+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
-+
-+ dev_info(&xd->dev, "host disconnected\n");
-+ device_unregister(&xd->dev);
-+}
-+
-+/**
- * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain
- * @xd: XDomain connection
- *
+++ /dev/null
-From stable+bounces-289296-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:04 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sun, 26 Jul 2026 09:59:51 -0400
-Subject: thunderbolt: Update property.c function documentation
-To: stable@vger.kernel.org
-Cc: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260726135955.668115-3-sashal@kernel.org>
-
-From: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>
-
-[ Upstream commit d015642ad36d78e6eba12d8ab96cea6fd4602b49 ]
-
-Make property.c function documentation compliant with current kernel-doc
-standards. No functional changes.
-
-Signed-off-by: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/property.c | 38 ++++++++++++++++++++++++++++----------
- 1 file changed, 28 insertions(+), 10 deletions(-)
-
---- a/drivers/thunderbolt/property.c
-+++ b/drivers/thunderbolt/property.c
-@@ -231,11 +231,13 @@ static struct tb_property_dir *__tb_prop
- *
- * This function parses the XDomain properties data block into format that
- * can be traversed using the helper functions provided by this module.
-- * Upon success returns the parsed directory. In case of error returns
-- * %NULL. The resulting &struct tb_property_dir needs to be released by
-+ *
-+ * The resulting &struct tb_property_dir needs to be released by
- * calling tb_property_free_dir() when not needed anymore.
- *
- * The @block is expected to be root directory.
-+ *
-+ * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
- */
- struct tb_property_dir *tb_property_parse_dir(const u32 *block,
- size_t block_len)
-@@ -258,6 +260,8 @@ struct tb_property_dir *tb_property_pars
- *
- * Creates new, empty property directory. If @uuid is %NULL then the
- * directory is assumed to be root directory.
-+ *
-+ * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
- */
- struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
- {
-@@ -501,9 +505,11 @@ static ssize_t __tb_property_format_dir(
- * @block_len: Length of the property block
- *
- * This function formats the directory to the packed format that can be
-- * then send over the thunderbolt fabric to receiving host. Returns %0 in
-- * case of success and negative errno on faulure. Passing %NULL in @block
-- * returns number of entries the block takes.
-+ * then sent over the thunderbolt fabric to receiving host.
-+ *
-+ * Passing %NULL in @block returns number of entries the block takes.
-+ *
-+ * Return: %0 on success, negative errno otherwise.
- */
- ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
- size_t block_len)
-@@ -525,9 +531,9 @@ ssize_t tb_property_format_dir(const str
- * tb_property_copy_dir() - Take a deep copy of directory
- * @dir: Directory to copy
- *
-- * This function takes a deep copy of @dir and returns back the copy. In
-- * case of error returns %NULL. The resulting directory needs to be
-- * released by calling tb_property_free_dir().
-+ * The resulting directory needs to be released by calling tb_property_free_dir().
-+ *
-+ * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
- */
- struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
- {
-@@ -597,6 +603,8 @@ err_free:
- * @parent: Directory to add the property
- * @key: Key for the property
- * @value: Immediate value to store with the property
-+ *
-+ * Return: %0 on success, negative errno otherwise.
- */
- int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
- u32 value)
-@@ -626,6 +634,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_immedi
- * @buflen: Number of bytes in the data buffer
- *
- * Function takes a copy of @buf and adds it to the directory.
-+ *
-+ * Return: %0 on success, negative errno otherwise.
- */
- int tb_property_add_data(struct tb_property_dir *parent, const char *key,
- const void *buf, size_t buflen)
-@@ -662,6 +672,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_data);
- * @text: String to add
- *
- * Function takes a copy of @text and adds it to the directory.
-+ *
-+ * Return: %0 on success, negative errno otherwise.
- */
- int tb_property_add_text(struct tb_property_dir *parent, const char *key,
- const char *text)
-@@ -696,6 +708,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_text);
- * @parent: Directory to add the property
- * @key: Key for the property
- * @dir: Directory to add
-+ *
-+ * Return: %0 on success, negative errno otherwise.
- */
- int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
- struct tb_property_dir *dir)
-@@ -736,8 +750,10 @@ EXPORT_SYMBOL_GPL(tb_property_remove);
- * @key: Key to look for
- * @type: Type of the property
- *
-- * Finds and returns property from the given directory. Does not recurse
-- * into sub-directories. Returns %NULL if the property was not found.
-+ * Finds and returns property from the given directory. Does not
-+ * recurse into sub-directories.
-+ *
-+ * Return: Pointer to &struct tb_property, %NULL if the property was not found.
- */
- struct tb_property *tb_property_find(struct tb_property_dir *dir,
- const char *key, enum tb_property_type type)
-@@ -757,6 +773,8 @@ EXPORT_SYMBOL_GPL(tb_property_find);
- * tb_property_get_next() - Get next property from directory
- * @dir: Directory holding properties
- * @prev: Previous property in the directory (%NULL returns the first)
-+ *
-+ * Return: Pointer to &struct tb_property, %NULL if property was not found.
- */
- struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
- struct tb_property *prev)
sunrpc-return-an-error-from-xdr_buf_to_bvec-on-overflow.patch
mm-sparse-vmemmap-pass-pgmap-argument-to-memory-deactivation-paths.patch
mm-sparse-vmemmap-fix-dax-vmemmap-accounting-with-optimization.patch
-thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch
-thunderbolt-remove-service-debugfs-entries-during-unregister.patch
-thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch
-thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch
net-mana-optimize-irq-affinity-for-low-vcpu-configs.patch
bootconfig-move-xbc_snprint_cmdline-to-lib-bootconfig.c.patch
bootconfig-fix-null-pointer-arithmetic-in-xbc_snprint_cmdline.patch
+++ /dev/null
-From stable+bounces-289101-greg=kroah.com@vger.kernel.org Sat Jul 25 16:12:32 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sat, 25 Jul 2026 10:12:19 -0400
-Subject: thunderbolt: Keep XDomain reference during the lifetime of a service
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260725141222.3814514-1-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit 8b4060998637f06975fceee9b73845d8672d411e ]
-
-This is needed because we release the service ID in tb_service_release()
-and the ID array is owned by the parent XDomain.
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1012,6 +1012,7 @@ static void tb_service_release(struct de
- ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
-+ tb_xdomain_put(xd);
- }
-
- const struct device_type tb_service_type = {
-@@ -1120,7 +1121,7 @@ static void enumerate_services(struct tb
- svc->id = id;
- svc->dev.bus = &tb_bus_type;
- svc->dev.type = &tb_service_type;
-- svc->dev.parent = &xd->dev;
-+ svc->dev.parent = get_device(&xd->dev);
- dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id);
-
- tb_service_debugfs_init(svc);
+++ /dev/null
-From stable+bounces-289104-greg=kroah.com@vger.kernel.org Sat Jul 25 16:13:42 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sat, 25 Jul 2026 10:12:22 -0400
-Subject: thunderbolt: Prevent XDomain delayed work use-after-free on disconnect
-To: stable@vger.kernel.org
-Cc: Michael Bommarito <michael.bommarito@gmail.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260725141222.3814514-4-sashal@kernel.org>
-
-From: Michael Bommarito <michael.bommarito@gmail.com>
-
-[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
-
-tb_xdp_handle_request() runs on system_wq and queues
-xd->state_work via queue_delayed_work() in three request handlers:
-PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
-and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
-xd->properties_changed_work when local properties change.
-
-Concurrently, tb_xdomain_remove() calls stop_handshake() which does
-cancel_delayed_work_sync() on both delayed works. Later,
-tb_xdomain_unregister() calls device_unregister() which eventually
-frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
-tb_xdp_handle_request() in system workqueue") moved the request
-handler off tb->wq, the handler and the remove path are no longer
-serialized. If queue_delayed_work() executes after
-cancel_delayed_work_sync() but before the xdomain is freed, the
-delayed work fires on a freed object.
-
-Add xd->removing that tb_xdomain_remove() sets under xd->lock
-before calling stop_handshake(). Each external queue site holds
-the same lock and checks removing before calling
-queue_delayed_work(). This provides the mutual exclusion needed:
-either the queue site acquires the lock first and queues work that
-the subsequent cancel will see, or the remove path acquires the
-lock first and the queue site observes removing == true and skips
-the queue.
-
-Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
-Cc: stable@vger.kernel.org
-Assisted-by: Claude:claude-opus-4-7
-Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 26 +++++++++++++-------------
- 1 file changed, 13 insertions(+), 13 deletions(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -909,6 +909,19 @@ void tb_unregister_service_driver(struct
- }
- EXPORT_SYMBOL_GPL(tb_unregister_service_driver);
-
-+static int update_xdomain(struct device *dev, void *data)
-+{
-+ struct tb_xdomain *xd;
-+
-+ xd = tb_to_xdomain(dev);
-+ if (xd) {
-+ queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-+ msecs_to_jiffies(50));
-+ }
-+
-+ return 0;
-+}
-+
- static ssize_t key_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
-@@ -2500,19 +2513,6 @@ bool tb_xdomain_handle_request(struct tb
- return ret > 0;
- }
-
--static int update_xdomain(struct device *dev, void *data)
--{
-- struct tb_xdomain *xd;
--
-- xd = tb_to_xdomain(dev);
-- if (xd) {
-- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-- msecs_to_jiffies(50));
-- }
--
-- return 0;
--}
--
- static void update_all_xdomains(void)
- {
- bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain);
+++ /dev/null
-From stable+bounces-289102-greg=kroah.com@vger.kernel.org Sat Jul 25 16:12:32 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sat, 25 Jul 2026 10:12:20 -0400
-Subject: thunderbolt: Remove service debugfs entries during unregister
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260725141222.3814514-2-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit 4d5fc3f4068568dfcb8cbe2852b4adc56394aa26 ]
-
-We add them as part of the register path so to keep it symmetric remove
-them as part of the unregister path. This also removes them even if the
-service itself is not yet released (but is unregistered), thus allowing
-new register with the same service name to happen.
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/xdomain.c | 14 +++++++++++---
- 1 file changed, 11 insertions(+), 3 deletions(-)
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -1008,7 +1008,6 @@ static void tb_service_release(struct de
- struct tb_service *svc = container_of(dev, struct tb_service, dev);
- struct tb_xdomain *xd = tb_service_parent(svc);
-
-- tb_service_debugfs_remove(svc);
- ida_free(&xd->service_ids, svc->id);
- kfree(svc->key);
- kfree(svc);
-@@ -1023,6 +1022,14 @@ const struct device_type tb_service_type
- };
- EXPORT_SYMBOL_GPL(tb_service_type);
-
-+static void __unregister_service(struct device *dev)
-+{
-+ struct tb_service *svc = tb_to_service(dev);
-+
-+ tb_service_debugfs_remove(svc);
-+ device_unregister(&svc->dev);
-+}
-+
- static int remove_missing_service(struct device *dev, void *data)
- {
- struct tb_xdomain *xd = data;
-@@ -1034,7 +1041,7 @@ static int remove_missing_service(struct
-
- if (!tb_property_find(xd->remote_properties, svc->key,
- TB_PROPERTY_TYPE_DIRECTORY))
-- device_unregister(dev);
-+ __unregister_service(dev);
-
- return 0;
- }
-@@ -1127,6 +1134,7 @@ static void enumerate_services(struct tb
- tb_service_debugfs_init(svc);
-
- if (device_register(&svc->dev)) {
-+ tb_service_debugfs_remove(svc);
- put_device(&svc->dev);
- break;
- }
-@@ -2059,7 +2067,7 @@ void tb_xdomain_add(struct tb_xdomain *x
-
- static int unregister_service(struct device *dev, void *data)
- {
-- device_unregister(dev);
-+ __unregister_service(dev);
- return 0;
- }
-
+++ /dev/null
-From stable+bounces-289103-greg=kroah.com@vger.kernel.org Sat Jul 25 16:12:40 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sat, 25 Jul 2026 10:12:21 -0400
-Subject: thunderbolt: Remove XDomain from the bus without holding tb->lock
-To: stable@vger.kernel.org
-Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260725141222.3814514-3-sashal@kernel.org>
-
-From: Mika Westerberg <mika.westerberg@linux.intel.com>
-
-[ Upstream commit a8937f35cf39c39c64325aa84d0463d866850857 ]
-
-Currently we call device_unregister() for services and the XDomain
-itself with tb->lock held. This prevents the service drivers from
-calling any functions that may take it. For this reason separate
-removing the XDomain from the topology data structures (where we need
-the lock) from unregistering the device from the bus (where remove
-callbacks of the drivers are being called).
-
-Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/thunderbolt/debugfs.c | 2 +
- drivers/thunderbolt/domain.c | 30 +++++++++++++++++++++
- drivers/thunderbolt/icm.c | 5 +++
- drivers/thunderbolt/switch.c | 14 +++++++++
- drivers/thunderbolt/tb.c | 59 ++++++++++++++++++++----------------------
- drivers/thunderbolt/tb.h | 2 +
- drivers/thunderbolt/xdomain.c | 53 +++++++++++++++++++++++--------------
- 7 files changed, 115 insertions(+), 50 deletions(-)
-
---- a/drivers/thunderbolt/debugfs.c
-+++ b/drivers/thunderbolt/debugfs.c
-@@ -1786,6 +1786,8 @@ static void margining_port_remove(struct
-
- if (!port->usb4)
- return;
-+ if (!port->usb4->margining)
-+ return;
-
- snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
- parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
---- a/drivers/thunderbolt/domain.c
-+++ b/drivers/thunderbolt/domain.c
-@@ -850,6 +850,36 @@ int tb_domain_disconnect_all_paths(struc
- return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain);
- }
-
-+struct unregister_context {
-+ const struct tb *tb;
-+ int n;
-+};
-+
-+static int unregister_unplugged_xdomain(struct device *dev, void *data)
-+{
-+ struct unregister_context *ctx = data;
-+ struct tb_xdomain *xd;
-+
-+ xd = tb_to_xdomain(dev);
-+ if (xd && xd->tb == ctx->tb && xd->is_unplugged) {
-+ tb_xdomain_unregister(xd);
-+ ctx->n++;
-+ }
-+ return 0;
-+}
-+
-+int tb_domain_unregister_unplugged_xdomains(struct tb *tb)
-+{
-+ struct unregister_context ctx;
-+
-+ ctx.tb = tb_domain_get(tb);
-+ ctx.n = 0;
-+ bus_for_each_dev(&tb_bus_type, NULL, &ctx, unregister_unplugged_xdomain);
-+ tb_domain_put(tb);
-+
-+ return ctx.n;
-+}
-+
- int tb_domain_init(void)
- {
- int ret;
---- a/drivers/thunderbolt/icm.c
-+++ b/drivers/thunderbolt/icm.c
-@@ -738,6 +738,7 @@ static void remove_xdomain(struct tb_xdo
-
- sw = tb_to_switch(xd->dev.parent);
- tb_port_at(xd->route, sw)->xdomain = NULL;
-+ xd->is_unplugged = true;
- tb_xdomain_remove(xd);
- }
-
-@@ -1762,6 +1763,8 @@ static void icm_handle_notification(stru
-
- kfree(n->pkg);
- kfree(n);
-+
-+ tb_domain_unregister_unplugged_xdomains(tb);
- }
-
- static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
-@@ -2112,6 +2115,8 @@ static void icm_rescan_work(struct work_
- if (tb->root_switch)
- icm_free_unplugged_children(tb->root_switch);
- mutex_unlock(&tb->lock);
-+
-+ tb_domain_unregister_unplugged_xdomains(tb);
- }
-
- static void icm_complete(struct tb *tb)
---- a/drivers/thunderbolt/switch.c
-+++ b/drivers/thunderbolt/switch.c
-@@ -3625,6 +3625,20 @@ int tb_switch_resume(struct tb_switch *s
- tb_port_warn(port,
- "lost during suspend, disconnecting\n");
- tb_sw_set_unplugged(port->remote->sw);
-+ } else if (port->xdomain) {
-+ /*
-+ * If the user replaced the XDomain with
-+ * another router, this will succeed in
-+ * which case we must remove the XDomain
-+ * before adding the new router.
-+ */
-+ err = tb_cfg_get_upstream_port(sw->tb->ctl,
-+ port->xdomain->route);
-+ if (err > 0) {
-+ tb_port_warn(port,
-+ "XDomain was disconnected\n");
-+ port->xdomain->is_unplugged = true;
-+ }
- }
- }
- }
---- a/drivers/thunderbolt/tb.c
-+++ b/drivers/thunderbolt/tb.c
-@@ -2524,6 +2524,8 @@ put_sw:
- out:
- mutex_unlock(&tb->lock);
-
-+ tb_domain_unregister_unplugged_xdomains(tb);
-+
- pm_runtime_mark_last_busy(&tb->dev);
- pm_runtime_put_autosuspend(&tb->dev);
-
-@@ -3110,6 +3112,24 @@ static void tb_restore_children(struct t
- }
- }
-
-+static void tb_free_unplugged_xdomains(struct tb_switch *sw)
-+{
-+ struct tb_port *port;
-+
-+ tb_switch_for_each_port(sw, port) {
-+ if (tb_is_upstream_port(port))
-+ continue;
-+ if (port->xdomain && port->xdomain->is_unplugged) {
-+ tb_retimer_remove_all(port);
-+ tb_xdomain_remove(port->xdomain);
-+ tb_port_unconfigure_xdomain(port);
-+ port->xdomain = NULL;
-+ } else if (port->remote) {
-+ tb_free_unplugged_xdomains(port->remote->sw);
-+ }
-+ }
-+}
-+
- static int tb_resume_noirq(struct tb *tb)
- {
- struct tb_cm *tcm = tb_priv(tb);
-@@ -3129,6 +3149,7 @@ static int tb_resume_noirq(struct tb *tb
- tb_switch_resume(tb->root_switch, false);
- tb_free_invalid_tunnels(tb);
- tb_free_unplugged_children(tb->root_switch);
-+ tb_free_unplugged_xdomains(tb->root_switch);
- tb_restore_children(tb->root_switch);
-
- /*
-@@ -3171,28 +3192,6 @@ static int tb_resume_noirq(struct tb *tb
- return 0;
- }
-
--static int tb_free_unplugged_xdomains(struct tb_switch *sw)
--{
-- struct tb_port *port;
-- int ret = 0;
--
-- tb_switch_for_each_port(sw, port) {
-- if (tb_is_upstream_port(port))
-- continue;
-- if (port->xdomain && port->xdomain->is_unplugged) {
-- tb_retimer_remove_all(port);
-- tb_xdomain_remove(port->xdomain);
-- tb_port_unconfigure_xdomain(port);
-- port->xdomain = NULL;
-- ret++;
-- } else if (port->remote) {
-- ret += tb_free_unplugged_xdomains(port->remote->sw);
-- }
-- }
--
-- return ret;
--}
--
- static int tb_freeze_noirq(struct tb *tb)
- {
- struct tb_cm *tcm = tb_priv(tb);
-@@ -3212,14 +3211,14 @@ static int tb_thaw_noirq(struct tb *tb)
- static void tb_complete(struct tb *tb)
- {
- /*
-- * Release any unplugged XDomains and if there is a case where
-+ * Unregister unplugged XDomains and if there is a case where
- * another domain is swapped in place of unplugged XDomain we
- * need to run another rescan.
- */
-- mutex_lock(&tb->lock);
-- if (tb_free_unplugged_xdomains(tb->root_switch))
-- tb_scan_switch(tb->root_switch);
-- mutex_unlock(&tb->lock);
-+ if (tb_domain_unregister_unplugged_xdomains(tb)) {
-+ scoped_guard(mutex, &tb->lock)
-+ tb_scan_switch(tb->root_switch);
-+ }
- }
-
- static int tb_runtime_suspend(struct tb *tb)
-@@ -3246,11 +3245,11 @@ static void tb_remove_work(struct work_s
- struct tb *tb = tcm_to_tb(tcm);
-
- mutex_lock(&tb->lock);
-- if (tb->root_switch) {
-+ if (tb->root_switch)
- tb_free_unplugged_children(tb->root_switch);
-- tb_free_unplugged_xdomains(tb->root_switch);
-- }
- mutex_unlock(&tb->lock);
-+
-+ tb_free_unplugged_xdomains(tb->root_switch);
- }
-
- static int tb_runtime_resume(struct tb *tb)
---- a/drivers/thunderbolt/tb.h
-+++ b/drivers/thunderbolt/tb.h
-@@ -793,6 +793,7 @@ int tb_domain_disconnect_xdomain_paths(s
- int transmit_path, int transmit_ring,
- int receive_path, int receive_ring);
- int tb_domain_disconnect_all_paths(struct tb *tb);
-+int tb_domain_unregister_unplugged_xdomains(struct tb *tb);
-
- static inline struct tb *tb_domain_get(struct tb *tb)
- {
-@@ -1263,6 +1264,7 @@ struct tb_xdomain *tb_xdomain_alloc(stru
- const uuid_t *remote_uuid);
- void tb_xdomain_add(struct tb_xdomain *xd);
- void tb_xdomain_remove(struct tb_xdomain *xd);
-+void tb_xdomain_unregister(struct tb_xdomain *xd);
- struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
- u8 depth);
-
---- a/drivers/thunderbolt/xdomain.c
-+++ b/drivers/thunderbolt/xdomain.c
-@@ -2072,41 +2072,54 @@ static int unregister_service(struct dev
- }
-
- /**
-- * tb_xdomain_remove() - Remove XDomain from the bus
-+ * tb_xdomain_remove() - Remove XDomain
- * @xd: XDomain to remove
- *
-- * This will stop all ongoing configuration work and remove the XDomain
-- * along with any services from the bus. When the last reference to @xd
-- * is released the object will be released as well.
-+ * This will stop all ongoing configuration work. XDomain is not removed
-+ * from the bus if it was added. That needs to be done separately by
-+ * calling tb_xdomain_unregister().
-+ *
-+ * Called with @tb->lock held.
- */
- void tb_xdomain_remove(struct tb_xdomain *xd)
- {
- tb_xdomain_debugfs_remove(xd);
--
- stop_handshake(xd);
--
-- device_for_each_child_reverse(&xd->dev, xd, unregister_service);
--
- tb_xdomain_link_exit(xd);
-
-- /*
-- * Undo runtime PM here explicitly because it is possible that
-- * the XDomain was never added to the bus and thus device_del()
-- * is not called for it (device_del() would handle this otherwise).
-- */
-- pm_runtime_disable(&xd->dev);
-- pm_runtime_put_noidle(&xd->dev);
-- pm_runtime_set_suspended(&xd->dev);
--
- if (!device_is_registered(&xd->dev)) {
-+ /*
-+ * Undo runtime PM here explicitly because it is
-+ * possible that the XDomain was never added to the bus
-+ * and thus device_del() is not called for it
-+ * (device_del() would handle this otherwise).
-+ */
-+ pm_runtime_disable(&xd->dev);
-+ pm_runtime_put_noidle(&xd->dev);
-+ pm_runtime_set_suspended(&xd->dev);
- put_device(&xd->dev);
-- } else {
-- dev_info(&xd->dev, "host disconnected\n");
-- device_unregister(&xd->dev);
- }
- }
-
- /**
-+ * tb_xdomain_unregister() - Unregister XDomain
-+ * @xd: XDomain to unregister
-+ *
-+ * This will unregister the XDomain along with any services from the
-+ * bus. When the last reference to @xd is released the object will be
-+ * released as well.
-+ */
-+void tb_xdomain_unregister(struct tb_xdomain *xd)
-+{
-+ lockdep_assert_not_held(&xd->tb->lock);
-+
-+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
-+
-+ dev_info(&xd->dev, "host disconnected\n");
-+ device_unregister(&xd->dev);
-+}
-+
-+/**
- * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain
- * @xd: XDomain connection
- *