From 943a6c140cece3b1d642ac756868f40b9f65f74e Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 16 Mar 2022 10:39:08 -0700 Subject: [PATCH 01/11] Workflow's for syncing with upstream, build, unit test, and test-runner --- .github/workflows/ci.yml | 236 +++++++++++++++++++++++++++ .github/workflows/pw-to-pr-email.txt | 16 ++ .github/workflows/pw-to-pr.json | 14 ++ .github/workflows/schedule_work.yml | 43 +++++ 4 files changed, 309 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pw-to-pr-email.txt create mode 100644 .github/workflows/pw-to-pr.json create mode 100644 .github/workflows/schedule_work.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..8e140ad8c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,236 @@ +name: IWD CI + +# +# The basic flow of the CI is as follows: +# +# 1. Get all inputs, or default values, and set as 'setup' job output +# 2. Find any cached binaries (hostapd, wpa_supplicant, kernel etc) +# 3. Checkout all dependent repositories +# 4. Tar all local files. This is an unfortunate requirement since github jobs +# cannot share local files. Since there are multiple CI's acting on the same +# set of repositories it makes more sense to retain these and re-download +# them for each CI job. +# 5. Run each CI, currently 'main' and 'musl'. +# * 'main' is the default IWD CI which runs all the build steps as well +# as test-runner +# * 'musl' uses an alpine docker image to test the build on musl-libc +# +# Both CI's use the 'iwd-ci-v2' repo which calls into 'ci-docker'. The +# 'ci-docker' action essentially re-implements the native Github docker +# action but allows arbitrary options to be passed in (e.g. privileged or +# mounting non-standard directories) +# + +on: + pull_request: + workflow_dispatch: + inputs: + tests: + description: Tests to run (comma separated, no spaces) + default: all + kernel: + description: Kernel version + default: '5.16' + hostapd_version: + description: Hostapd and wpa_supplicant version + default: '2_10' + ell_ref: + description: ELL reference + default: refs/heads/workflow + + repository_dispatch: + types: [ell-dispatch] + +jobs: + setup: + runs-on: ubuntu-22.04 + outputs: + tests: ${{ steps.inputs.outputs.tests }} + kernel: ${{ steps.inputs.outputs.kernel }} + hostapd_version: ${{ steps.inputs.outputs.hostapd_version }} + ell_ref: ${{ steps.inputs.outputs.ell_ref }} + repository: ${{ steps.inputs.outputs.repository }} + ref_branch: ${{ steps.inputs.outputs.ref_branch }} + steps: + # + # This makes CI inputs consistent depending on how the CI was invoked: + # * pull_request trigger won't have any inputs, so these need to be set + # to default values. + # * workflow_dispatch sets all inputs from the user input + # * repository_dispatch sets all inputs based on the JSON payload of + # the request. + # + - name: Setup Inputs + id: inputs + run: | + if [ ${{ github.event_name }} == 'workflow_dispatch' ] + then + TESTS=${{ github.event.inputs.tests }} + KERNEL=${{ github.event.inputs.kernel }} + HOSTAPD_VERSION=${{ github.event.inputs.hostapd_version }} + ELL_REF=${{ github.event.inputs.ell_ref }} + REF="$GITHUB_REF" + REPO="$GITHUB_REPOSITORY" + elif [ ${{ github.event_name }} == 'repository_dispatch' ] + then + TESTS=all + KERNEL=5.16 + HOSTAPD_VERSION=2_10 + ELL_REF=${{ github.event.client_payload.ref }} + REF=$ELL_REF + REPO=${{ github.event.client_payload.repo }} + else + TESTS=all + KERNEL=5.16 + HOSTAPD_VERSION=2_10 + ELL_REF="refs/heads/workflow" + REF="$GITHUB_REF" + REPO="$GITHUB_REPOSITORY" + fi + + # + # Now that the inputs are sorted, set the output of this step to these + # values so future jobs can refer to them. + # + echo ::set-output name=tests::$TESTS + echo ::set-output name=kernel::$KERNEL + echo ::set-output name=hostapd_version::$HOSTAPD_VERSION + echo ::set-output name=ell_ref::$ELL_REF + echo ::set-output name=repository::$REPO + echo ::set-output name=ref_branch::$REF + + - name: Cache UML Kernel + id: cache-uml-kernel + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/cache/um-linux-${{ steps.inputs.outputs.kernel }} + key: um-linux-${{ steps.inputs.outputs.kernel }}_ubuntu22 + + - name: Cache Hostapd + id: cache-hostapd + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/cache/hostapd_${{ steps.inputs.outputs.hostapd_version }} + ${{ github.workspace }}/cache/hostapd_cli_${{ steps.inputs.outputs.hostapd_version }} + key: hostapd_${{ steps.inputs.outputs.hostapd_version }}_ssl3 + + - name: Cache WpaSupplicant + id: cache-wpas + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/cache/wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }} + ${{ github.workspace }}/cache/wpa_cli_${{ steps.inputs.outputs.hostapd_version }} + key: wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }}_ssl3 + + - name: Checkout IWD + uses: actions/checkout@v3 + with: + path: iwd + repository: IWDTestBot/iwd + token: ${{ secrets.ACTION_TOKEN }} + + - name: Checkout ELL + uses: actions/checkout@v3 + with: + path: ell + repository: IWDTestBot/ell + ref: ${{ steps.inputs.outputs.ell_ref }} + + - name: Checkout CiBase + uses: actions/checkout@v3 + with: + repository: IWDTestBot/cibase + path: cibase + + - name: Checkout CI + uses: actions/checkout@v3 + with: + repository: IWDTestBot/iwd-ci-v2 + path: iwd-ci + + - name: Tar files + run: | + tar -cvf archive.tar \ + ${{ github.workspace }}/cache/um-linux-${{ steps.inputs.outputs.kernel }} \ + ${{ github.workspace }}/cache/hostapd_${{ steps.inputs.outputs.hostapd_version }} \ + ${{ github.workspace }}/cache/hostapd_cli_${{ steps.inputs.outputs.hostapd_version }} \ + ${{ github.workspace }}/cache/wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }} \ + ${{ github.workspace }}/cache/wpa_cli_${{ steps.inputs.outputs.hostapd_version }} \ + iwd \ + ell \ + cibase \ + iwd-ci \ + cache + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: iwd-artifacts + path: | + archive.tar + + iwd-alpine-ci: + runs-on: ubuntu-22.04 + needs: setup + steps: + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + name: iwd-artifacts + + - name: Untar + run: tar -xf archive.tar + + - name: Modprobe pkcs8_key_parser + run: | + sudo modprobe pkcs8_key_parser + + - name: Alpine CI + uses: IWDTestBot/iwd-ci-v2@master + with: + ref_branch: ${{ needs.setup.outputs.ref_branch }} + repository: ${{ needs.setup.outputs.repository }} + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + ci: musl + + iwd-ci: + runs-on: ubuntu-22.04 + needs: setup + steps: + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + name: iwd-artifacts + + - name: Untar + run: tar -xf archive.tar + + - name: Modprobe pkcs8_key_parser + run: | + sudo modprobe pkcs8_key_parser + echo ${{ needs.setup.outputs.ref_branch }} + echo ${{ needs.setup.outputs.repository }} + + - name: Run CI + uses: IWDTestBot/iwd-ci-v2@master + with: + ref_branch: ${{ needs.setup.outputs.ref_branch }} + repository: ${{ needs.setup.outputs.repository }} + tests: ${{ needs.setup.outputs.tests }} + kernel: ${{ needs.setup.outputs.kernel }} + hostapd_version: ${{ needs.setup.outputs.hostapd_version }} + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + ci: main + + - name: Upload Logs + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-runner-logs + path: ${{ github.workspace }}/log diff --git a/.github/workflows/pw-to-pr-email.txt b/.github/workflows/pw-to-pr-email.txt new file mode 100644 index 000000000..0ad6d7659 --- /dev/null +++ b/.github/workflows/pw-to-pr-email.txt @@ -0,0 +1,16 @@ +This is an automated email and please do not reply to this email. + +Dear Submitter, + +Thank you for submitting the patches to the IWD mailing list. +While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository. + +----- Output ----- +{} + +Please resolve the issue and submit the patches again. + + +--- +Regards, +IWDTestBot diff --git a/.github/workflows/pw-to-pr.json b/.github/workflows/pw-to-pr.json new file mode 100644 index 000000000..b4491413c --- /dev/null +++ b/.github/workflows/pw-to-pr.json @@ -0,0 +1,14 @@ +{ + "email": { + "enable": true, + "server": "smtp.gmail.com", + "port": 587, + "user": "iwd.ci.bot@gmail.com", + "starttls": true, + "default-to": "prestwoj@gmail.com", + "only-maintainers": false, + "maintainers": [ + "prestwoj@gmail.com" + ] + } +} diff --git a/.github/workflows/schedule_work.yml b/.github/workflows/schedule_work.yml new file mode 100644 index 000000000..cfc14fba9 --- /dev/null +++ b/.github/workflows/schedule_work.yml @@ -0,0 +1,43 @@ +name: Sync Upstream +on: + schedule: + - cron: "*/15 * * * *" + workflow_dispatch: + +jobs: + repo-sync: + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v2 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Manage Repo + uses: IWDTestBot/action-manage-repo@master + with: + src_repo: "https://git.kernel.org/pub/scm/network/wireless/iwd.git" + src_branch: "master" + dest_branch: "master" + workflow_branch: "workflow" + github_token: ${{ secrets.GITHUB_TOKEN }} + + create_pr: + needs: repo-sync + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Patchwork to PR + uses: IWDTestBot/action-patchwork-to-pr@master + with: + pw_key_str: "user" + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + config: https://raw.githubusercontent.com/IWDTestBot/iwd/workflow/.github/workflows/pw-to-pr.json + patchwork_id: "408" + email_message: https://raw.githubusercontent.com/IWDTestBot/iwd/workflow/.github/workflows/pw-to-pr-email.txt From c152e440ec1ec9319ca640afb20d1b344c305687 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 24 Jun 2022 15:27:03 -0700 Subject: [PATCH 02/11] workflow: use newer commit for hostapd --- .github/workflows/ci.yml | 61 +++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e140ad8c..4bf5b1347 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ on: default: '5.16' hostapd_version: description: Hostapd and wpa_supplicant version - default: '2_10' + default: '09a281e52a25b5461c4b08d261f093181266a554' ell_ref: description: ELL reference default: refs/heads/workflow @@ -75,14 +75,14 @@ jobs: then TESTS=all KERNEL=5.16 - HOSTAPD_VERSION=2_10 + HOSTAPD_VERSION=09a281e52a25b5461c4b08d261f093181266a554 ELL_REF=${{ github.event.client_payload.ref }} REF=$ELL_REF REPO=${{ github.event.client_payload.repo }} else TESTS=all KERNEL=5.16 - HOSTAPD_VERSION=2_10 + HOSTAPD_VERSION=09a281e52a25b5461c4b08d261f093181266a554 ELL_REF="refs/heads/workflow" REF="$GITHUB_REF" REPO="$GITHUB_REPOSITORY" @@ -152,17 +152,25 @@ jobs: - name: Tar files run: | - tar -cvf archive.tar \ - ${{ github.workspace }}/cache/um-linux-${{ steps.inputs.outputs.kernel }} \ - ${{ github.workspace }}/cache/hostapd_${{ steps.inputs.outputs.hostapd_version }} \ - ${{ github.workspace }}/cache/hostapd_cli_${{ steps.inputs.outputs.hostapd_version }} \ - ${{ github.workspace }}/cache/wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }} \ - ${{ github.workspace }}/cache/wpa_cli_${{ steps.inputs.outputs.hostapd_version }} \ - iwd \ - ell \ - cibase \ - iwd-ci \ - cache + FILES="iwd ell cibase iwd-ci cache" + + if [ "${{ steps.cache-uml-kernel.outputs.cache-hit }}" == 'true' ] + then + FILES+=" ${{ github.workspace }}/cache/um-linux-${{ steps.inputs.outputs.kernel }}" + fi + + if [ "${{ steps.cache-hostapd.outputs.cache-hit }}" == 'true' ] + then + FILES+=" ${{ github.workspace }}/cache/hostapd_${{ steps.inputs.outputs.hostapd_version }}" + FILES+=" ${{ github.workspace }}/cache/hostapd_cli_${{ steps.inputs.outputs.hostapd_version }}" + fi + if [ "${{ steps.cache-wpas.outputs.cache-hit }}" == 'true' ] + then + FILES+=" ${{ github.workspace }}/cache/wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }}" + FILES+=" ${{ github.workspace }}/cache/wpa_cli_${{ steps.inputs.outputs.hostapd_version }}" + fi + + tar -cvf archive.tar $FILES - name: Upload artifacts uses: actions/upload-artifact@v3 @@ -209,6 +217,31 @@ jobs: - name: Untar run: tar -xf archive.tar + - name: Cache UML Kernel + id: cache-uml-kernel + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/cache/um-linux-${{ needs.setup.outputs.kernel }} + key: um-linux-${{ needs.setup.outputs.kernel }}_ubuntu22 + + - name: Cache Hostapd + id: cache-hostapd + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/cache/hostapd_${{ needs.setup.outputs.hostapd_version }} + ${{ github.workspace }}/cache/hostapd_cli_${{ needs.setup.outputs.hostapd_version }} + key: hostapd_${{ needs.setup.outputs.hostapd_version }}_ssl3 + + - name: Cache WpaSupplicant + id: cache-wpas + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/cache/wpa_supplicant_${{ needs.setup.outputs.hostapd_version }} + ${{ github.workspace }}/cache/wpa_cli_${{ needs.setup.outputs.hostapd_version }} + key: wpa_supplicant_${{ needs.setup.outputs.hostapd_version }}_ssl3 + - name: Modprobe pkcs8_key_parser run: | sudo modprobe pkcs8_key_parser From 8acb23e50d0aed67372621c46617850ad25ffa0b Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 7 Sep 2022 14:51:41 -0700 Subject: [PATCH 03/11] ci: remove cache/ from tar file list This is taken care of by the individual cache items and if none exist, tar fails. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4bf5b1347..09bbb2961 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,7 +152,7 @@ jobs: - name: Tar files run: | - FILES="iwd ell cibase iwd-ci cache" + FILES="iwd ell cibase iwd-ci" if [ "${{ steps.cache-uml-kernel.outputs.cache-hit }}" == 'true' ] then From f3d714175693865525009c247432f6796f5f8063 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 14 Sep 2022 15:35:30 -0700 Subject: [PATCH 04/11] ci: use kernel 5.19 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09bbb2961..20b2e8419 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ on: default: all kernel: description: Kernel version - default: '5.16' + default: '5.19' hostapd_version: description: Hostapd and wpa_supplicant version default: '09a281e52a25b5461c4b08d261f093181266a554' @@ -74,14 +74,14 @@ jobs: elif [ ${{ github.event_name }} == 'repository_dispatch' ] then TESTS=all - KERNEL=5.16 + KERNEL=5.19 HOSTAPD_VERSION=09a281e52a25b5461c4b08d261f093181266a554 ELL_REF=${{ github.event.client_payload.ref }} REF=$ELL_REF REPO=${{ github.event.client_payload.repo }} else TESTS=all - KERNEL=5.16 + KERNEL=5.19 HOSTAPD_VERSION=09a281e52a25b5461c4b08d261f093181266a554 ELL_REF="refs/heads/workflow" REF="$GITHUB_REF" From 49dd88e98b56a5b4e13354d7c98a6cc686e7820b Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 14 Oct 2022 08:58:15 -0700 Subject: [PATCH 05/11] ci: use iwd-ci after renaming to remove -v2 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20b2e8419..3f9d6981a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ name: IWD CI # as test-runner # * 'musl' uses an alpine docker image to test the build on musl-libc # -# Both CI's use the 'iwd-ci-v2' repo which calls into 'ci-docker'. The +# Both CI's use the 'iwd-ci' repo which calls into 'ci-docker'. The # 'ci-docker' action essentially re-implements the native Github docker # action but allows arbitrary options to be passed in (e.g. privileged or # mounting non-standard directories) @@ -147,7 +147,7 @@ jobs: - name: Checkout CI uses: actions/checkout@v3 with: - repository: IWDTestBot/iwd-ci-v2 + repository: IWDTestBot/iwd-ci path: iwd-ci - name: Tar files @@ -196,7 +196,7 @@ jobs: sudo modprobe pkcs8_key_parser - name: Alpine CI - uses: IWDTestBot/iwd-ci-v2@master + uses: IWDTestBot/iwd-ci@master with: ref_branch: ${{ needs.setup.outputs.ref_branch }} repository: ${{ needs.setup.outputs.repository }} @@ -249,7 +249,7 @@ jobs: echo ${{ needs.setup.outputs.repository }} - name: Run CI - uses: IWDTestBot/iwd-ci-v2@master + uses: IWDTestBot/iwd-ci@master with: ref_branch: ${{ needs.setup.outputs.ref_branch }} repository: ${{ needs.setup.outputs.repository }} From f758510fbfe4b83babbd0271297b30bfd811ae7c Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 14 Oct 2022 10:18:25 -0700 Subject: [PATCH 06/11] ci: remove set-output use, now deprecated --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f9d6981a..393341c27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,12 +92,12 @@ jobs: # Now that the inputs are sorted, set the output of this step to these # values so future jobs can refer to them. # - echo ::set-output name=tests::$TESTS - echo ::set-output name=kernel::$KERNEL - echo ::set-output name=hostapd_version::$HOSTAPD_VERSION - echo ::set-output name=ell_ref::$ELL_REF - echo ::set-output name=repository::$REPO - echo ::set-output name=ref_branch::$REF + echo "tests=$TESTS" >> $GITHUB_OUTPUT + echo "kernel=$KERNEL" >> $GITHUB_OUTPUT + echo "hostapd_version=$HOSTAPD_VERSION" >> $GITHUB_OUTPUT + echo "ell_ref=$ELL_REF" >> $GITHUB_OUTPUT + echo "repository=$REPO" >> $GITHUB_OUTPUT + echo "ref_branch=$REF" >> $GITHUB_OUTPUT - name: Cache UML Kernel id: cache-uml-kernel From bc617463d3c5f2e3780bd2a0fec883a492a12094 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 7 Nov 2024 06:12:51 -0800 Subject: [PATCH 07/11] Update kernel to 6.2 and hostapd/wpa_s to 2.11 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 393341c27..993ce662d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,10 +30,10 @@ on: default: all kernel: description: Kernel version - default: '5.19' + default: '6.2' hostapd_version: description: Hostapd and wpa_supplicant version - default: '09a281e52a25b5461c4b08d261f093181266a554' + default: 'hostapd_2_11' ell_ref: description: ELL reference default: refs/heads/workflow From 8713461fca01eec193f6e150c13c6b6c67060a58 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 13 Feb 2025 08:18:29 -0800 Subject: [PATCH 08/11] Update upload/download-artifact to v4 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 993ce662d..a9582eb14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -173,7 +173,7 @@ jobs: tar -cvf archive.tar $FILES - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: iwd-artifacts path: | @@ -184,7 +184,7 @@ jobs: needs: setup steps: - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: iwd-artifacts @@ -210,7 +210,7 @@ jobs: needs: setup steps: - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: iwd-artifacts @@ -263,7 +263,7 @@ jobs: - name: Upload Logs if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-runner-logs path: ${{ github.workspace }}/log From 1385cb04197a805d3a0119cd9beff3aec80a2caa Mon Sep 17 00:00:00 2001 From: Alexander Ganslandt Date: Tue, 15 Apr 2025 10:21:12 +0200 Subject: [PATCH 09/11] util: add scan_freq_set_size function Return the number of freqs in a scan_freq_set, useful for knowing how big the set is. --- src/util.c | 11 +++++++++++ src/util.h | 1 + 2 files changed, 12 insertions(+) diff --git a/src/util.c b/src/util.c index 65b97c8eb..1e1dfc150 100644 --- a/src/util.c +++ b/src/util.c @@ -637,6 +637,17 @@ struct scan_freq_set *scan_freq_set_clone(const struct scan_freq_set *set, return new; } +uint32_t scan_freq_set_size(struct scan_freq_set *freqs) +{ + uint32_t size = 0; + + size += __builtin_popcount(freqs->channels_2ghz); + size += l_uintset_size(freqs->channels_5ghz); + size += l_uintset_size(freqs->channels_6ghz); + + return size; +} + /* First 64 entries calculated by 1 / pow(n, 0.3) for n >= 1 */ static const double rankmod_table[] = { 1.0000000000, 0.8122523964, 0.7192230933, 0.6597539554, diff --git a/src/util.h b/src/util.h index 8aef2985b..7c024c791 100644 --- a/src/util.h +++ b/src/util.h @@ -138,6 +138,7 @@ uint32_t *scan_freq_set_to_fixed_array(const struct scan_freq_set *set, size_t *len_out); struct scan_freq_set *scan_freq_set_clone(const struct scan_freq_set *set, uint32_t band_mask); +uint32_t scan_freq_set_size(struct scan_freq_set *freqs); DEFINE_CLEANUP_FUNC(scan_freq_set_free); From 80f0d05f59d544d218cb4a2d9d19c60cbca0d70c Mon Sep 17 00:00:00 2001 From: Alexander Ganslandt Date: Tue, 15 Apr 2025 10:21:13 +0200 Subject: [PATCH 10/11] scan: add scan_freq_map This is a hashmap where the keys are freqs and the values are timestamps. When a freq has been scanned, the timestamp for that freq is updated to "now". The function scan_get_freq_age can be used for getting the age of a freq, which is how long time ago it was last scanned. This is useful for deciding if a certain freq should be scanned, or if it's better to spend that time on scanning another freq. --- src/scan.c | 36 ++++++++++++++++++++++++++++++++++++ src/scan.h | 2 ++ 2 files changed, 38 insertions(+) diff --git a/src/scan.c b/src/scan.c index aeab65168..f4c2e548d 100644 --- a/src/scan.c +++ b/src/scan.c @@ -62,6 +62,7 @@ static uint32_t SCAN_MAX_INTERVAL; static uint32_t SCAN_INIT_INTERVAL; static struct l_queue *scan_contexts; +static struct l_hashmap *scan_freq_map; static struct l_genl_family *nl80211; @@ -2316,6 +2317,37 @@ static void scan_retry_pending(uint32_t wiphy_id) } } +static void scan_update_freq_map_entry(uint32_t freq, void *user_data) +{ + void *existing = l_hashmap_lookup(scan_freq_map, L_UINT_TO_PTR(freq)); + uint64_t now = l_time_now(); + + if (existing) { + l_hashmap_remove(scan_freq_map, L_UINT_TO_PTR(freq)); + } + + l_hashmap_insert(scan_freq_map, L_UINT_TO_PTR(freq), L_UINT_TO_PTR(now)); +} + +static void scan_update_freq_map(struct scan_freq_set *freqs) +{ + scan_freq_set_foreach(freqs, scan_update_freq_map_entry, NULL); +} + +uint64_t scan_get_freq_age(uint32_t freq) +{ + void *entry = l_hashmap_lookup(scan_freq_map, L_UINT_TO_PTR(freq)); + + if (entry) { + uint64_t timestamp = (uintptr_t) entry; + uint64_t now = l_time_now(); + + return (now - timestamp) / 1000000; + } + + return UINT64_MAX; +} + static void scan_notify(struct l_genl_msg *msg, void *user_data) { struct l_genl_attr attr; @@ -2461,6 +2493,8 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data) scan_get_results(sc, sr, freqs); + scan_update_freq_map(freqs); + break; } @@ -2645,6 +2679,7 @@ static int scan_init(void) const struct l_settings *config = iwd_get_config(); scan_contexts = l_queue_new(); + scan_freq_map = l_hashmap_new(); RANK_2G_FACTOR = scan_get_band_rank_modifier(BAND_FREQ_2_4_GHZ); RANK_5G_FACTOR = scan_get_band_rank_modifier(BAND_FREQ_5_GHZ); @@ -2688,6 +2723,7 @@ static void scan_exit(void) scan_contexts = NULL; l_genl_family_free(nl80211); nl80211 = NULL; + l_hashmap_destroy(scan_freq_map, NULL); } IWD_MODULE(scan, scan_init, scan_exit) diff --git a/src/scan.h b/src/scan.h index 4c1ebc21d..135ffa5e3 100644 --- a/src/scan.h +++ b/src/scan.h @@ -182,3 +182,5 @@ double scan_get_band_rank_modifier(enum band_freq band); bool scan_wdev_add(uint64_t wdev_id); bool scan_wdev_remove(uint64_t wdev_id); + +uint64_t scan_get_freq_age(uint32_t freq); From 63e8ef2bc3e9c0277e5b527331063153742480cb Mon Sep 17 00:00:00 2001 From: Alexander Ganslandt Date: Tue, 15 Apr 2025 10:21:14 +0200 Subject: [PATCH 11/11] station: improve roam scan strategy When IWD decides to roam, it scans either neighbor freqs or known freqs (if neighbors are not available). If it fails to roam after getting the scan results, it scans ALL freqs. In my testing there's a high chance that both neighbor and/or known scans fail to roam and we end up scanning all freqs. This is very slow and if you're already moving away from the current BSS, there's a high chance you will lose connection completely before the scan is finished. Instead of scanning all freqs at once, split them up into prioritized subsets. Each subset contains a handful of freqs each, a lower index for the subset means that its freqs are more common. So subset 0 has the most common freqs and subset 3 has the least common freqs. The first two subsets also contain no DFS channels, speeding up scanning even more. In order to make this efficient, use the "scan_freq_map" to avoid scanning freqs that were recently scanned. When a roam scan is triggered, add the most prioritized freqs to the list of freqs that should be scanned. The order of priority is: 1. Neighbor freqs 2. Known freqs 3. Subsets, starting with index 0 and incrementing if the subset is exhausted For each freq candidate, check the scan_freq_map to see how long time ago this freq was last scanned, this is denoted as its "age". If its age is above a threshold, add the freq to the list, otherwise discard it. Once the list has a certain size, start the scan. If no roaming occurs after the scan is completed, run the same function again. It will now pick new freqs since the previous freqs have been updated in scan_freq_map. This approach results in more scans, but fewer freqs per scan, leading to shorter delays between scan results. It also avoids scanning the same freqs back-to-back, which is generally not very useful. In combination with the freq priority, this increases the chance of finding a good BSS early. --- src/station.c | 190 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 149 insertions(+), 41 deletions(-) diff --git a/src/station.c b/src/station.c index 9972ea765..87cf9df06 100644 --- a/src/station.c +++ b/src/station.c @@ -67,6 +67,8 @@ #define STATION_RECENT_NETWORK_LIMIT 5 #define STATION_RECENT_FREQS_LIMIT 5 +#define STATION_MAX_SCAN_FREQ_AGE 3 +#define STATION_MAX_SCAN_FREQS 10 static struct l_queue *station_list; static uint32_t netdev_watch; @@ -124,7 +126,7 @@ struct station { struct l_queue *roam_bss_list; /* Frequencies split into subsets by priority */ - struct scan_freq_set *scan_freqs_order[3]; + struct scan_freq_set *scan_freqs_order[4]; unsigned int dbus_scan_subset_idx; uint32_t wiphy_watch; @@ -2385,6 +2387,8 @@ static void station_roam_retry(struct station *station) station_roam_timeout_rearm(station, roam_retry_interval); } +static void station_start_roam(struct station *station); + static void station_roam_failed(struct station *station) { l_debug("%u", netdev_get_ifindex(station->netdev)); @@ -2414,10 +2418,9 @@ static void station_roam_failed(struct station *station) goto delayed_retry; /* - * If we tried a limited scan, failed and the signal is still low, - * repeat with a full scan right away + * If the signal is still low, keep trying to roam */ - if (station->signal_low && !station->roam_scan_full) { + if (station->signal_low) { /* * Since we're re-using roam_scan_id, explicitly cancel * the scan here, so that the destroy callback is not called @@ -2426,8 +2429,8 @@ static void station_roam_failed(struct station *station) scan_cancel(netdev_get_wdev_id(station->netdev), station->roam_scan_id); - if (!station_roam_scan(station, NULL)) - return; + station_start_roam(station); + return; } delayed_retry: @@ -3102,12 +3105,85 @@ static void station_neighbor_report_cb(struct netdev *netdev, int err, station_roam_failed(station); } +static void station_filter_roam_scan_freq(uint32_t freq, void *user_data) +{ + struct scan_freq_set *freqs = user_data; + uint64_t age = scan_get_freq_age(freq); + + if (scan_freq_set_size(freqs) >= STATION_MAX_SCAN_FREQS) { + return; + } + + if (age < STATION_MAX_SCAN_FREQ_AGE) { + return; + } + + scan_freq_set_add(freqs, freq); +} + +static struct scan_freq_set *station_get_roam_scan_freqs(struct station *station) +{ + struct scan_freq_set *tmp; + struct scan_freq_set *scan_freqs; + + scan_freqs = scan_freq_set_new(); + + /* Add current frequency, always scan this to get updated data for the + * current BSS */ + scan_freq_set_add(scan_freqs, station->connected_bss->frequency); + + /* Add neighbor frequencies */ + scan_freq_set_foreach(station->roam_freqs, station_filter_roam_scan_freq, scan_freqs); + if (scan_freq_set_size(scan_freqs) >= STATION_MAX_SCAN_FREQS) { + goto out; + } + + /* Add known frequencies */ + const struct network_info *info = network_get_info( + station->connected_network); + tmp = network_info_get_roam_frequencies(info, + station->connected_bss->frequency, + 10); + scan_freq_set_foreach(tmp, station_filter_roam_scan_freq, scan_freqs); + scan_freq_set_free(tmp); + if (scan_freq_set_size(scan_freqs) >= STATION_MAX_SCAN_FREQS) { + goto out; + } + + /* Add frequencies based on the prioritized subsets */ + for (uint8_t i = 0; i < L_ARRAY_SIZE(station->scan_freqs_order); i++) { + scan_freq_set_foreach(station->scan_freqs_order[i], station_filter_roam_scan_freq, scan_freqs); + if (scan_freq_set_size(scan_freqs) >= STATION_MAX_SCAN_FREQS) { + goto out; + } + } + +out: + /* TODO: Arbitrary number to not have too small freq list */ + if (scan_freq_set_size(scan_freqs) <= 5) { + /* Might as well add the neighbors */ + scan_freq_set_merge(scan_freqs, station->roam_freqs); + } + + return scan_freqs; +} + static void station_start_roam(struct station *station) { int r; + struct scan_freq_set *freqs; station->preparing_roam = true; + /* TODO: Need to request neighbor report here, like below */ + + freqs = station_get_roam_scan_freqs(station); + station_roam_scan(station, freqs); + + scan_freq_set_free(freqs); + + return; + /* * If current BSS supports Neighbor Reports, narrow the scan down * to channels occupied by known neighbors in the ESS. If no neighbor @@ -5007,44 +5083,79 @@ static void station_add_2_4ghz_freq(uint32_t freq, void *user_data) static void station_fill_scan_freq_subsets(struct station *station) { - const struct scan_freq_set *supported = - wiphy_get_supported_freqs(station->wiphy); unsigned int subset_idx = 0; - /* - * Scan the 2.4GHz "social channels" first, 5GHz second, if supported, - * all other 2.4GHz channels last. To be refined as needed. - */ + station->scan_freqs_order[subset_idx] = scan_freq_set_new(); + + /* Subset 0: 2.4GHz "social channels" and lower 5GHz non-DFS channels */ if (allowed_bands & BAND_FREQ_2_4_GHZ) { - station->scan_freqs_order[subset_idx] = scan_freq_set_new(); - scan_freq_set_add(station->scan_freqs_order[subset_idx], 2412); - scan_freq_set_add(station->scan_freqs_order[subset_idx], 2437); - scan_freq_set_add(station->scan_freqs_order[subset_idx], 2462); - subset_idx++; + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2412); /* 1 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2437); /* 6 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2462); /* 11 */ } - /* - * TODO: It may might sense to split up 5 and 6ghz into separate subsets - * since the channel set is so large. - */ - if (allowed_bands & (BAND_FREQ_5_GHZ | BAND_FREQ_6_GHZ)) { - uint32_t mask = allowed_bands & - (BAND_FREQ_5_GHZ | BAND_FREQ_6_GHZ); - struct scan_freq_set *set = scan_freq_set_clone(supported, - mask); - - /* 5/6ghz didn't add any frequencies */ - if (scan_freq_set_isempty(set)) { - scan_freq_set_free(set); - } else - station->scan_freqs_order[subset_idx++] = set; + if (allowed_bands & BAND_FREQ_5_GHZ) { + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5180); /* 36 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5200); /* 40 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5220); /* 44 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5240); /* 48 */ } - /* Add remaining 2.4ghz channels to subset */ + station->scan_freqs_order[++subset_idx] = scan_freq_set_new(); + + /* Subset 1: 2.4GHz common "middle channels" and high 5GHz non-DFS channels */ if (allowed_bands & BAND_FREQ_2_4_GHZ) { - station->scan_freqs_order[subset_idx] = scan_freq_set_new(); - scan_freq_set_foreach(supported, station_add_2_4ghz_freq, - station->scan_freqs_order[subset_idx]); + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2422); /* 3 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2427); /* 4 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2447); /* 8 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2452); /* 9 */ + } + + if (allowed_bands & BAND_FREQ_5_GHZ) { + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5745); /* 149 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5765); /* 153 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5785); /* 157 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5805); /* 161 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5825); /* 165 */ + } + + station->scan_freqs_order[++subset_idx] = scan_freq_set_new(); + + /* TODO: Add 6GHz here, after more common 2.4 and 5GHz, but before DFS */ + + /* Subset 2: 2.4GHz remaining channels and 5GHz most common DFS channels */ + if (allowed_bands & BAND_FREQ_2_4_GHZ) { + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2417); /* 2 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2432); /* 5 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2442); /* 7 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2457); /* 10 */ + } + + if (allowed_bands & BAND_FREQ_5_GHZ) { + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5260); /* 52 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5280); /* 56 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5300); /* 60 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5320); /* 64 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5500); /* 100 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5520); /* 104 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5540); /* 108 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5560); /* 112 */ + } + + station->scan_freqs_order[++subset_idx] = scan_freq_set_new(); + + /* Subset 3: Remaining 5GHz DFS channels */ + if (allowed_bands & BAND_FREQ_5_GHZ) { + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5340); /* 68 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5480); /* 96 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5580); /* 116 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5600); /* 120 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5620); /* 124 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5640); /* 128 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5660); /* 132 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5680); /* 136 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5700); /* 140 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 5720); /* 144 */ } /* @@ -5223,11 +5334,8 @@ static void station_free(struct station *station) l_queue_destroy(station->anqp_pending, remove_anqp); - scan_freq_set_free(station->scan_freqs_order[0]); - scan_freq_set_free(station->scan_freqs_order[1]); - - if (station->scan_freqs_order[2]) - scan_freq_set_free(station->scan_freqs_order[2]); + for (uint8_t i = 0; i < 4; i++) + scan_freq_set_free(station->scan_freqs_order[i]); wiphy_state_watch_remove(station->wiphy, station->wiphy_watch);