* [PATCH v1 2/2] Bluetooth: btintel_pcie: Fix TOCTOU race in reset path
2026-06-12 1:28 [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback Chandrashekar Devegowda
@ 2026-06-12 1:28 ` Chandrashekar Devegowda
2026-06-12 1:46 ` sashiko-bot
2026-06-12 11:56 ` kernel test robot
2026-06-12 1:41 ` [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback sashiko-bot
` (5 subsequent siblings)
6 siblings, 2 replies; 20+ messages in thread
From: Chandrashekar Devegowda @ 2026-06-12 1:28 UTC (permalink / raw)
To: linux-bluetooth
Cc: linux-pci, bhelgaas, ravishankar.srivatsa, chethan.tumkur.narayan,
Chandrashekar Devegowda
Move the test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS) check
before the SETUP_DONE check to fix a Time-Of-Check to Time-Of-Use
race. Previously, multiple callers could pass the SETUP_DONE check
concurrently and then race on the RECOVERY_IN_PROGRESS flag,
potentially scheduling conflicting removal work.
By reordering the existing atomic guard to execute first, concurrent
reset requests are atomically rejected before any state is inspected.
The RECOVERY_IN_PROGRESS flag is cleared on the early-exit path
(setup not done) to allow future reset attempts.
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
drivers/bluetooth/btintel_pcie.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index d3a03cf96421..f4f400421833 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -2686,11 +2686,15 @@ static void btintel_pcie_reset(struct hci_dev *hdev, u8 reset_type)
data = hci_get_drvdata(hdev);
- if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
+ if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) {
+ bt_dev_warn(hdev, "Reset rejected: recovery already in progress");
return;
+ }
- if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
+ if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags)) {
+ clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
return;
+ }
data->reset_type = (reset_type == 1) ?
BTINTEL_PCIE_IOSF_PRR_PLDR : BTINTEL_PCIE_IOSF_PRR_FLR;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [PATCH v1 2/2] Bluetooth: btintel_pcie: Fix TOCTOU race in reset path
2026-06-12 1:28 ` [PATCH v1 2/2] Bluetooth: btintel_pcie: Fix TOCTOU race in reset path Chandrashekar Devegowda
@ 2026-06-12 1:46 ` sashiko-bot
2026-06-12 11:56 ` kernel test robot
1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-06-12 1:46 UTC (permalink / raw)
To: Chandrashekar Devegowda; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
Pre-existing issues:
- [High] Non-atomic assignment `data->flags = 0;` in `btintel_pcie_reset_work` wipes out flags set concurrently via atomic bitops.
- [High] PCI device reference leak when `schedule_work()` fails due to reset work being disabled during device removal.
- [High] Null pointer dereference of `data->hdev` in suspend/resume handlers if device recovery fails.
- [High] Missing `dma_rmb()` after reading hardware-updated completion ring indices in MSI-X receive/transmit handlers.
--
commit 9ad44c0c51fefc58aa3ecc2842ab5a316618a24a
Author: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
Bluetooth: btintel_pcie: Fix TOCTOU race in reset path
Move the test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS) check
before the SETUP_DONE check to fix a Time-Of-Check to Time-Of-Use
race. Previously, multiple callers could pass the SETUP_DONE check
concurrently and then race on the RECOVERY_IN_PROGRESS flag,
potentially scheduling conflicting removal work.
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index d3a03cf964216..f4f400421833b 100644
> --- a/drivers/bluetooth/btintel_pcie.c
> +++ b/drivers/bluetooth/btintel_pcie.c
> @@ -2686,11 +2686,15 @@ static void btintel_pcie_reset(struct hci_dev *hdev, u8 reset_type)
>
> data = hci_get_drvdata(hdev);
[Severity: High]
This isn't a bug introduced by this patch, but is it possible for
btintel_pcie_reset() to dereference a null hdev pointer here if device
recovery failed previously?
During an FLR reset triggered via btintel_pcie_reset_work(), the code calls
btintel_pcie_release_hdev(data), explicitly unregistering and setting
data->hdev = NULL. If the subsequent hardware reset operations fail (e.g.,
pci_try_reset_function() errors out), the worker exits early and abandons
recovery:
drivers/bluetooth/btintel_pcie.c:btintel_pcie_reset_work() {
...
btintel_pcie_release_hdev(data);
...
err = pci_try_reset_function(pdev);
if (err) {
BT_ERR("Failed resetting the pcie device (%d)", err);
goto out_enable;
}
...
}
This leaves data->hdev permanently as NULL. Later, if the system undergoes a
sleep/wake cycle, the PCI PM callback unconditionally checks for a halted
controller state and invokes btintel_pcie_reset():
drivers/bluetooth/btintel_pcie.c:btintel_pcie_resume() {
...
btintel_pcie_reset(data->hdev, 0);
...
}
Because data->hdev is null, won't btintel_pcie_reset() immediately
dereference the null pointer via hci_get_drvdata(hdev)?
> - if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
> + if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) {
> + bt_dev_warn(hdev, "Reset rejected: recovery already in progress");
> return;
> + }
>
> - if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
> + if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags)) {
> + clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
> return;
> + }
>
> data->reset_type = (reset_type == 1) ?
> BTINTEL_PCIE_IOSF_PRR_PLDR : BTINTEL_PCIE_IOSF_PRR_FLR;
[Severity: High]
This isn't a bug introduced by this patch, but does this function leak a
PCI device reference if schedule_work() fails during driver teardown?
In btintel_pcie_reset(), the driver unconditionally increments the PCI
device reference before calling schedule_work():
drivers/bluetooth/btintel_pcie.c:btintel_pcie_reset() {
...
pci_dev_get(data->pdev);
schedule_work(&data->reset_work);
}
Normally, this reference is balanced by a pci_dev_put() inside the worker
thread. However, during driver teardown, disable_work_sync() is
intentionally called to block further reset executions:
drivers/bluetooth/btintel_pcie.c:btintel_pcie_remove() {
...
disable_work_sync(&data->reset_work);
btintel_pcie_disable_interrupts(data);
...
}
Because disable_work_sync() is called before disabling interrupts, if a
hardware exception interrupt fires within this deterministic window, it will
invoke btintel_pcie_reset(). The schedule_work() call will return false
(since the work is disabled), but the driver fails to check this return
value. Could this permanently leak the pci_dev reference and prevent the
PCI subsystem from fully releasing the device?
[Severity: High]
This isn't a bug introduced by this patch, but can the non-atomic assignment
in btintel_pcie_reset_work() wipe out flags set concurrently via atomic
bitops?
In btintel_pcie_reset_work(), the driver attempts to clear all flags by
executing a direct assignment:
drivers/bluetooth/btintel_pcie.c:btintel_pcie_reset_work() {
...
data->flags = 0;
...
}
However, this occurs shortly after interrupts are unmasked via
btintel_pcie_enable_interrupts(data). If an MSI-X interrupt fires between
the unmasking and the assignment, the IRQ handler might use atomic bitops
(e.g., test_and_set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags) in
btintel_pcie_msix_hw_exp_handler) to record a critical hardware event.
Will the non-atomic store silently overwrite and lose these concurrently set
flags, potentially dropping hardware exceptions or blocking core dumps from
executing?
[Severity: High]
This isn't a bug introduced by this patch, but are memory barriers required
after reading the hardware-updated completion ring indices in the MSI-X
interrupt handlers?
In the MSI-X interrupt handlers, the driver reads the hardware-updated
completion ring head indices to determine how many descriptors are ready:
drivers/bluetooth/btintel_pcie.c:btintel_pcie_msix_rx_handle() {
...
cr_hia = data->ia.cr_hia[BTINTEL_PCIE_RXQ_NUM];
...
}
And similarly in the TX handler:
drivers/bluetooth/btintel_pcie.c:btintel_pcie_msix_tx_handle() {
...
cr_hia = data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
...
}
It then immediately iterates into the ring to process the descriptors from
coherent DMA memory. There is no read memory barrier (dma_rmb()) between
fetching the index and reading the descriptor contents.
On weakly ordered CPU architectures, can these reads be reordered by the CPU
or compiler, leading to the host fetching and processing stale descriptor
data before the hardware's updates to the memory have become visible?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612012832.2395034-1-chandrashekar.devegowda@intel.com?part=2
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH v1 2/2] Bluetooth: btintel_pcie: Fix TOCTOU race in reset path
2026-06-12 1:28 ` [PATCH v1 2/2] Bluetooth: btintel_pcie: Fix TOCTOU race in reset path Chandrashekar Devegowda
2026-06-12 1:46 ` sashiko-bot
@ 2026-06-12 11:56 ` kernel test robot
1 sibling, 0 replies; 20+ messages in thread
From: kernel test robot @ 2026-06-12 11:56 UTC (permalink / raw)
To: Chandrashekar Devegowda, linux-bluetooth
Cc: oe-kbuild-all, linux-pci, bhelgaas, ravishankar.srivatsa,
chethan.tumkur.narayan, Chandrashekar Devegowda
Hi Chandrashekar,
kernel test robot noticed the following build warnings:
[auto build test WARNING on bluetooth-next/master]
[also build test WARNING on next-20260611]
[cannot apply to bluetooth/master linus/master v7.1-rc7]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Chandrashekar-Devegowda/Bluetooth-btintel_pcie-Fix-TOCTOU-race-in-reset-path/20260612-093045
base: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
patch link: https://lore.kernel.org/r/20260612012832.2395034-2-chandrashekar.devegowda%40intel.com
patch subject: [PATCH v1 2/2] Bluetooth: btintel_pcie: Fix TOCTOU race in reset path
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project f43d6834093b19baf79beda8c0337ab020ac5f17)
docutils: docutils (Docutils 0.21.2, Python 3.13.5, on linux)
reproduce: (https://download.01.org/0day-ci/archive/20260612/202606121343.Ix11WMZO-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606121343.Ix11WMZO-lkp@intel.com/
All warnings (new ones prefixed by >>):
WARNING: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode is defined 2 times: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go:364; Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s:234
WARNING: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode_index is defined 2 times: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go:373; Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s:243
WARNING: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled is defined 2 times: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go:636; Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s:252
WARNING: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled_index is defined 2 times: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go:645; Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s:261
Documentation/ABI/stable/sysfs-class-bluetooth:1: ERROR: Unexpected indentation. [docutils]
>> Documentation/ABI/stable/sysfs-class-bluetooth:1: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
Documentation/core-api/kref:328: ./include/linux/kref.h:72: WARNING: Invalid C declaration: Expected end of definition. [error at 96]
int kref_put_mutex (struct kref *kref, void (*release)(struct kref *kref), struct mutex *mutex) __cond_acquires(true# mutex)
------------------------------------------------------------------------------------------------^
Documentation/core-api/kref:328: ./include/linux/kref.h:94: WARNING: Invalid C declaration: Expected end of definition. [error at 92]
int kref_put_lock (struct kref *kref, void (*release)(struct kref *kref), spinlock_t *lock) __cond_acquires(true# lock)
vim +1 Documentation/ABI/stable/sysfs-class-bluetooth
514a8e6d6152a41 Hsin-chen Chuang 2025-01-20 @1 What: /sys/class/bluetooth/hci<index>/reset
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback
2026-06-12 1:28 [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback Chandrashekar Devegowda
2026-06-12 1:28 ` [PATCH v1 2/2] Bluetooth: btintel_pcie: Fix TOCTOU race in reset path Chandrashekar Devegowda
@ 2026-06-12 1:41 ` sashiko-bot
2026-06-12 5:36 ` [v1,1/2] " bluez.test.bot
` (4 subsequent siblings)
6 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-06-12 1:41 UTC (permalink / raw)
To: Chandrashekar Devegowda; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] BTINTEL_PCIE_RECOVERY_IN_PROGRESS flag is never cleared on reset failure, causing a permanent deadlock of the recovery mechanism.
--
commit 01c1befdb681c2e10f544ce7e32e0eba288f218c
Author: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback
This patch adds a reset_type parameter to the Bluetooth hci_dev reset
callback, allowing userspace to specify whether to perform a Function Level
Reset (FLR) or a Product Level Device Reset (PLDR) via sysfs. Internal
callers are updated to default to FLR.
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index 9e39327dc1fe2..d3a03cf964216 100644
> --- a/drivers/bluetooth/btintel_pcie.c
> +++ b/drivers/bluetooth/btintel_pcie.c
[ ... ]
> @@ -2680,7 +2680,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
> pci_unlock_rescan_remove();
> }
>
> -static void btintel_pcie_reset(struct hci_dev *hdev)
> +static void btintel_pcie_reset(struct hci_dev *hdev, u8 reset_type)
> {
> struct btintel_pcie_data *data;
>
> @@ -2692,6 +2692,12 @@ static void btintel_pcie_reset(struct hci_dev *hdev)
> if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
> return;
[Severity: High]
This is a pre-existing issue, but if btintel_pcie_reset_work() fails, does
the BTINTEL_PCIE_RECOVERY_IN_PROGRESS flag remain set permanently?
Looking at btintel_pcie_reset_work() in the same file, if
pci_try_reset_function() or btintel_pcie_setup_hdev() fail, the code jumps
to the out_enable label:
btintel_pcie_reset_work() {
err = pci_try_reset_function(pdev);
if (err) {
BT_ERR("Failed resetting the pcie device (%d)", err);
goto out_enable;
}
...
data->flags = 0;
...
out_enable:
enable_work(&data->coredump_work);
out:
...
}
Because the error paths bypass the data->flags = 0 assignment, the
BTINTEL_PCIE_RECOVERY_IN_PROGRESS flag is never cleared. Would this cause
all future reset attempts (including the sysfs triggers introduced in this
patch) to be rejected indefinitely by the test_and_set_bit() check until
the module is reloaded?
> + data->reset_type = (reset_type == 1) ?
> + BTINTEL_PCIE_IOSF_PRR_PLDR : BTINTEL_PCIE_IOSF_PRR_FLR;
> +
> + bt_dev_info(hdev, "Reset triggered: %s",
> + data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
> +
> pci_dev_get(data->pdev);
> schedule_work(&data->reset_work);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612012832.2395034-1-chandrashekar.devegowda@intel.com?part=1
^ permalink raw reply [flat|nested] 20+ messages in thread* RE: [v1,1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback
2026-06-12 1:28 [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback Chandrashekar Devegowda
2026-06-12 1:28 ` [PATCH v1 2/2] Bluetooth: btintel_pcie: Fix TOCTOU race in reset path Chandrashekar Devegowda
2026-06-12 1:41 ` [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback sashiko-bot
@ 2026-06-12 5:36 ` bluez.test.bot
2026-06-12 14:08 ` [PATCH v1 1/2] " Luiz Augusto von Dentz
` (3 subsequent siblings)
6 siblings, 0 replies; 20+ messages in thread
From: bluez.test.bot @ 2026-06-12 5:36 UTC (permalink / raw)
To: linux-bluetooth, chandrashekar.devegowda
[-- Attachment #1: Type: text/plain, Size: 2794 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1110374
---Test result---
Test Summary:
CheckPatch PASS 3.16 seconds
VerifyFixes PASS 0.07 seconds
VerifySignedoff PASS 0.07 seconds
GitLint PASS 0.40 seconds
SubjectPrefix PASS 0.12 seconds
BuildKernel PASS 26.77 seconds
CheckAllWarning PASS 29.45 seconds
CheckSparse PASS 28.09 seconds
BuildKernel32 PASS 28.65 seconds
TestRunnerSetup PASS 578.26 seconds
TestRunner_l2cap-tester PASS 64.11 seconds
TestRunner_iso-tester PASS 76.12 seconds
TestRunner_bnep-tester PASS 18.76 seconds
TestRunner_mgmt-tester FAIL 212.36 seconds
TestRunner_rfcomm-tester PASS 25.63 seconds
TestRunner_sco-tester PASS 31.99 seconds
TestRunner_ioctl-tester PASS 25.87 seconds
TestRunner_mesh-tester FAIL 25.83 seconds
TestRunner_smp-tester PASS 22.87 seconds
TestRunner_userchan-tester PASS 19.36 seconds
TestRunner_6lowpan-tester FAIL 45.83 seconds
IncrementalBuild PASS 26.05 seconds
Details
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4
Failed Test Cases
Read Exp Feature - Success Failed 0.244 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0
Failed Test Cases
Mesh - Send cancel - 1 Timed out 2.657 seconds
Mesh - Send cancel - 2 Timed out 2.001 seconds
##############################
Test: TestRunner_6lowpan-tester - FAIL
Desc: Run 6lowpan-tester with test-runner
Output:
Total: 8, Passed: 3 (37.5%), Failed: 5, Not Run: 0
Failed Test Cases
Client Connect - Disconnect Timed out 5.240 seconds
Client Recv Dgram - Success Timed out 4.987 seconds
Client Recv Raw - Success Timed out 5.009 seconds
Client Recv IPHC Dgram - Success Timed out 4.986 seconds
Client Recv IPHC Raw - Success Timed out 4.997 seconds
https://github.com/bluez/bluetooth-next/pull/306
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback
2026-06-12 1:28 [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback Chandrashekar Devegowda
` (2 preceding siblings ...)
2026-06-12 5:36 ` [v1,1/2] " bluez.test.bot
@ 2026-06-12 14:08 ` Luiz Augusto von Dentz
2026-06-12 14:28 ` Bjorn Helgaas
2026-07-22 0:14 ` [PATCH v3] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR Chandrashekar Devegowda
` (2 subsequent siblings)
6 siblings, 1 reply; 20+ messages in thread
From: Luiz Augusto von Dentz @ 2026-06-12 14:08 UTC (permalink / raw)
To: Chandrashekar Devegowda
Cc: linux-bluetooth, linux-pci, bhelgaas, ravishankar.srivatsa,
chethan.tumkur.narayan
Hi Chandru,
On Thu, Jun 11, 2026 at 10:29 PM Chandrashekar Devegowda
<chandrashekar.devegowda@intel.com> wrote:
>
> Add a u8 reset_type parameter to the hdev->reset() callback to allow
> userspace to select the reset method via sysfs. Writing 1 to
> /sys/class/bluetooth/hci0/reset triggers a Product Level Device
> Reset (PLDR), while any other value triggers a Function Level Reset
> (FLR).
>
> The reset_type values are:
> 0 - Function Level Reset (FLR)
> 1 - Product Level Device Reset (PLDR)
This should probably be treated as a generic level at the sysfs level,
then each vendor can interpret according to its own needs, for
btintel_pcie that would result in the above levels.
> Internal callers (command timeout, suspend/resume, coredump)
> default to FLR (0).
>
> All drivers implementing the reset callback are updated to accept
> the new parameter:
> - btusb: btusb_intel_reset, btusb_qca_reset, btusb_rtl_reset
> - hci_qca: qca_reset
> - btmtksdio: btmtksdio_reset
> - btmtk: btmtk_reset_sync
> - btnxpuart: nxp_reset
> - btintel_pcie: btintel_pcie_reset (maps to Intel PRR types)
>
> Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
> ---
> Documentation/ABI/stable/sysfs-class-bluetooth | 6 +++++-
> drivers/bluetooth/btintel_pcie.c | 17 +++++++++++------
> drivers/bluetooth/btmtk.c | 6 +++---
> drivers/bluetooth/btmtk.h | 4 ++--
> drivers/bluetooth/btmtksdio.c | 2 +-
> drivers/bluetooth/btnxpuart.c | 2 +-
> drivers/bluetooth/btusb.c | 6 +++---
> drivers/bluetooth/hci_qca.c | 2 +-
> include/net/bluetooth/hci_core.h | 2 +-
> net/bluetooth/hci_core.c | 2 +-
> net/bluetooth/hci_sysfs.c | 10 ++++++++--
> 11 files changed, 37 insertions(+), 22 deletions(-)
>
> diff --git a/Documentation/ABI/stable/sysfs-class-bluetooth b/Documentation/ABI/stable/sysfs-class-bluetooth
> index 36be02471174..8f485fa02a47 100644
> --- a/Documentation/ABI/stable/sysfs-class-bluetooth
> +++ b/Documentation/ABI/stable/sysfs-class-bluetooth
> @@ -3,7 +3,11 @@ Date: 14-Jan-2025
> KernelVersion: 6.13
> Contact: linux-bluetooth@vger.kernel.org
> Description: This write-only attribute allows users to trigger the vendor reset
> - method on the Bluetooth device when arbitrary data is written.
> + method on the Bluetooth device. The value written selects the
> + reset type:
> + 0 - Function Level Reset (FLR)
> + 1 - Product Level Device Reset (PLDR)
> + Writing any value other than 1 defaults to FLR.
> The reset may or may not be done through the device transport
> (e.g., UART/USB), and can also be done through an out-of-band
> approach such as GPIO.
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index 9e39327dc1fe..d3a03cf96421 100644
> --- a/drivers/bluetooth/btintel_pcie.c
> +++ b/drivers/bluetooth/btintel_pcie.c
> @@ -2486,7 +2486,7 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
> }
>
> static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data);
> -static void btintel_pcie_reset(struct hci_dev *hdev);
> +static void btintel_pcie_reset(struct hci_dev *hdev, u8 reset_type);
>
> static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)
> {
> @@ -2680,7 +2680,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
> pci_unlock_rescan_remove();
> }
>
> -static void btintel_pcie_reset(struct hci_dev *hdev)
> +static void btintel_pcie_reset(struct hci_dev *hdev, u8 reset_type)
> {
> struct btintel_pcie_data *data;
>
> @@ -2692,6 +2692,12 @@ static void btintel_pcie_reset(struct hci_dev *hdev)
> if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
> return;
>
> + data->reset_type = (reset_type == 1) ?
> + BTINTEL_PCIE_IOSF_PRR_PLDR : BTINTEL_PCIE_IOSF_PRR_FLR;
> +
> + bt_dev_info(hdev, "Reset triggered: %s",
> + data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
> +
> pci_dev_get(data->pdev);
> schedule_work(&data->reset_work);
> }
> @@ -2729,7 +2735,7 @@ static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
> return;
> }
> btintel_pcie_inc_recovery_count(pdev, &hdev->dev);
> - btintel_pcie_reset(hdev);
> + btintel_pcie_reset(hdev, (code == 0x13) ? 1 : 0);
> }
>
> static bool btintel_pcie_wakeup(struct hci_dev *hdev)
> @@ -3111,8 +3117,7 @@ static int btintel_pcie_resume(struct device *dev)
> if (data->pm_sx_event == PM_EVENT_FREEZE ||
> data->pm_sx_event == PM_EVENT_HIBERNATE) {
> set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
> - data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
> - btintel_pcie_reset(data->hdev);
> + btintel_pcie_reset(data->hdev, 0);
> return 0;
> }
>
> @@ -3143,7 +3148,7 @@ static int btintel_pcie_resume(struct device *dev)
> queue_work(data->coredump_workqueue, &data->coredump_work);
> }
> set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
> - btintel_pcie_reset(data->hdev);
> + btintel_pcie_reset(data->hdev, 0);
> }
> return err;
> }
> diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
> index 02a96342e964..641f62912f63 100644
> --- a/drivers/bluetooth/btmtk.c
> +++ b/drivers/bluetooth/btmtk.c
> @@ -104,7 +104,7 @@ static void btmtk_coredump_notify(struct hci_dev *hdev, int state)
> case HCI_DEVCOREDUMP_ABORT:
> case HCI_DEVCOREDUMP_DONE:
> data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
> - btmtk_reset_sync(hdev);
> + btmtk_reset_sync(hdev, 0);
> break;
> }
> }
> @@ -384,7 +384,7 @@ int btmtk_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
> }
> EXPORT_SYMBOL_GPL(btmtk_set_bdaddr);
>
> -void btmtk_reset_sync(struct hci_dev *hdev)
> +void btmtk_reset_sync(struct hci_dev *hdev, u8 reset_type)
> {
> struct btmtk_data *reset_work = hci_get_priv(hdev);
> int err;
> @@ -1403,7 +1403,7 @@ int btmtk_usb_setup(struct hci_dev *hdev)
> if (err < 0) {
> /* retry once if setup firmware error */
> if (!test_and_set_bit(BTMTK_FIRMWARE_DL_RETRY, &btmtk_data->flags))
> - btmtk_reset_sync(hdev);
> + btmtk_reset_sync(hdev, 0);
> bt_dev_err(hdev, "Failed to set up firmware (%d)", err);
> return err;
> }
> diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
> index c83c24897c95..5cda42444e94 100644
> --- a/drivers/bluetooth/btmtk.h
> +++ b/drivers/bluetooth/btmtk.h
> @@ -196,7 +196,7 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
> int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
> wmt_cmd_sync_func_t wmt_cmd_sync);
>
> -void btmtk_reset_sync(struct hci_dev *hdev);
> +void btmtk_reset_sync(struct hci_dev *hdev, u8 reset_type);
>
> int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
> u32 fw_version);
> @@ -244,7 +244,7 @@ static inline int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
> return -EOPNOTSUPP;
> }
>
> -static inline void btmtk_reset_sync(struct hci_dev *hdev)
> +static inline void btmtk_reset_sync(struct hci_dev *hdev, u8 reset_type)
> {
> }
>
> diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
> index c6f80c419e90..3c4401ce1e00 100644
> --- a/drivers/bluetooth/btmtksdio.c
> +++ b/drivers/bluetooth/btmtksdio.c
> @@ -1269,7 +1269,7 @@ static int btmtksdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
> return 0;
> }
>
> -static void btmtksdio_reset(struct hci_dev *hdev)
> +static void btmtksdio_reset(struct hci_dev *hdev, u8 reset_type)
> {
> struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
> u32 status;
> diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
> index e7036a48ce48..e2416b48116c 100644
> --- a/drivers/bluetooth/btnxpuart.c
> +++ b/drivers/bluetooth/btnxpuart.c
> @@ -1539,7 +1539,7 @@ static bool nxp_wakeup(struct hci_dev *hdev)
> return false;
> }
>
> -static void nxp_reset(struct hci_dev *hdev)
> +static void nxp_reset(struct hci_dev *hdev, u8 reset_type)
> {
> struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
>
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index 08c0a99a62c5..d701c7d82cad 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -1043,7 +1043,7 @@ static void btusb_reset(struct hci_dev *hdev)
> usb_queue_reset_device(data->intf);
> }
>
> -static void btusb_intel_reset(struct hci_dev *hdev)
> +static void btusb_intel_reset(struct hci_dev *hdev, u8 reset_type)
> {
> struct btusb_data *data = hci_get_drvdata(hdev);
> struct gpio_desc *reset_gpio = data->reset_gpio;
> @@ -1121,7 +1121,7 @@ static inline void btusb_rtl_alloc_devcoredump(struct hci_dev *hdev,
> }
> }
>
> -static void btusb_rtl_reset(struct hci_dev *hdev)
> +static void btusb_rtl_reset(struct hci_dev *hdev, u8 reset_type)
> {
> struct btusb_data *data = hci_get_drvdata(hdev);
> struct gpio_desc *reset_gpio = data->reset_gpio;
> @@ -1165,7 +1165,7 @@ static void btusb_rtl_hw_error(struct hci_dev *hdev, u8 code)
> btusb_rtl_alloc_devcoredump(hdev, &hdr, NULL, 0);
> }
>
> -static void btusb_qca_reset(struct hci_dev *hdev)
> +static void btusb_qca_reset(struct hci_dev *hdev, u8 reset_type)
> {
> struct btusb_data *data = hci_get_drvdata(hdev);
> struct gpio_desc *reset_gpio = data->reset_gpio;
> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> index 244447195619..02b4afe77669 100644
> --- a/drivers/bluetooth/hci_qca.c
> +++ b/drivers/bluetooth/hci_qca.c
> @@ -1693,7 +1693,7 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
> clear_bit(QCA_HW_ERROR_EVENT, &qca->flags);
> }
>
> -static void qca_reset(struct hci_dev *hdev)
> +static void qca_reset(struct hci_dev *hdev, u8 reset_type)
> {
> struct hci_uart *hu = hci_get_drvdata(hdev);
> struct qca_data *qca = hu->priv;
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 7e15da47fe3a..00421352fcb5 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -650,7 +650,7 @@ struct hci_dev {
> int (*post_init)(struct hci_dev *hdev);
> int (*set_diag)(struct hci_dev *hdev, bool enable);
> int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
> - void (*reset)(struct hci_dev *hdev);
> + void (*reset)(struct hci_dev *hdev, u8 reset_type);
> bool (*wakeup)(struct hci_dev *hdev);
> int (*set_quality_report)(struct hci_dev *hdev, bool enable);
> int (*get_data_path_id)(struct hci_dev *hdev, __u8 *data_path);
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 5ba9fe8261ec..360b329ae6da 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -1435,7 +1435,7 @@ static void hci_cmd_timeout(struct work_struct *work)
> }
>
> if (hdev->reset)
> - hdev->reset(hdev);
> + hdev->reset(hdev, 0);
>
> atomic_set(&hdev->cmd_cnt, 1);
> queue_work(hdev->workqueue, &hdev->cmd_work);
> diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
> index 8957ce7c21b7..a4fe329158cf 100644
> --- a/net/bluetooth/hci_sysfs.c
> +++ b/net/bluetooth/hci_sysfs.c
> @@ -97,8 +97,14 @@ static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
> {
> struct hci_dev *hdev = to_hci_dev(dev);
>
> - if (hdev->reset)
> - hdev->reset(hdev);
> + if (hdev->reset) {
> + int val;
> +
> + if (kstrtoint(buf, 10, &val) || val != 1)
> + hdev->reset(hdev, 0);
> + else
> + hdev->reset(hdev, 1);
> + }
>
> return count;
> }
> --
> 2.43.0
>
>
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback
2026-06-12 14:08 ` [PATCH v1 1/2] " Luiz Augusto von Dentz
@ 2026-06-12 14:28 ` Bjorn Helgaas
2026-06-12 14:43 ` Luiz Augusto von Dentz
0 siblings, 1 reply; 20+ messages in thread
From: Bjorn Helgaas @ 2026-06-12 14:28 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: Chandrashekar Devegowda, linux-bluetooth, linux-pci, bhelgaas,
ravishankar.srivatsa, chethan.tumkur.narayan
On Fri, Jun 12, 2026 at 11:08:06AM -0300, Luiz Augusto von Dentz wrote:
> On Thu, Jun 11, 2026 at 10:29 PM Chandrashekar Devegowda
> <chandrashekar.devegowda@intel.com> wrote:
> >
> > Add a u8 reset_type parameter to the hdev->reset() callback to allow
> > userspace to select the reset method via sysfs. Writing 1 to
> > /sys/class/bluetooth/hci0/reset triggers a Product Level Device
> > Reset (PLDR), while any other value triggers a Function Level Reset
> > (FLR).
> >
> > The reset_type values are:
> > 0 - Function Level Reset (FLR)
> > 1 - Product Level Device Reset (PLDR)
>
> This should probably be treated as a generic level at the sysfs level,
> then each vendor can interpret according to its own needs, for
> btintel_pcie that would result in the above levels.
Are you suggesting that this should be implemented in the
pci_dev_reset_methods[] framework in drivers/pci/quirks.c, so the
PCI-generic /sys/bus/pci/devices/.../reset interface could do the
PLDR?
That sounds worth exploring, although I really don't know anything
about the unusual multi-function model of some of these devices.
And the /sys/class/bluetooth/hci<index>/reset attribute already exists
and probably couldn't be removed.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback
2026-06-12 14:28 ` Bjorn Helgaas
@ 2026-06-12 14:43 ` Luiz Augusto von Dentz
0 siblings, 0 replies; 20+ messages in thread
From: Luiz Augusto von Dentz @ 2026-06-12 14:43 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: Chandrashekar Devegowda, linux-bluetooth, linux-pci, bhelgaas,
ravishankar.srivatsa, chethan.tumkur.narayan
Hi Bjorn,
On Fri, Jun 12, 2026 at 11:28 AM Bjorn Helgaas <helgaas@kernel.org> wrote:
>
> On Fri, Jun 12, 2026 at 11:08:06AM -0300, Luiz Augusto von Dentz wrote:
> > On Thu, Jun 11, 2026 at 10:29 PM Chandrashekar Devegowda
> > <chandrashekar.devegowda@intel.com> wrote:
> > >
> > > Add a u8 reset_type parameter to the hdev->reset() callback to allow
> > > userspace to select the reset method via sysfs. Writing 1 to
> > > /sys/class/bluetooth/hci0/reset triggers a Product Level Device
> > > Reset (PLDR), while any other value triggers a Function Level Reset
> > > (FLR).
> > >
> > > The reset_type values are:
> > > 0 - Function Level Reset (FLR)
> > > 1 - Product Level Device Reset (PLDR)
> >
> > This should probably be treated as a generic level at the sysfs level,
> > then each vendor can interpret according to its own needs, for
> > btintel_pcie that would result in the above levels.
>
> Are you suggesting that this should be implemented in the
> pci_dev_reset_methods[] framework in drivers/pci/quirks.c, so the
> PCI-generic /sys/bus/pci/devices/.../reset interface could do the
> PLDR?
This is the Bluetooth sysfs reset entry, not the PCI one, which was
created for Bluetooth-specific reset methods like the one above. If
you think the generic level could be applied to the PCI sysfs level
that is perhaps a different suggestion.
> That sounds worth exploring, although I really don't know anything
> about the unusual multi-function model of some of these devices.
> And the /sys/class/bluetooth/hci<index>/reset attribute already exists
> and probably couldn't be removed.
Yeah, I suggested just treating the parameter as generic levels, so we
don't define its domain and leave it for each driver to decide how to
interpret it. The sysfs entry is not transport-specific (pci, usb,
uart), so using pci terminology in the documentation may lead to the
assumption that it is PCI-specific when it is not.
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v3] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR
2026-06-12 1:28 [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback Chandrashekar Devegowda
` (3 preceding siblings ...)
2026-06-12 14:08 ` [PATCH v1 1/2] " Luiz Augusto von Dentz
@ 2026-07-22 0:14 ` Chandrashekar Devegowda
2026-07-22 1:04 ` [v3] " bluez.test.bot
2026-07-22 1:21 ` [PATCH v4] " Chandrashekar Devegowda
2026-07-23 9:44 ` [PATCH v5] Bluetooth: btintel_pcie: Add vendor_reset " Chandrashekar Devegowda
6 siblings, 1 reply; 20+ messages in thread
From: Chandrashekar Devegowda @ 2026-07-22 0:14 UTC (permalink / raw)
To: linux-bluetooth
Cc: luiz.dentz, bhelgaas, ravishankar.srivatsa,
chethan.tumkur.narayan, kiran.k, Chandrashekar Devegowda
Add a read-write sysfs entry at /sys/bus/pci/devices/<BDF>/vendor_rst
to allow userspace to trigger PLDR (Product Level Device Reset).
Reading the attribute displays supported reset types. Writing
integer 0 triggers PLDR. Any other input is rejected with
-EINVAL and a warning log.
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
drivers/bluetooth/btintel_pcie.c | 42 +++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 2e28847263ab..a412ca7ff3ad 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -2781,7 +2781,10 @@ static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
data->reset_type = type;
pci_dev_get(data->pdev);
- schedule_work(&data->reset_work);
+ if (!schedule_work(&data->reset_work)) {
+ pci_dev_put(data->pdev);
+ clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
+ }
}
static void btintel_pcie_hci_reset(struct hci_dev *hdev)
@@ -2791,6 +2794,36 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
}
+static ssize_t vendor_rst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct btintel_pcie_data *data = pci_get_drvdata(pdev);
+
+ if (!data || !data->hdev)
+ return -ENODEV;
+
+ if (kstrtouint(buf, 10, &val) || val != 0) {
+ bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
+ return -EINVAL;
+ }
+
+ bt_dev_info(data->hdev, "PLDR triggered via sysfs");
+ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
+
+ return count;
+}
+
+static ssize_t vendor_rst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0 - PLDR\n");
+}
+
+static DEVICE_ATTR_RW(vendor_rst);
+
static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
{
struct btintel_pcie_dev_recovery *rec;
@@ -3010,6 +3043,11 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
if (err)
goto exit_error;
+ err = device_create_file(&pdev->dev, &dev_attr_vendor_rst);
+ if (err)
+ bt_dev_warn(data->hdev, "Failed to create vendor_rst sysfs (%d)",
+ err);
+
bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
data->cnvr);
return 0;
@@ -3046,6 +3084,8 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
+ device_remove_file(&pdev->dev, &dev_attr_vendor_rst);
+
/* Cancel pending reset work. Skip only when remove() is called from
* within the reset work itself (PLDR device_reprobe path) to avoid
* deadlock. current_work() returns the work_struct of the caller if
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v4] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR
2026-06-12 1:28 [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback Chandrashekar Devegowda
` (4 preceding siblings ...)
2026-07-22 0:14 ` [PATCH v3] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR Chandrashekar Devegowda
@ 2026-07-22 1:21 ` Chandrashekar Devegowda
2026-07-22 4:26 ` [v4] " bluez.test.bot
` (2 more replies)
2026-07-23 9:44 ` [PATCH v5] Bluetooth: btintel_pcie: Add vendor_reset " Chandrashekar Devegowda
6 siblings, 3 replies; 20+ messages in thread
From: Chandrashekar Devegowda @ 2026-07-22 1:21 UTC (permalink / raw)
To: linux-bluetooth
Cc: luiz.dentz, bhelgaas, ravishankar.srivatsa,
chethan.tumkur.narayan, kiran.k, Chandrashekar Devegowda
Add a read-write sysfs entry at /sys/bus/pci/devices/<BDF>/vendor_rst
to allow userspace to trigger PLDR (Product Level Device Reset).
Reading the attribute displays supported reset types. Writing
integer 0 triggers PLDR. Any other input is rejected with
-EINVAL and a warning log.
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
Changes in v4:
- Rebased on latest bluetooth-next (6f55ad8fb0ac) to fix
CI apply failure
Changes in v3:
- Dropped reset_type parameter approach from hdev->reset()
- Directly call btintel_pcie_request_reset() instead of manual
flag manipulation and schedule_work()
- Accept only integer 0 for PLDR trigger
- Handle schedule_work() failure: release pci_dev_get refcount
and clear RECOVERY_IN_PROGRESS flag
- Fix remove ordering: device_remove_file before disable_work_sync
Changes in v2:
- Added reset_type parameter to hdev->reset() callback (1/2)
- vendor_rst sysfs used reset_type to select PLDR (2/2)
Changes in v1:
- Initial vendor_rst PCI sysfs implementation
drivers/bluetooth/btintel_pcie.c | 42 +++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 2e28847263ab..a412ca7ff3ad 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -2781,7 +2781,10 @@ static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
data->reset_type = type;
pci_dev_get(data->pdev);
- schedule_work(&data->reset_work);
+ if (!schedule_work(&data->reset_work)) {
+ pci_dev_put(data->pdev);
+ clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
+ }
}
static void btintel_pcie_hci_reset(struct hci_dev *hdev)
@@ -2791,6 +2794,36 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
}
+static ssize_t vendor_rst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct btintel_pcie_data *data = pci_get_drvdata(pdev);
+
+ if (!data || !data->hdev)
+ return -ENODEV;
+
+ if (kstrtouint(buf, 10, &val) || val != 0) {
+ bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
+ return -EINVAL;
+ }
+
+ bt_dev_info(data->hdev, "PLDR triggered via sysfs");
+ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
+
+ return count;
+}
+
+static ssize_t vendor_rst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0 - PLDR\n");
+}
+
+static DEVICE_ATTR_RW(vendor_rst);
+
static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
{
struct btintel_pcie_dev_recovery *rec;
@@ -3010,6 +3043,11 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
if (err)
goto exit_error;
+ err = device_create_file(&pdev->dev, &dev_attr_vendor_rst);
+ if (err)
+ bt_dev_warn(data->hdev, "Failed to create vendor_rst sysfs (%d)",
+ err);
+
bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
data->cnvr);
return 0;
@@ -3046,6 +3084,8 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
+ device_remove_file(&pdev->dev, &dev_attr_vendor_rst);
+
/* Cancel pending reset work. Skip only when remove() is called from
* within the reset work itself (PLDR device_reprobe path) to avoid
* deadlock. current_work() returns the work_struct of the caller if
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* RE: [v4] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR
2026-07-22 1:21 ` [PATCH v4] " Chandrashekar Devegowda
@ 2026-07-22 4:26 ` bluez.test.bot
2026-07-22 15:43 ` [PATCH v4] " Luiz Augusto von Dentz
2026-07-22 22:19 ` Bjorn Helgaas
2 siblings, 0 replies; 20+ messages in thread
From: bluez.test.bot @ 2026-07-22 4:26 UTC (permalink / raw)
To: linux-bluetooth, chandrashekar.devegowda
[-- Attachment #1: Type: text/plain, Size: 567 bytes --]
This is an automated email and please do not reply to this email.
Dear Submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.
----- Output -----
error: patch failed: drivers/bluetooth/btintel_pcie.c:2781
error: drivers/bluetooth/btintel_pcie.c: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch
Please resolve the issue and submit the patches again.
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v4] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR
2026-07-22 1:21 ` [PATCH v4] " Chandrashekar Devegowda
2026-07-22 4:26 ` [v4] " bluez.test.bot
@ 2026-07-22 15:43 ` Luiz Augusto von Dentz
2026-07-23 9:23 ` Devegowda, Chandrashekar
2026-07-22 22:19 ` Bjorn Helgaas
2 siblings, 1 reply; 20+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-22 15:43 UTC (permalink / raw)
To: Chandrashekar Devegowda
Cc: linux-bluetooth, bhelgaas, ravishankar.srivatsa,
chethan.tumkur.narayan, kiran.k
Hi Chandru,
On Tue, Jul 21, 2026 at 9:22 PM Chandrashekar Devegowda
<chandrashekar.devegowda@intel.com> wrote:
>
> Add a read-write sysfs entry at /sys/bus/pci/devices/<BDF>/vendor_rst
> to allow userspace to trigger PLDR (Product Level Device Reset).
> Reading the attribute displays supported reset types. Writing
> integer 0 triggers PLDR. Any other input is rejected with
> -EINVAL and a warning log.
>
> Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
> ---
> Changes in v4:
> - Rebased on latest bluetooth-next (6f55ad8fb0ac) to fix
> CI apply failure
>
> Changes in v3:
> - Dropped reset_type parameter approach from hdev->reset()
> - Directly call btintel_pcie_request_reset() instead of manual
> flag manipulation and schedule_work()
> - Accept only integer 0 for PLDR trigger
> - Handle schedule_work() failure: release pci_dev_get refcount
> and clear RECOVERY_IN_PROGRESS flag
> - Fix remove ordering: device_remove_file before disable_work_sync
>
> Changes in v2:
> - Added reset_type parameter to hdev->reset() callback (1/2)
> - vendor_rst sysfs used reset_type to select PLDR (2/2)
>
> Changes in v1:
> - Initial vendor_rst PCI sysfs implementation
> drivers/bluetooth/btintel_pcie.c | 42 +++++++++++++++++++++++++++++++-
> 1 file changed, 41 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index 2e28847263ab..a412ca7ff3ad 100644
> --- a/drivers/bluetooth/btintel_pcie.c
> +++ b/drivers/bluetooth/btintel_pcie.c
> @@ -2781,7 +2781,10 @@ static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
> data->reset_type = type;
>
> pci_dev_get(data->pdev);
> - schedule_work(&data->reset_work);
> + if (!schedule_work(&data->reset_work)) {
> + pci_dev_put(data->pdev);
> + clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
> + }
> }
>
> static void btintel_pcie_hci_reset(struct hci_dev *hdev)
> @@ -2791,6 +2794,36 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
> btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
> }
>
> +static ssize_t vendor_rst_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + unsigned int val;
> + struct pci_dev *pdev = to_pci_dev(dev);
> + struct btintel_pcie_data *data = pci_get_drvdata(pdev);
> +
> + if (!data || !data->hdev)
> + return -ENODEV;
> +
> + if (kstrtouint(buf, 10, &val) || val != 0) {
> + bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
> + return -EINVAL;
> + }
> +
> + bt_dev_info(data->hdev, "PLDR triggered via sysfs");
> + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
> +
> + return count;
> +}
> +
> +static ssize_t vendor_rst_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return sysfs_emit(buf, "0 - PLDR\n");
> +}
Ok, so I assume this will be btintel_pcie only right? Not
Bluetooth-wide? In that case you can call it pldr directly since it no
longer need to generic and only applies to btintel_pcie driver.
> +static DEVICE_ATTR_RW(vendor_rst);
> +
> static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
> {
> struct btintel_pcie_dev_recovery *rec;
> @@ -3010,6 +3043,11 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
> if (err)
> goto exit_error;
>
> + err = device_create_file(&pdev->dev, &dev_attr_vendor_rst);
> + if (err)
> + bt_dev_warn(data->hdev, "Failed to create vendor_rst sysfs (%d)",
> + err);
> +
> bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
> data->cnvr);
> return 0;
> @@ -3046,6 +3084,8 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
> disable_work_sync(&data->hwexp_work);
> disable_work_sync(&data->fwtrigger_work);
>
> + device_remove_file(&pdev->dev, &dev_attr_vendor_rst);
> +
> /* Cancel pending reset work. Skip only when remove() is called from
> * within the reset work itself (PLDR device_reprobe path) to avoid
> * deadlock. current_work() returns the work_struct of the caller if
> --
> 2.43.0
>
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 20+ messages in thread* RE: [PATCH v4] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR
2026-07-22 15:43 ` [PATCH v4] " Luiz Augusto von Dentz
@ 2026-07-23 9:23 ` Devegowda, Chandrashekar
0 siblings, 0 replies; 20+ messages in thread
From: Devegowda, Chandrashekar @ 2026-07-23 9:23 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: linux-bluetooth@vger.kernel.org, bhelgaas@google.com,
Srivatsa, Ravishankar, Tumkur Narayan, Chethan, K, Kiran
Hi Luiz
Thanks for the comments.
> -----Original Message-----
> From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
> Sent: Wednesday, July 22, 2026 9:13 PM
> To: Devegowda, Chandrashekar <chandrashekar.devegowda@intel.com>
> Cc: linux-bluetooth@vger.kernel.org; bhelgaas@google.com; Srivatsa,
> Ravishankar <ravishankar.srivatsa@intel.com>; Tumkur Narayan, Chethan
> <chethan.tumkur.narayan@intel.com>; K, Kiran <kiran.k@intel.com>
> Subject: Re: [PATCH v4] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for
> PLDR
>
> Hi Chandru,
>
> On Tue, Jul 21, 2026 at 9:22 PM Chandrashekar Devegowda
> <chandrashekar.devegowda@intel.com> wrote:
> >
> > Add a read-write sysfs entry at /sys/bus/pci/devices/<BDF>/vendor_rst
> > to allow userspace to trigger PLDR (Product Level Device Reset).
> > Reading the attribute displays supported reset types. Writing integer
> > 0 triggers PLDR. Any other input is rejected with -EINVAL and a
> > warning log.
> >
> > Signed-off-by: Chandrashekar Devegowda
> > <chandrashekar.devegowda@intel.com>
> > ---
> > Changes in v4:
> > - Rebased on latest bluetooth-next (6f55ad8fb0ac) to fix
> > CI apply failure
> >
> > Changes in v3:
> > - Dropped reset_type parameter approach from hdev->reset()
> > - Directly call btintel_pcie_request_reset() instead of manual
> > flag manipulation and schedule_work()
> > - Accept only integer 0 for PLDR trigger
> > - Handle schedule_work() failure: release pci_dev_get refcount
> > and clear RECOVERY_IN_PROGRESS flag
> > - Fix remove ordering: device_remove_file before disable_work_sync
> >
> > Changes in v2:
> > - Added reset_type parameter to hdev->reset() callback (1/2)
> > - vendor_rst sysfs used reset_type to select PLDR (2/2)
> >
> > Changes in v1:
> > - Initial vendor_rst PCI sysfs implementation
> > drivers/bluetooth/btintel_pcie.c | 42
> +++++++++++++++++++++++++++++++-
> > 1 file changed, 41 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/bluetooth/btintel_pcie.c
> > b/drivers/bluetooth/btintel_pcie.c
> > index 2e28847263ab..a412ca7ff3ad 100644
> > --- a/drivers/bluetooth/btintel_pcie.c
> > +++ b/drivers/bluetooth/btintel_pcie.c
> > @@ -2781,7 +2781,10 @@ static void btintel_pcie_request_reset(struct
> btintel_pcie_data *data,
> > data->reset_type = type;
> >
> > pci_dev_get(data->pdev);
> > - schedule_work(&data->reset_work);
> > + if (!schedule_work(&data->reset_work)) {
> > + pci_dev_put(data->pdev);
> > + clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
> > + }
> > }
> >
> > static void btintel_pcie_hci_reset(struct hci_dev *hdev) @@ -2791,6
> > +2794,36 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
> > btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
> > }
> >
> > +static ssize_t vendor_rst_store(struct device *dev,
> > + struct device_attribute *attr,
> > + const char *buf, size_t count) {
> > + unsigned int val;
> > + struct pci_dev *pdev = to_pci_dev(dev);
> > + struct btintel_pcie_data *data = pci_get_drvdata(pdev);
> > +
> > + if (!data || !data->hdev)
> > + return -ENODEV;
> > +
> > + if (kstrtouint(buf, 10, &val) || val != 0) {
> > + bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
> > + return -EINVAL;
> > + }
> > +
> > + bt_dev_info(data->hdev, "PLDR triggered via sysfs");
> > + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
> > +
> > + return count;
> > +}
> > +
> > +static ssize_t vendor_rst_show(struct device *dev,
> > + struct device_attribute *attr, char
> > +*buf) {
> > + return sysfs_emit(buf, "0 - PLDR\n"); }
>
> Ok, so I assume this will be btintel_pcie only right? Not Bluetooth-wide? In
> that case you can call it pldr directly since it no longer need to generic and only
> applies to btintel_pcie driver.
>
This is only for btintel_pcie driver currently keeping it as integer so that it can be scaled further for top-silent reset.
> > +static DEVICE_ATTR_RW(vendor_rst);
> > +
> > static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) {
> > struct btintel_pcie_dev_recovery *rec; @@ -3010,6 +3043,11 @@
> > static int btintel_pcie_probe(struct pci_dev *pdev,
> > if (err)
> > goto exit_error;
> >
> > + err = device_create_file(&pdev->dev, &dev_attr_vendor_rst);
> > + if (err)
> > + bt_dev_warn(data->hdev, "Failed to create vendor_rst sysfs (%d)",
> > + err);
> > +
> > bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
> > data->cnvr);
> > return 0;
> > @@ -3046,6 +3084,8 @@ static void btintel_pcie_remove(struct pci_dev
> *pdev)
> > disable_work_sync(&data->hwexp_work);
> > disable_work_sync(&data->fwtrigger_work);
> >
> > + device_remove_file(&pdev->dev, &dev_attr_vendor_rst);
> > +
> > /* Cancel pending reset work. Skip only when remove() is called from
> > * within the reset work itself (PLDR device_reprobe path) to avoid
> > * deadlock. current_work() returns the work_struct of the
> > caller if
> > --
> > 2.43.0
> >
>
>
> --
> Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v4] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR
2026-07-22 1:21 ` [PATCH v4] " Chandrashekar Devegowda
2026-07-22 4:26 ` [v4] " bluez.test.bot
2026-07-22 15:43 ` [PATCH v4] " Luiz Augusto von Dentz
@ 2026-07-22 22:19 ` Bjorn Helgaas
2026-07-23 9:31 ` Devegowda, Chandrashekar
2 siblings, 1 reply; 20+ messages in thread
From: Bjorn Helgaas @ 2026-07-22 22:19 UTC (permalink / raw)
To: Chandrashekar Devegowda
Cc: linux-bluetooth, luiz.dentz, bhelgaas, ravishankar.srivatsa,
chethan.tumkur.narayan, kiran.k, Alex Williamson, linux-pci
[+cc Alex, linux-pci]
On Wed, Jul 22, 2026 at 06:51:57AM +0530, Chandrashekar Devegowda wrote:
> Add a read-write sysfs entry at /sys/bus/pci/devices/<BDF>/vendor_rst
> to allow userspace to trigger PLDR (Product Level Device Reset).
IMO there's no need to abbreviate "reset" as "rst". I'd rather spell
it out as "vendor_reset".
Several other drivers that add files in /sys/bus/pci/devices/ have
documentation in Documentation/ABI/testing/sysfs-bus-pci-drivers-*.
It seems important to mention the fact that IIUC, this PLDR will
release any driver attached to the wifi device that shares hardware
with this bluetooth device, do the reset, and reprobe the wifi.
I guess this can't be integrated into the generic pci_reset_function()
reachable via the sysfs 'reset' attribute because of the fact that it
affects both the BT and the wifi device, which are different PCI
functions. But I cc'd Alex anyway since he's the real guru on resets.
FWIW, I learned a lot about the idiosyncrasies of these connected BT
and wifi devices in this old conversation:
https://lore.kernel.org/all/20250314101613.3682010-1-chandrashekar.devegowda@intel.com
> Reading the attribute displays supported reset types. Writing
> integer 0 triggers PLDR. Any other input is rejected with
> -EINVAL and a warning log.
>
> Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
> ---
> Changes in v4:
> - Rebased on latest bluetooth-next (6f55ad8fb0ac) to fix
> CI apply failure
v4: https://lore.kernel.org/all/20260722012158.813793-1-chandrashekar.devegowda@intel.com/
> Changes in v3:
> - Dropped reset_type parameter approach from hdev->reset()
> - Directly call btintel_pcie_request_reset() instead of manual
> flag manipulation and schedule_work()
> - Accept only integer 0 for PLDR trigger
> - Handle schedule_work() failure: release pci_dev_get refcount
> and clear RECOVERY_IN_PROGRESS flag
> - Fix remove ordering: device_remove_file before disable_work_sync
v3: https://lore.kernel.org/all/20260722001434.804931-1-chandrashekar.devegowda@intel.com/
> Changes in v2:
> - Added reset_type parameter to hdev->reset() callback (1/2)
> - vendor_rst sysfs used reset_type to select PLDR (2/2)
v2: https://lore.kernel.org/all/20260618085016.9173-1-chandrashekar.devegowda@intel.com/
> Changes in v1:
> - Initial vendor_rst PCI sysfs implementation
> drivers/bluetooth/btintel_pcie.c | 42 +++++++++++++++++++++++++++++++-
v1: https://lore.kernel.org/all/20260612012832.2395034-1-chandrashekar.devegowda@intel.com/
> 1 file changed, 41 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index 2e28847263ab..a412ca7ff3ad 100644
> --- a/drivers/bluetooth/btintel_pcie.c
> +++ b/drivers/bluetooth/btintel_pcie.c
> @@ -2781,7 +2781,10 @@ static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
> data->reset_type = type;
>
> pci_dev_get(data->pdev);
> - schedule_work(&data->reset_work);
> + if (!schedule_work(&data->reset_work)) {
> + pci_dev_put(data->pdev);
> + clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
> + }
> }
>
> static void btintel_pcie_hci_reset(struct hci_dev *hdev)
> @@ -2791,6 +2794,36 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
> btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
> }
>
> +static ssize_t vendor_rst_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + unsigned int val;
> + struct pci_dev *pdev = to_pci_dev(dev);
> + struct btintel_pcie_data *data = pci_get_drvdata(pdev);
> +
> + if (!data || !data->hdev)
> + return -ENODEV;
> +
> + if (kstrtouint(buf, 10, &val) || val != 0) {
> + bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
> + return -EINVAL;
> + }
> +
> + bt_dev_info(data->hdev, "PLDR triggered via sysfs");
> + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
> +
> + return count;
> +}
> +
> +static ssize_t vendor_rst_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return sysfs_emit(buf, "0 - PLDR\n");
> +}
> +
> +static DEVICE_ATTR_RW(vendor_rst);
> +
> static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
> {
> struct btintel_pcie_dev_recovery *rec;
> @@ -3010,6 +3043,11 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
> if (err)
> goto exit_error;
>
> + err = device_create_file(&pdev->dev, &dev_attr_vendor_rst);
Can you avoid the manual device_create_file() by taking advantage of
23b6904442d0 ("driver core: add dev_groups to all drivers") and
setting .dev_groups in your struct driver?
If you can, I think it would avoid a race between the device becoming
visible in sysfs (and the uevent announcing that) and the addition of
this sysfs attribute.
> + if (err)
> + bt_dev_warn(data->hdev, "Failed to create vendor_rst sysfs (%d)",
> + err);
> +
> bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
> data->cnvr);
> return 0;
> @@ -3046,6 +3084,8 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
> disable_work_sync(&data->hwexp_work);
> disable_work_sync(&data->fwtrigger_work);
>
> + device_remove_file(&pdev->dev, &dev_attr_vendor_rst);
> +
> /* Cancel pending reset work. Skip only when remove() is called from
> * within the reset work itself (PLDR device_reprobe path) to avoid
> * deadlock. current_work() returns the work_struct of the caller if
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 20+ messages in thread* RE: [PATCH v4] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for PLDR
2026-07-22 22:19 ` Bjorn Helgaas
@ 2026-07-23 9:31 ` Devegowda, Chandrashekar
0 siblings, 0 replies; 20+ messages in thread
From: Devegowda, Chandrashekar @ 2026-07-23 9:31 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: linux-bluetooth@vger.kernel.org, luiz.dentz@gmail.com,
bhelgaas@google.com, Srivatsa, Ravishankar,
Tumkur Narayan, Chethan, K, Kiran, Alex Williamson,
linux-pci@vger.kernel.org
Hi Bjorn,
Thanks for the comments
> -----Original Message-----
> From: Bjorn Helgaas <helgaas@kernel.org>
> Sent: Thursday, July 23, 2026 3:49 AM
> To: Devegowda, Chandrashekar <chandrashekar.devegowda@intel.com>
> Cc: linux-bluetooth@vger.kernel.org; luiz.dentz@gmail.com;
> bhelgaas@google.com; Srivatsa, Ravishankar
> <ravishankar.srivatsa@intel.com>; Tumkur Narayan, Chethan
> <chethan.tumkur.narayan@intel.com>; K, Kiran <kiran.k@intel.com>; Alex
> Williamson <alex@shazbot.org>; linux-pci@vger.kernel.org
> Subject: Re: [PATCH v4] Bluetooth: btintel_pcie: Add vendor_rst PCI sysfs for
> PLDR
>
> [+cc Alex, linux-pci]
>
> On Wed, Jul 22, 2026 at 06:51:57AM +0530, Chandrashekar Devegowda
> wrote:
> > Add a read-write sysfs entry at /sys/bus/pci/devices/<BDF>/vendor_rst
> > to allow userspace to trigger PLDR (Product Level Device Reset).
>
> IMO there's no need to abbreviate "reset" as "rst". I'd rather spell it out as
> "vendor_reset".
>
Ack, will update in v5
> Several other drivers that add files in /sys/bus/pci/devices/ have
> documentation in Documentation/ABI/testing/sysfs-bus-pci-drivers-*.
>
> It seems important to mention the fact that IIUC, this PLDR will release any
> driver attached to the wifi device that shares hardware with this bluetooth
> device, do the reset, and reprobe the wifi.
>
Ack, will update the documentation and mention the impact on wifi.
> I guess this can't be integrated into the generic pci_reset_function() reachable
> via the sysfs 'reset' attribute because of the fact that it affects both the BT and
> the wifi device, which are different PCI functions. But I cc'd Alex anyway since
> he's the real guru on resets.
>
> FWIW, I learned a lot about the idiosyncrasies of these connected BT and wifi
> devices in this old conversation:
> https://lore.kernel.org/all/20250314101613.3682010-1-
> chandrashekar.devegowda@intel.com
>
> > Reading the attribute displays supported reset types. Writing integer
> > 0 triggers PLDR. Any other input is rejected with -EINVAL and a
> > warning log.
> >
> > Signed-off-by: Chandrashekar Devegowda
> > <chandrashekar.devegowda@intel.com>
> > ---
> > Changes in v4:
> > - Rebased on latest bluetooth-next (6f55ad8fb0ac) to fix
> > CI apply failure
>
> v4: https://lore.kernel.org/all/20260722012158.813793-1-
> chandrashekar.devegowda@intel.com/
>
> > Changes in v3:
> > - Dropped reset_type parameter approach from hdev->reset()
> > - Directly call btintel_pcie_request_reset() instead of manual
> > flag manipulation and schedule_work()
> > - Accept only integer 0 for PLDR trigger
> > - Handle schedule_work() failure: release pci_dev_get refcount
> > and clear RECOVERY_IN_PROGRESS flag
> > - Fix remove ordering: device_remove_file before disable_work_sync
>
> v3: https://lore.kernel.org/all/20260722001434.804931-1-
> chandrashekar.devegowda@intel.com/
>
> > Changes in v2:
> > - Added reset_type parameter to hdev->reset() callback (1/2)
> > - vendor_rst sysfs used reset_type to select PLDR (2/2)
>
> v2: https://lore.kernel.org/all/20260618085016.9173-1-
> chandrashekar.devegowda@intel.com/
>
> > Changes in v1:
> > - Initial vendor_rst PCI sysfs implementation
> > drivers/bluetooth/btintel_pcie.c | 42
> +++++++++++++++++++++++++++++++-
>
> v1: https://lore.kernel.org/all/20260612012832.2395034-1-
> chandrashekar.devegowda@intel.com/
>
> > 1 file changed, 41 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/bluetooth/btintel_pcie.c
> > b/drivers/bluetooth/btintel_pcie.c
> > index 2e28847263ab..a412ca7ff3ad 100644
> > --- a/drivers/bluetooth/btintel_pcie.c
> > +++ b/drivers/bluetooth/btintel_pcie.c
> > @@ -2781,7 +2781,10 @@ static void btintel_pcie_request_reset(struct
> btintel_pcie_data *data,
> > data->reset_type = type;
> >
> > pci_dev_get(data->pdev);
> > - schedule_work(&data->reset_work);
> > + if (!schedule_work(&data->reset_work)) {
> > + pci_dev_put(data->pdev);
> > + clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data-
> >flags);
> > + }
> > }
> >
> > static void btintel_pcie_hci_reset(struct hci_dev *hdev) @@ -2791,6
> > +2794,36 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
> > btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); }
> >
> > +static ssize_t vendor_rst_store(struct device *dev,
> > + struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + unsigned int val;
> > + struct pci_dev *pdev = to_pci_dev(dev);
> > + struct btintel_pcie_data *data = pci_get_drvdata(pdev);
> > +
> > + if (!data || !data->hdev)
> > + return -ENODEV;
> > +
> > + if (kstrtouint(buf, 10, &val) || val != 0) {
> > + bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
> > + return -EINVAL;
> > + }
> > +
> > + bt_dev_info(data->hdev, "PLDR triggered via sysfs");
> > + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
> > +
> > + return count;
> > +}
> > +
> > +static ssize_t vendor_rst_show(struct device *dev,
> > + struct device_attribute *attr, char *buf) {
> > + return sysfs_emit(buf, "0 - PLDR\n"); }
> > +
> > +static DEVICE_ATTR_RW(vendor_rst);
> > +
> > static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) {
> > struct btintel_pcie_dev_recovery *rec; @@ -3010,6 +3043,11 @@
> static
> > int btintel_pcie_probe(struct pci_dev *pdev,
> > if (err)
> > goto exit_error;
> >
> > + err = device_create_file(&pdev->dev, &dev_attr_vendor_rst);
>
> Can you avoid the manual device_create_file() by taking advantage of
> 23b6904442d0 ("driver core: add dev_groups to all drivers") and setting
> .dev_groups in your struct driver?
>
> If you can, I think it would avoid a race between the device becoming visible in
> sysfs (and the uevent announcing that) and the addition of this sysfs attribute.
>
Ack, will change to dev_groups in v5
> > + if (err)
> > + bt_dev_warn(data->hdev, "Failed to create vendor_rst sysfs
> (%d)",
> > + err);
> > +
> > bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
> > data->cnvr);
> > return 0;
> > @@ -3046,6 +3084,8 @@ static void btintel_pcie_remove(struct pci_dev
> *pdev)
> > disable_work_sync(&data->hwexp_work);
> > disable_work_sync(&data->fwtrigger_work);
> >
> > + device_remove_file(&pdev->dev, &dev_attr_vendor_rst);
> > +
> > /* Cancel pending reset work. Skip only when remove() is called from
> > * within the reset work itself (PLDR device_reprobe path) to avoid
> > * deadlock. current_work() returns the work_struct of the caller if
> > --
> > 2.43.0
> >
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v5] Bluetooth: btintel_pcie: Add vendor_reset PCI sysfs for PLDR
2026-06-12 1:28 [PATCH v1 1/2] Bluetooth: hci_core: Add reset_type parameter to hdev->reset() callback Chandrashekar Devegowda
` (5 preceding siblings ...)
2026-07-22 1:21 ` [PATCH v4] " Chandrashekar Devegowda
@ 2026-07-23 9:44 ` Chandrashekar Devegowda
2026-07-23 10:06 ` sashiko-bot
2026-07-23 15:12 ` [v5] " bluez.test.bot
6 siblings, 2 replies; 20+ messages in thread
From: Chandrashekar Devegowda @ 2026-07-23 9:44 UTC (permalink / raw)
To: linux-bluetooth
Cc: linux-pci, luiz.dentz, bhelgaas, ravishankar.srivatsa,
chethan.tumkur.narayan, kiran.k, alex, Chandrashekar Devegowda
Add a read-write sysfs entry at /sys/bus/pci/devices/<BDF>/vendor_reset
to allow userspace to trigger PLDR (Product Level Device Reset).
Reading the attribute displays supported reset types. Writing
integer 0 triggers PLDR. Any other input is rejected with
-EINVAL and a warning log.
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
Changes in v5:
- Renamed sysfs from vendor_rst to vendor_reset (reviewer feedback)
- Switched from device_create_file() to driver dev_groups for
automatic race-free sysfs lifecycle management
- Added ABI doc at Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
- Added WiFi impact description to ABI documentation
- Added MAINTAINERS F: entry for ABI doc
Changes in v4:
- Rebased on latest bluetooth-next to fix CI apply failure
- v4: https://lore.kernel.org/all/20260722012158.813793-1-chandrashekar.devegowda@intel.com/
Changes in v3:
- Dropped reset_type parameter approach from hdev->reset()
- Directly call btintel_pcie_request_reset() instead of manual
flag manipulation and schedule_work()
- Accept only integer 0 for PLDR trigger
- Handle schedule_work() failure: release pci_dev_get refcount
and clear RECOVERY_IN_PROGRESS flag
- Fix remove ordering: device_remove_file before disable_work_sync
- v3: https://lore.kernel.org/all/20260722001434.804931-1-chandrashekar.devegowda@intel.com/
Changes in v2:
- Added reset_type parameter to hdev->reset() callback (1/2)
- vendor_rst sysfs used reset_type to select PLDR (2/2)
- v2: https://lore.kernel.org/all/20260618085016.9173-1-chandrashekar.devegowda@intel.com/
Changes in v1:
- Initial implementation
- v1: https://lore.kernel.org/all/20260612012832.2395034-1-chandrashekar.devegowda@intel.com/
.../sysfs-bus-pci-drivers-btintel_pcie | 15 +++++++
MAINTAINERS | 1 +
drivers/bluetooth/btintel_pcie.c | 43 ++++++++++++++++++-
3 files changed, 58 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie b/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
new file mode 100644
index 000000000000..cceec6ac96bc
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
@@ -0,0 +1,15 @@
+What: /sys/bus/pci/devices/<BDF>/vendor_reset
+Date: 22-Jul-2026
+KernelVersion: 6.17
+Contact: linux-bluetooth@vger.kernel.org
+Description: This read-write attribute allows userspace to trigger a
+ Product Level Device Reset (PLDR) on Intel PCIe Bluetooth
+ controllers. Reading the attribute displays the supported
+ reset type. Writing integer 0 triggers PLDR. Any other
+ input is rejected with -EINVAL.
+
+ PLDR resets the entire on-chip platform shared between
+ Bluetooth and WiFi. This means any driver attached to
+ the WiFi device that shares hardware with this Bluetooth
+ device will be released, the platform will be reset, and
+ both the Bluetooth and WiFi devices will be re-probed.
diff --git a/MAINTAINERS b/MAINTAINERS
index eb8cdcc76324..39d390f1ceb4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4625,6 +4625,7 @@ S: Supported
W: http://www.bluez.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
+F: Documentation/ABI/testing/sysfs-bus-pci-drivers-btintel_pcie
F: Documentation/devicetree/bindings/net/bluetooth/
F: drivers/bluetooth/
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 2e28847263ab..b3d3219e4ebd 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -2781,7 +2781,10 @@ static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
data->reset_type = type;
pci_dev_get(data->pdev);
- schedule_work(&data->reset_work);
+ if (!schedule_work(&data->reset_work)) {
+ pci_dev_put(data->pdev);
+ clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
+ }
}
static void btintel_pcie_hci_reset(struct hci_dev *hdev)
@@ -2791,6 +2794,43 @@ static void btintel_pcie_hci_reset(struct hci_dev *hdev)
btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR);
}
+static ssize_t vendor_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct btintel_pcie_data *data = pci_get_drvdata(pdev);
+
+ if (!data || !data->hdev)
+ return -ENODEV;
+
+ if (kstrtouint(buf, 10, &val) || val != 0) {
+ bt_dev_warn(data->hdev, "PLDR rejected: invalid input");
+ return -EINVAL;
+ }
+
+ bt_dev_info(data->hdev, "PLDR triggered via sysfs");
+ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_PLDR);
+
+ return count;
+}
+
+static ssize_t vendor_reset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0 - PLDR\n");
+}
+
+static DEVICE_ATTR_RW(vendor_reset);
+
+static struct attribute *btintel_pcie_attrs[] = {
+ &dev_attr_vendor_reset.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(btintel_pcie);
+
static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
{
struct btintel_pcie_dev_recovery *rec;
@@ -3251,6 +3291,7 @@ static struct pci_driver btintel_pcie_driver = {
.probe = btintel_pcie_probe,
.remove = btintel_pcie_remove,
.driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
+ .driver.dev_groups = btintel_pcie_groups,
#ifdef CONFIG_DEV_COREDUMP
.driver.coredump = btintel_pcie_coredump
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [PATCH v5] Bluetooth: btintel_pcie: Add vendor_reset PCI sysfs for PLDR
2026-07-23 9:44 ` [PATCH v5] Bluetooth: btintel_pcie: Add vendor_reset " Chandrashekar Devegowda
@ 2026-07-23 10:06 ` sashiko-bot
2026-07-23 15:12 ` [v5] " bluez.test.bot
1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-07-23 10:06 UTC (permalink / raw)
To: Chandrashekar Devegowda; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Medium] The newly added `vendor_reset` sysfs attribute is never created due to incorrect driver structure initialization.
Pre-existing issues:
- [High] Triggering the reset work concurrently with device removal causes an AB-BA deadlock.
- [High] Failed resets leave the device permanently unresettable by failing to clear the recovery state flag.
--
--- Patch [1]: [PATCH v5] Bluetooth: btintel_pcie: Add vendor_reset PCI sysfs for PLDR ---
commit ed608b7313d52c701233c9571961267b8d846500
Author: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
Bluetooth: btintel_pcie: Add vendor_reset PCI sysfs for PLDR
Add a read-write sysfs entry at /sys/bus/pci/devices/<BDF>/vendor_reset
to allow userspace to trigger PLDR (Product Level Device Reset).
Reading the attribute displays supported reset types. Writing
integer 0 triggers PLDR. Any other input is rejected with
-EINVAL and a warning log.
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index 2e28847263ab0..b3d3219e4ebd7 100644
> --- a/drivers/bluetooth/btintel_pcie.c
> +++ b/drivers/bluetooth/btintel_pcie.c
[ ... ]
> @@ -2781,7 +2781,10 @@ static void btintel_pcie_request_reset(struct btintel_pcie_data *data,
> data->reset_type = type;
>
> pci_dev_get(data->pdev);
> - schedule_work(&data->reset_work);
> + if (!schedule_work(&data->reset_work)) {
[Severity: High]
This isn't a bug introduced by this patch, but triggering the reset work
concurrently with device removal appears to cause an AB-BA deadlock.
If driver removal (e.g., via sysfs unbind) occurs while
btintel_pcie_reset_work() is executing, the unbind thread holds the
device lock and blocks on disable_work_sync(&data->reset_work):
drivers/bluetooth/btintel_pcie.c:btintel_pcie_remove() {
...
if (current_work() != &data->reset_work)
disable_work_sync(&data->reset_work);
...
}
Meanwhile, the reset work executes btintel_pcie_perform_pldr() and
btintel_pcie_acpi_reset_method(), which unconditionally attempts to acquire
the same lock:
drivers/bluetooth/btintel_pcie.c:btintel_pcie_acpi_reset_method() {
...
pci_dev_lock(data->pdev);
...
}
Does this deadlock the unbind thread and the reset worker thread?
> + pci_dev_put(data->pdev);
> + clear_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags);
[Severity: High]
This is a pre-existing issue, but failed resets seem to leave the device
permanently unresettable by failing to clear the recovery state flag.
When a reset fails (e.g., btintel_pcie_acpi_reset_method() returns an
error), btintel_pcie_perform_pldr() skips device_reprobe():
drivers/bluetooth/btintel_pcie.c:btintel_pcie_perform_pldr() {
...
if (!ret) {
if (device_reprobe(&pdev->dev))
BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev));
}
...
}
The control returns to btintel_pcie_reset_work(), which jumps to the out
label and exits without clearing BTINTEL_PCIE_RECOVERY_IN_PROGRESS:
drivers/bluetooth/btintel_pcie.c:btintel_pcie_reset_work() {
...
if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) {
btintel_pcie_perform_pldr(data);
goto out;
}
...
out:
pci_dev_put(pdev);
pci_unlock_rescan_remove();
}
Since device_reprobe() was not called, the data structure is not
reinitialized. Does this leave BTINTEL_PCIE_RECOVERY_IN_PROGRESS set
indefinitely, causing subsequent calls to btintel_pcie_request_reset()
to return early without doing anything?
> + }
> }
[ ... ]
> @@ -3251,6 +3291,7 @@ static struct pci_driver btintel_pcie_driver = {
> .probe = btintel_pcie_probe,
> .remove = btintel_pcie_remove,
> .driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
> + .driver.dev_groups = btintel_pcie_groups,
[Severity: Medium]
Does this initialization prevent the vendor_reset sysfs attribute from being
created?
This assigns the attribute groups to .driver.dev_groups of the pci_driver
struct. However, the PCI core's __pci_register_driver() explicitly
overwrites the nested .driver.dev_groups with the outer .dev_groups field.
Since .dev_groups is not initialized, it defaults to NULL, which will wipe
the assigned sysfs attributes during registration.
> #ifdef CONFIG_DEV_COREDUMP
> .driver.coredump = btintel_pcie_coredump
> #endif
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723094443.855492-1-chandrashekar.devegowda@intel.com?part=1
^ permalink raw reply [flat|nested] 20+ messages in thread* RE: [v5] Bluetooth: btintel_pcie: Add vendor_reset PCI sysfs for PLDR
2026-07-23 9:44 ` [PATCH v5] Bluetooth: btintel_pcie: Add vendor_reset " Chandrashekar Devegowda
2026-07-23 10:06 ` sashiko-bot
@ 2026-07-23 15:12 ` bluez.test.bot
1 sibling, 0 replies; 20+ messages in thread
From: bluez.test.bot @ 2026-07-23 15:12 UTC (permalink / raw)
To: linux-bluetooth, chandrashekar.devegowda
[-- Attachment #1: Type: text/plain, Size: 567 bytes --]
This is an automated email and please do not reply to this email.
Dear Submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.
----- Output -----
error: patch failed: drivers/bluetooth/btintel_pcie.c:2781
error: drivers/bluetooth/btintel_pcie.c: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch
Please resolve the issue and submit the patches again.
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 20+ messages in thread