From dd3607e436a34239af01ecc8d79aef3c837cfb08 Mon Sep 17 00:00:00 2001 From: Tedd Ho-Jeong An Date: Wed, 4 Nov 2020 21:09:48 -0800 Subject: [PATCH 1/6] workflow: Add workflow files for ci This patch adds workflow files for ci: [sync.yml] - The workflow file for scheduled work - Sync the repo with upstream repo and rebase the workflow branch - Review the patches in the patchwork and creates the PR if needed [ci.yml] - The workflow file for CI tasks - Run CI tests when PR is created Signed-off-by: Tedd Ho-Jeong An --- .github/workflows/ci.yml | 25 ++++++++++++++++++++++ .github/workflows/sync.yml | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/sync.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000..3a2c45c37553c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CI + +on: [pull_request] + +jobs: + ci: + runs-on: ubuntu-latest + name: CI for Pull Request + steps: + - name: Checkout the source code + uses: actions/checkout@v3 + with: + path: src/src + + - name: CI + uses: tedd-an/bzcafe@main + with: + task: ci + base_folder: src + space: kernel + github_token: ${{ secrets.GITHUB_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} + diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000000000..3883d55a23267 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,43 @@ +name: Sync + +on: + schedule: + - cron: "*/30 * * * *" + +jobs: + sync_repo: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: master + + - name: Sync Repo + uses: tedd-an/bzcafe@main + with: + task: sync + upstream_repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git' + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Cleanup PR + uses: tedd-an/bzcafe@main + with: + task: cleanup + github_token: ${{ secrets.ACTION_TOKEN }} + + sync_patchwork: + needs: sync_repo + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Sync Patchwork + uses: tedd-an/bzcafe@main + with: + task: patchwork + space: kernel + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} + From 66dc8a25f6b8cb62b71e77c8073822f7d47d96dd Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 9 Apr 2026 12:57:28 -0400 Subject: [PATCH 2/6] workflows: Make use bluez/action-ci action This replaces the bzcafe action with bluez/action-ci so we can maintain everything in the github bluez organization Signed-off-by: Luiz Augusto von Dentz --- .github/workflows/ci.yml | 2 +- .github/workflows/sync.yml | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a2c45c37553c..58bd27096c3cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: path: src/src - name: CI - uses: tedd-an/bzcafe@main + uses: bluez/action-ci@main with: task: ci base_folder: src diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 3883d55a23267..6fb956309caa0 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -13,14 +13,14 @@ jobs: ref: master - name: Sync Repo - uses: tedd-an/bzcafe@main + uses: bluez/action-ci@main with: task: sync upstream_repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git' github_token: ${{ secrets.GITHUB_TOKEN }} - name: Cleanup PR - uses: tedd-an/bzcafe@main + uses: bluez/action-ci@main with: task: cleanup github_token: ${{ secrets.ACTION_TOKEN }} @@ -32,7 +32,7 @@ jobs: - uses: actions/checkout@v3 - name: Sync Patchwork - uses: tedd-an/bzcafe@main + uses: bluez/action-ci@main with: task: patchwork space: kernel @@ -40,4 +40,3 @@ jobs: email_token: ${{ secrets.EMAIL_TOKEN }} patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} patchwork_user: ${{ secrets.PATCHWORK_USER }} - From ae2a73446ff52e019d539a030fa36ea0a8e3b965 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 9 Apr 2026 13:18:58 -0400 Subject: [PATCH 3/6] workflow/sync: Attempt to sync every 5 minutes This attempts to sync every 5 minutes instead of 30. Signed-off-by: Luiz Augusto von Dentz --- .github/workflows/sync.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 6fb956309caa0..92164bde28cef 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -2,7 +2,7 @@ name: Sync on: schedule: - - cron: "*/30 * * * *" + - cron: "*/5 * * * *" jobs: sync_repo: From bc6b782af7acb9a1f44b28c75e69deecec471ec8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 9 Apr 2026 15:21:22 -0400 Subject: [PATCH 4/6] workflow/sync: Set workflow to use worflow branch bluez/action-ci uses master as default branch for workflow which is incorrect for kernel Signed-off-by: Luiz Augusto von Dentz --- .github/workflows/sync.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 92164bde28cef..5e95af92ab1ab 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -16,6 +16,7 @@ jobs: uses: bluez/action-ci@main with: task: sync + workflow: workflow upstream_repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git' github_token: ${{ secrets.GITHUB_TOKEN }} @@ -35,6 +36,7 @@ jobs: uses: bluez/action-ci@main with: task: patchwork + workflow: workflow space: kernel github_token: ${{ secrets.ACTION_TOKEN }} email_token: ${{ secrets.EMAIL_TOKEN }} From 2f887f7ac82afb8e6bccd7facfef9ce560abb206 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 13 Apr 2026 16:41:33 -0400 Subject: [PATCH 5/6] workflow/ci: Add checks:write permission and explicit reopened trigger The CI action now creates individual GitHub Check Runs per test, which requires 'checks: write' permission on the GITHUB_TOKEN. Also make the pull_request trigger types explicit to include 'reopened', allowing CI to be retriggered by closing and reopening a PR. --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58bd27096c3cf..60d318f805ff1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,12 @@ name: CI -on: [pull_request] +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + checks: write + pull-requests: write jobs: ci: From ecc3a444381766d67fc14d1297f7579a0fbd84e7 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Mon, 24 Aug 2026 21:41:49 +0800 Subject: [PATCH 6/6] Bluetooth: hci_sync: wait for directed advertising completion le_conn_timeout is embedded in struct hci_conn, but queuing the work does not hold a reference to the connection. hci_conn_del() uses cancel_delayed_work() because synchronous cancellation would deadlock when le_conn_timeout() itself calls hci_conn_del() while holding hdev->lock. This leaves the following interleaving possible: CPU 0 CPU 1 le_conn_timeout() hci_conn_del() cancel_delayed_work() = false hci_conn_cleanup() put_device() kfree(conn) hci_conn_failed(conn, ...) The callback then dereferences the released connection. KASAN reported: BUG: KASAN: slab-use-after-free in hci_conn_failed+0x232/0x250 Read of size 8 at addr ffff8881180e8e20 by task kworker/u33:1/111 Workqueue: hci0 le_conn_timeout Call Trace: hci_conn_failed+0x232/0x250 le_conn_timeout+0x23e/0x2c0 process_one_work+0x61b/0xf50 worker_thread+0x45b/0xd10 Remove le_conn_timeout instead of adding another connection reference. Have the directed-advertising enable commands complete normally, then wait for the appropriate LE Connection Complete event with HCI_OP_NOP, matching other Command Complete then later-event sequences such as PAST. The command-sync entry already holds a connection reference until its completion callback returns. LE Set Advertising Enable and LE Set Extended Advertising Enable return Command Complete rather than Command Status, so the later event cannot be attached to the enable command itself without changing generic command completion. Waiting with HCI_OP_NOP keeps that completion path unchanged. Mark directed advertising as an in-flight connection attempt so teardown can cancel the wait. Disable advertising synchronously when that wait fails, and preserve HCI_ERROR_ADVERTISING_TIMEOUT for a software timeout. Clear the instance-0 extended advertising state when advertising is stopped or a connection completes so resuming paused advertising does not restart the directed advertising instance. There is then no delayed callback that can race with connection deletion. Fixes: 980ffc0a2cec ("Bluetooth: Fix LE connection timeout deadlock") Cc: stable@vger.kernel.org Suggested-by: Luiz Augusto von Dentz Signed-off-by: Chengfeng Ye --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_conn.c | 45 -------------------------------- net/bluetooth/hci_event.c | 33 +++++------------------ net/bluetooth/hci_sync.c | 38 ++++++++++++++++++++++----- 4 files changed, 38 insertions(+), 79 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c12cd6873f65e..d7a2df82ff387 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -769,7 +769,6 @@ struct hci_conn { struct delayed_work disc_work; struct delayed_work auto_accept_work; struct delayed_work idle_work; - struct delayed_work le_conn_timeout; struct device dev; struct dentry *debugfs; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 8de98af2fb581..cfcc5d055d5a8 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -697,48 +697,6 @@ static void hci_conn_auto_accept(struct work_struct *work) &conn->dst); } -static void le_disable_advertising(struct hci_dev *hdev) -{ - if (ext_adv_capable(hdev)) { - struct hci_cp_le_set_ext_adv_enable cp; - - cp.enable = 0x00; - cp.num_of_sets = 0x00; - - hci_send_cmd(hdev, HCI_OP_LE_SET_EXT_ADV_ENABLE, sizeof(cp), - &cp); - } else { - u8 enable = 0x00; - hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), - &enable); - } -} - -static void le_conn_timeout(struct work_struct *work) -{ - struct hci_conn *conn = container_of(work, struct hci_conn, - le_conn_timeout.work); - struct hci_dev *hdev = conn->hdev; - - BT_DBG(""); - - /* We could end up here due to having done directed advertising, - * so clean up the state if necessary. This should however only - * happen with broken hardware or if low duty cycle was used - * (which doesn't have a timeout of its own). - */ - if (conn->role == HCI_ROLE_SLAVE) { - /* Disable LE Advertising */ - le_disable_advertising(hdev); - hci_dev_lock(hdev); - hci_conn_failed(conn, HCI_ERROR_ADVERTISING_TIMEOUT); - hci_dev_unlock(hdev); - return; - } - - hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); -} - struct iso_list_data { union { u8 cig; @@ -1131,7 +1089,6 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout); INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept); INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle); - INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout); spin_lock_init(&conn->proto_lock); @@ -1279,8 +1236,6 @@ void hci_conn_del(struct hci_conn *conn) hdev->acl_cnt += conn->sent; break; case LE_LINK: - cancel_delayed_work(&conn->le_conn_timeout); - if (hdev->le_pkts) { if (!hci_conn_num(hdev, LE_LINK) || hdev->le_cnt + conn->sent > hdev->le_pkts) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2f5e21ff97529..b0af5635f831f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1595,22 +1595,10 @@ static u8 hci_cc_le_set_adv_enable(struct hci_dev *hdev, void *data, hci_dev_lock(hdev); - /* If we're doing connection initiation as peripheral. Set a - * timeout in case something goes wrong. - */ - if (*sent) { - struct hci_conn *conn; - + if (*sent) hci_dev_set_flag(hdev, HCI_LE_ADV); - - conn = hci_lookup_le_connect(hdev); - if (conn) - queue_delayed_work(hdev->workqueue, - &conn->le_conn_timeout, - conn->conn_timeout); - } else { + else hci_dev_clear_flag(hdev, HCI_LE_ADV); - } hci_dev_unlock(hdev); @@ -1642,20 +1630,12 @@ static u8 hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev, void *data, adv = hci_find_adv_instance(hdev, set->handle); if (cp->enable) { - struct hci_conn *conn; - hci_dev_set_flag(hdev, HCI_LE_ADV); if (adv) adv->enabled = true; else if (!set->handle) hci_dev_set_flag(hdev, HCI_LE_ADV_0); - - conn = hci_lookup_le_connect(hdev); - if (conn) - queue_delayed_work(hdev->workqueue, - &conn->le_conn_timeout, - conn->conn_timeout); } else { if (cp->num_of_sets) { if (adv) @@ -1676,6 +1656,7 @@ static u8 hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev, void *data, list_for_each_entry_safe(adv, n, &hdev->adv_instances, list) adv->enabled = false; + hci_dev_clear_flag(hdev, HCI_LE_ADV_0); } hci_dev_clear_flag(hdev, HCI_LE_ADV); @@ -5764,10 +5745,12 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, hci_store_wake_reason(hdev, bdaddr, bdaddr_type); /* Advertising stops when a connection is created. On a failed - * connection it keeps running, so leave the state bit alone. + * connection it keeps running, so leave the state bits alone. */ - if (!status) + if (!status) { hci_dev_clear_flag(hdev, HCI_LE_ADV); + hci_dev_clear_flag(hdev, HCI_LE_ADV_0); + } /* Check for existing connection: * @@ -5814,8 +5797,6 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, &conn->init_addr_type); } } - } else { - cancel_delayed_work(&conn->le_conn_timeout); } /* The HCI_LE_Connection_Complete event is only sent once per connection. diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 7150037a864b4..7e092e827ab82 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6639,6 +6639,19 @@ static bool conn_use_rpa(struct hci_conn *conn) return hci_dev_test_flag(hdev, HCI_PRIVACY); } +static int hci_le_wait_directed_adv_complete_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + /* LE Set (Extended) Advertising Enable returns a command complete + * event, so it cannot wait for LE Connection Complete. + */ + return __hci_cmd_sync_status_sk(hdev, HCI_OP_NOP, 0, NULL, + use_enhanced_conn_complete(hdev) ? + HCI_EV_LE_ENHANCED_CONN_COMPLETE : + HCI_EV_LE_CONN_COMPLETE, + conn->conn_timeout, NULL); +} + static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev, struct hci_conn *conn) { @@ -6704,7 +6717,11 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev, return err; } - return hci_enable_ext_advertising_sync(hdev, 0x00); + err = hci_enable_ext_advertising_sync(hdev, 0x00); + if (err) + return err; + + return hci_le_wait_directed_adv_complete_sync(hdev, conn); } static int hci_le_directed_advertising_sync(struct hci_dev *hdev, @@ -6755,8 +6772,13 @@ static int hci_le_directed_advertising_sync(struct hci_dev *hdev, enable = 0x01; - return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_ENABLE, - sizeof(enable), &enable, HCI_CMD_TIMEOUT); + status = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_ENABLE, + sizeof(enable), &enable, + HCI_CMD_TIMEOUT); + if (status) + return status; + + return hci_le_wait_directed_adv_complete_sync(hdev, conn); } static void set_ext_conn_params(struct hci_conn *conn, @@ -6860,6 +6882,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) /* Pause advertising while doing directed advertising. */ hci_pause_advertising_sync(hdev); + set_bit(HCI_CONN_CREATE, &conn->flags); err = hci_le_directed_advertising_sync(hdev, conn); goto done; } @@ -6946,7 +6969,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) done: clear_bit(HCI_CONN_CREATE, &conn->flags); - if (err == -ETIMEDOUT) + if (err && conn->role == HCI_ROLE_SLAVE) + hci_disable_advertising_sync(hdev); + else if (err == -ETIMEDOUT) hci_le_connect_cancel_sync(hdev, conn, 0x00); /* Re-enable advertising after the connection attempt is finished. */ @@ -7283,9 +7308,8 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) if (conn != hci_lookup_le_connect(hdev)) goto unlock; - /* Flush to make sure we send create conn cancel command if needed */ - flush_delayed_work(&conn->le_conn_timeout); - hci_conn_failed(conn, bt_status(err)); + hci_conn_failed(conn, conn->role == HCI_ROLE_SLAVE && err == -ETIMEDOUT ? + HCI_ERROR_ADVERTISING_TIMEOUT : bt_status(err)); unlock: hci_dev_unlock(hdev);