diff --git a/.b4-config b/.b4-config new file mode 100644 index 0000000000000..a0c682e364af5 --- /dev/null +++ b/.b4-config @@ -0,0 +1,3 @@ +[b4] + send-series-to = MPTCP Upstream + send-prefixes = mptcp-net diff --git a/.git_markup b/.git_markup new file mode 100644 index 0000000000000..af78ae8cf5967 --- /dev/null +++ b/.git_markup @@ -0,0 +1 @@ +MPTCP-related modifications only needed for the -net tree. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000000..4e039626d5a56 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +github: [matttbe] +liberapay: matttbe +patreon: matttbe +thanks_dev: u/gh/matttbe diff --git a/.github/ISSUE_TEMPLATE/01-bug.yml b/.github/ISSUE_TEMPLATE/01-bug.yml new file mode 100644 index 0000000000000..a0784f2f872eb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01-bug.yml @@ -0,0 +1,104 @@ +name: Bug Report +description: Create a report to help us improve +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Thanks for helping us improve! 🙏 + Please answer these questions and provide as much information as possible about your problem. + + - type: checkboxes + id: pre-req + attributes: + label: Pre-requisites + description: "Before opening this ticket, I checked that:" + options: + - label: "A similar [issue](https://github.com/multipath-tcp/mptcp_net-next/issues/) has not been reported before." + - label: "[mptcp.dev](https://www.mptcp.dev) website does not cover my case." + - label: "An up-to-date kernel is being used." + - label: "This case is not fixed with the latest stable (or LTS) version listed on [kernel.org](https://kernel.org)" + + - type: textarea + id: what-did-you-do + attributes: + label: "What did you do?" + description: "If possible, provide a recipe for reproducing the error." + placeholder: | + Steps to reproduce the behavior: + 1. + 2. + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: "What happened?" + description: Prefer copying text output over using screenshots. + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: "What did you expect to have?" + description: Why is the current behavior incorrect, and any additional context we may need to understand the issue. + validations: + required: true + + - type: textarea + id: system-client + attributes: + label: "System info: Client" + description: | + Output of these commands executed on the **client** side: + ``` + uname -a + cat /etc/os-release + sysctl net.mptcp + ip mptcp endpoint show + ip mptcp limits show + ``` + placeholder: | + $ uname -a + Linux my-client 6.12.24-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.24-1 (2025-02-01) x86_64 GNU/Linux + (...) + render: shell + validations: + required: true + + - type: textarea + id: system-server + attributes: + label: "System info: Server" + description: | + Output of these commands executed on the **server** side: + ``` + uname -a + cat /etc/os-release + sysctl net.mptcp + ip mptcp endpoint show + ip mptcp limits show + ``` + placeholder: | + $ uname -a + Linux my-server 6.12.24-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.24-1 (2025-02-01) x86_64 GNU/Linux + (...) + render: shell + validations: + required: true + + - type: textarea + id: additional-context + attributes: + label: "Additional context" + description: | + Add any other context about the problem here. + Note: It might help to get the output of `ip mptcp monitor` while reproducing the issue, in addition to the output from these commands executed just before **and** after the issue: + ``` + ss -ManiH + nstat + ``` + Packet traces (TCPDump / WireShark), configured IP addresses and routing can be helpful too. Check [here](https://www.mptcp.dev/debugging.html) for more details. + mptcpd's [`/usr/libexec/mptcp-get-debug`](https://raw.githubusercontent.com/multipath-tcp/mptcpd/refs/heads/main/scripts/mptcp-get-debug) script (mptcpd >= 0.13) can help to collect such infos. diff --git a/.github/ISSUE_TEMPLATE/02-feature.yml b/.github/ISSUE_TEMPLATE/02-feature.yml new file mode 100644 index 0000000000000..404a29e874b2b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02-feature.yml @@ -0,0 +1,49 @@ +name: Feature Request +description: Suggest an idea for this project +labels: ["enhancement", "triage"] +body: + - type: markdown + attributes: + value: | + Thanks for helping us improve! 🙏 + Please answer these questions and provide as much information as possible about your idea. + + - type: checkboxes + id: pre-req + attributes: + label: Pre-requisites + description: "Before opening this ticket, I checked that:" + options: + - label: "A similar [idea](https://github.com/multipath-tcp/mptcp_net-next/issues?q=label%3Aenhancement) has not been reported before." + - label: "[mptcp.dev](https://www.mptcp.dev) website does not cover my case." + - label: "The [wiki](https://github.com/multipath-tcp/mptcp_net-next/wiki) doesn't cover my case." + + - type: textarea + id: description + attributes: + label: "Description" + description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]" + validations: + required: true + + - type: textarea + id: solution + attributes: + label: "Solution" + description: "A clear and concise description of what you want to have." + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: "Considered alternatives" + description: "A clear and concise description of any alternative solutions or features you've considered." + validations: + required: true + + - type: textarea + id: additional-context + attributes: + label: "Additional context" + description: "Add any other context or screenshots about the feature request here." diff --git a/.github/ISSUE_TEMPLATE/03-question.yml b/.github/ISSUE_TEMPLATE/03-question.yml new file mode 100644 index 0000000000000..5b7618de72db4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03-question.yml @@ -0,0 +1,28 @@ +name: Question +description: Ask any questions not related to an issue or a feature request here +labels: ["question", "triage"] +body: + - type: markdown + attributes: + value: | + Thanks for helping us improve! 🙏 + Please answer these questions and provide as much information as possible about your idea. + + - type: checkboxes + id: pre-req + attributes: + label: Pre-requisites + description: "Before opening this ticket, I checked that:" + options: + - label: "A similar [question](https://github.com/multipath-tcp/mptcp_net-next/issues?q=label%3question) has not been reported before." + - label: "[mptcp.dev](https://www.mptcp.dev) website does not cover my case." + - label: "The [wiki](https://github.com/multipath-tcp/mptcp_net-next/wiki) doesn't cover my case." + - label: "This is not a question related to the current behavior, an issue or a feature requst: if it is, please use [another template](https://github.com/multipath-tcp/mptcp_net-next/issues/new/choose) **even if it is a question**: we will need details about your system: kernel version, config, etc." + + - type: textarea + id: question + attributes: + label: "My question" + description: "A clear and concise description of your question" + validations: + required: true diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml new file mode 100644 index 0000000000000..143decc1bcdca --- /dev/null +++ b/.github/workflows/build-validation.yml @@ -0,0 +1,334 @@ +name: "MPTCP Upstream Build Validation" +on: + push: + branches-ignore: + - 'archived/**' # previous branches + - 't/**' # TopGit tree + - 'net' # part of the TopGit tree + - 'net-next' # part of the TopGit tree + - 'for-review' # part of the TopGit tree + - 'for-review-net' # part of the TopGit tree + tags: + - 'patchew/**' # patchew is using tags + # ideally, we would take 'export/**' but the cache is per branch... + # In other words, when using tags, we can only use the cache if we re-tag. + # https://github.com/actions/cache/issues/556 + # So we build the "export" branch and we try to find the tag later + +env: + CURL_OPT: "--no-progress-meter --connect-timeout 30 --retry 20 --retry-delay 10" + CURL_ACC: "Accept: application/vnd.github.v3+json" + URI: "https://api.github.com" + PW: "https://patchwork.kernel.org/api/1.2" + +permissions: {} + +jobs: + build: + name: "Build (matrix)" + if: "! startswith(github.ref, 'refs/tags/patchew/') || contains(github.event.head_commit.message, 'Message-Id: ')" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + defconfig: ['x86_64', 'i386'] + ipv6: ['with_ipv6', 'without_ipv6'] + mptcp: ['with_mptcp', 'without_mptcp'] + permissions: + contents: read # to fetch code (actions/checkout) + + steps: + - name: "Checkout (light)" + if: github.ref != 'refs/heads/export' + uses: actions/checkout@v4 + with: + fetch-depth: 100 # we should not have more commits on top of export and -net + + - name: "Checkout (export)" + if: github.ref == 'refs/heads/export' + uses: actions/checkout@v4 + with: + fetch-depth: 0 # we need to fetch all commits between net and net-next, quicker to get everything + + - name: "Find base branch" + id: branch + run: | + if [ "${REF_NAME}" = "export" ]; then # just to avoid the next cmd + echo "name=export" >> ${GITHUB_OUTPUT} + elif [ -n "$(git log -1 --grep "^DO-NOT-MERGE: mptcp: enabled by default (net)$" --format="format:%H" HEAD -- net/mptcp/Kconfig)" ]; then + echo "name=export-net" >> ${GITHUB_OUTPUT} + else + echo "name=export" >> ${GITHUB_OUTPUT} + fi + env: + REF_NAME: ${{ github.ref_name }} + + - name: "Restore cache for CCache" + uses: actions/cache/restore@v4 + id: restore-ccache + with: + path: ${{ github.workspace }}/.ccache + key: ${{ runner.os }}_build_${{ matrix.defconfig }}_${{ matrix.ipv6 }}_${{ matrix.mptcp }}_${{ steps.branch.outputs.name }}-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}_build_${{ matrix.defconfig }}_${{ matrix.ipv6 }}_${{ matrix.mptcp }}_${{ steps.branch.outputs.name }}-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.sha }} + ${{ runner.os }}_build_${{ matrix.defconfig }}_${{ matrix.ipv6 }}_${{ matrix.mptcp }}_${{ steps.branch.outputs.name }}-${{ github.run_id }}-${{ github.run_attempt }}- + ${{ runner.os }}_build_${{ matrix.defconfig }}_${{ matrix.ipv6 }}_${{ matrix.mptcp }}_${{ steps.branch.outputs.name }}-${{ github.run_id }}- + ${{ runner.os }}_build_${{ matrix.defconfig }}_${{ matrix.ipv6 }}_${{ matrix.mptcp }}_${{ steps.branch.outputs.name }}- + + - name: "Build Validation" + uses: multipath-tcp/mptcp-upstream-validate-export-action@main + with: + # we want to validate each commits on top of net-next/export (or -net) except for stable + each_commit: ${{ ! startswith(github.ref, 'refs/heads/stable/') }} + ccache_maxsize: 300M ## 10GB = project limit + defconfig: ${{ matrix.defconfig }} + ipv6: ${{ matrix.ipv6 }} + mptcp: ${{ matrix.mptcp }} + debug: ${{ runner.debug }} + + - name: "Artifacts" + if: always() + uses: actions/upload-artifact@v4 + with: + name: results-${{ matrix.defconfig }}_${{ matrix.ipv6 }}_${{ matrix.mptcp }} + path: ./build-*-results.txt + + - name: "Publish details" + if: always() + run: | + if stat ./build-*-results.txt &>/dev/null; then + echo '- Results for ${{ matrix.defconfig }} ${{ matrix.ipv6 }} ${{ matrix.mptcp }}:' >> ${GITHUB_STEP_SUMMARY} + echo "\`\`\`" >> ${GITHUB_STEP_SUMMARY} + cat ./build-*-results.txt >> ${GITHUB_STEP_SUMMARY} + echo "\`\`\`" >> ${GITHUB_STEP_SUMMARY} + fi + + - name: "Save cache for CCache" + if: github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net' + uses: actions/cache/save@v4 + with: + path: ${{ github.workspace }}/.ccache + key: ${{ steps.restore-ccache.outputs.cache-primary-key }} + + notif-export: + name: "Notifications export branches" + needs: build + # only for the official repo, export branches + if: always() && github.repository_owner == 'multipath-tcp' && (github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net') + runs-on: ubuntu-latest + steps: + - name: get linked tag + id: tag + run: | + TAG=$(curl ${CURL_OPT} -H "${CURL_ACC}" -H "${CURL_AUTH}" "${URL}" | jq -r ".[] | select(.object.sha == \"${SHA}\").ref" | tail -n1) + echo "Found: ${TAG} (${SHA} - ${BRANCH})" + TAG="${TAG:10}" + echo "tag=${TAG:-${BRANCH}}" >> ${GITHUB_OUTPUT} + env: + CURL_AUTH: "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" + URL: "${{ env.URI }}/repos/${{ github.repository }}/git/matching-refs/tags/" + SHA: "${{ github.sha }}" + BRANCH: "${{ github.ref_name }}" + + - name: irc build + uses: rectalogic/notify-irc@v2 + with: + server: irc.libera.chat + channel: "#mptcp-ci" + nickname: gh-build-bot + verbose: true + message: |- + New build validating ${{ steps.tag.outputs.tag }} (by ${{ github.actor }}) ended with ${{ needs.build.result }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: irc build error + if: needs.build.result == 'failure' + uses: rectalogic/notify-irc@v2 + with: + server: irc.libera.chat + channel: "#mptcp" + nickname: gh-build-bot + verbose: true + message: |- + New build validating ${{ steps.tag.outputs.tag }} (by ${{ github.actor }}) failed: ${{ needs.build.result }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + notif-patchew: + name: "Notifications patchew tags" + needs: build + # only for the official repo, patchew tags branches + if: always() && github.repository_owner == 'multipath-tcp' && startswith(github.ref, 'refs/tags/patchew/') && (needs.build.result == 'success' || needs.build.result == 'failure') + runs-on: ubuntu-latest + steps: + - name: "Get Results" + uses: actions/download-artifact@v4 + with: + pattern: results-* + merge-multiple: true + + - name: "Patchwork" + run: | + # $1: mid + get_status() { + case "$(awk "/^${1} /{ print \$2 }" build-*-results.txt | sort -u)" in + 'fail'*) echo "fail"; ;; + *'warning') echo "warning"; ;; + 'success') echo "success"; ;; + *) echo "fail"; ;; + esac + } + + # $1: mid, $2: status + get_desc() { + awk "/^${1} ${2} /{ + out=\$3 + for(i=4; i<=NF; i++) + out=out\" \"\$i + print out + }" build-*-results.txt | sort -u | sed '$!{:a;N;s/\n/ ; /;ta}' + } + + # $1: mid, $2: status, $3: desc + _send() { local check_url + check_url="$(curl "${URL_PW}${1}" | jq -r 'last(.[].checks)')" + if [ -z "${check_url}" ] || [ "${check_url}" = "null" ]; then + echo "URL not found: '${check_url}' '${URL_PW}${1}'" + return 1 + fi + + curl ${CURL_OPT} \ + -X POST \ + -H "Authorization: Token ${{ secrets.PW_TOKEN }}" \ + -F "state=${2}" \ + -F "target_url=${URL_GH}" \ + -F "context=build" \ + -F "description=${3}" \ + "${check_url}" | jq '.' + } + + FIRST=1 + send() { local i + # patches can take a bit of time to appear: retry the first time + if [ "${FIRST}" = "1" ]; then + FIRST=0 + + for i in $(seq 45); do + if _send "${@}"; then + echo "Successful sent after ${i} attempts" + return 0 + fi + sleep 1m + done + + curl "${URL_PW}${1}" + return 1 + else + _send "${@}" + fi + } + + if ! ls ./build-*-results.txt; then + echo "Strange, no results, please check why" + exit 1 + fi + + while read -r mid; do + status=$(get_status "${mid}") + desc=$(get_desc "${mid}" "${status}") + send "${mid}" "${status}" "${desc}" + done < <(awk '{ print $1 }' build-*-results.txt | sort -u) + env: + URL_GH: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + URL_PW: "${{ env.PW }}/patches/?project=mptcp&msgid=" + + - name: get commit info + id: commit + if: needs.build.result == 'failure' + run: | + cat <<'EOF' > commit.json + ${{ toJSON(github.event.head_commit) }} + EOF + + # ignore error, just in case the MID has not been added by the author + read -r TAG MID < <(jq -r '.message' commit.json | grep -i "^Message-Id: " | tail -n1) || true + + # Guess the subject from the last commit + SUBJECT=$(jq -r '.message' commit.json | head -n1) + + if [ -n "${MID:1:-1}" ]; then + # get cover-letter and series' name if any + URL_PW_SERIES_API=$(curl "${URL_PW}${MID:1:-1}" | jq -er 'last(last(.[].series)[].url)' || true) + if [ -n "${URL_PW_SERIES_API}" ] && [ "${URL_PW_SERIES_API}" != "null" ]; then + echo "series=${URL_PW_SERIES}$(basename "${URL_PW_SERIES_API}")" >> ${GITHUB_OUTPUT} + if curl "${URL_PW_SERIES_API}" > pw_series.json && [ -s pw_series.json ]; then + CL="$(jq '.cover_letter' pw_series.json || true)" + if [ -n "${CL}" ] && [ "${CL}" != "null" ] && [ "${CL}" != "{}" ]; then + MID=$(echo "${CL}" | jq -er '.msgid' || echo "${MID}") + SUBJECT=$(jq -er '.name' pw_series.json || echo "${SUBJECT}") + fi + fi + fi + + # get tags from Lore: not fully available from Patchwork + SUBJECT="$(curl "${URL_LORE//MID/${MID:1:-1}}" | grep '^Subject: ' | head -n1 | sed 's/^Subject: \(\[.*\] \).*/\1/')${SUBJECT}" + fi + + echo "Found message ID: '${TAG}' '${MID}'" + echo "mid=${MID:1:-1}" >> ${GITHUB_OUTPUT} + + echo "Found subject: '${SUBJECT}'" + echo "subject=${SUBJECT}" >> ${GITHUB_OUTPUT} + + NAME=$(jq -r '.author.name' commit.json) + EMAIL=$(jq -r '.author.email' commit.json) + echo "Found author: '${NAME}' '${EMAIL}'" + echo "name=${NAME%% *}" >> ${GITHUB_OUTPUT} + echo "author=${NAME} <${EMAIL}>" >> ${GITHUB_OUTPUT} + + SHA=$(jq -r '.id' commit.json) + echo "Found SHA: '${SHA}' ('${SHA:0:12}')" + echo "sha=${SHA:0:12}" >> ${GITHUB_OUTPUT} + + COMMITTER=$(jq -r '.committer.name' commit.json) + echo "Found committer: '${COMMITTER}'" + echo "committer=${COMMITTER}" >> ${GITHUB_OUTPUT} + env: + URL_PW: "${{ env.PW }}/patches/?project=mptcp&msgid=" + URL_PW_SERIES: "https://patchwork.kernel.org/project/mptcp/list/?series=" + URL_LORE: "https://lore.kernel.org/mptcp/MID/raw" + + - name: send email + if: needs.build.result == 'failure' + uses: dawidd6/action-send-mail@v3 + with: + server_address: smtp.gmail.com + server_port: 465 + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + to: ${{ steps.commit.outputs.author }} + cc: mptcp@lists.linux.dev + from: MPTCP CI + reply_to: mptcp@lists.linux.dev + in_reply_to: "<${{ steps.commit.outputs.mid }}>" + subject: "Re: ${{ steps.commit.outputs.subject }}" + body: | + Hi ${{ steps.commit.outputs.name }}, + + Thank you for your modifications, that's great! + + But sadly, our CI spotted some issues with it when trying to build it. + + You can find more details there: + + ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + Status: ${{ needs.build.result }} + Initiator: ${{ steps.commit.outputs.committer }} + Commits: ${{ github.server_url }}/${{ github.repository }}/commits/${{ steps.commit.outputs.sha }} + Patchwork: ${{ steps.commit.outputs.series }} + + Feel free to reply to this email if you cannot access logs, if you need + some support to fix the error, if this doesn't seem to be caused by your + modifications or if the error is a false positive one. + + Cheers, + MPTCP GH Action bot + Bot operated by Matthieu Baerts (NGI0 Core) diff --git a/.github/workflows/checkpatch.yml b/.github/workflows/checkpatch.yml new file mode 100644 index 0000000000000..d86e76eaed06c --- /dev/null +++ b/.github/workflows/checkpatch.yml @@ -0,0 +1,191 @@ +name: "CheckPatch" +on: + push: + branches-ignore: + - 'archived/**' # previous branches + - 't/**' # TopGit tree + - 'net' # part of the TopGit tree + - 'net-next' # part of the TopGit tree + - 'for-review' # part of the TopGit tree + - 'for-review-net' # part of the TopGit tree + tags: + - 'patchew/**' # patchew is using tags + +env: + CURL_OPT: "--no-progress-meter --connect-timeout 30 --retry 20 --retry-delay 10" + PW: "https://patchwork.kernel.org/api/1.2" + CHECKPATCH_RESULTS: "./checkpatch-results.txt" + CHECKPATCH_DETAILS: "./checkpatch-details.txt" + SHELLCHECK_RESULTS: "./shellcheck-results.txt" + SHELLCHECK_DETAILS: "./shellcheck-details.txt" + +permissions: {} + +jobs: + checkpatch: + name: "Checkpatch" + # for others or for the official repo but only commits from patchew + if: "github.repository_owner != 'multipath-tcp' || (startswith(github.ref, 'refs/tags/patchew/') && contains(github.event.head_commit.message, 'Message-Id: '))" + runs-on: ubuntu-latest + permissions: + contents: read # to fetch code (actions/checkout) + + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 ## to make sure a mentioned commit exists + + - name: "Checkpatch" + uses: multipath-tcp/mptcp-upstream-validate-export-action@main + with: + each_commit: true + checkpatch: true + debug: ${{ secrets.BUILD_ACTION_DEBUG }} + + - name: "Publish details" + if: always() + run: | + if [ -s "${{ env.CHECKPATCH_DETAILS }}" ]; then + echo '## CheckPatch' >> ${GITHUB_STEP_SUMMARY} + cat "${{ env.CHECKPATCH_DETAILS }}" >> ${GITHUB_STEP_SUMMARY} + fi + if [ -s "${{ env.SHELLCHECK_DETAILS }}" ]; then + echo '## ShellCheck' >> ${GITHUB_STEP_SUMMARY} + cat "${{ env.SHELLCHECK_DETAILS }}" >> ${GITHUB_STEP_SUMMARY} + fi + + - name: "Artifacts" + uses: actions/upload-artifact@v4 + with: + name: results + path: | + ${{ env.CHECKPATCH_RESULTS }} + ${{ env.SHELLCHECK_RESULTS }} + + - name: "Artifacts" + uses: actions/upload-artifact@v4 + with: + name: details + path: | + ${{ env.CHECKPATCH_DETAILS }} + ${{ env.SHELLCHECK_DETAILS }} + + notif: + name: "Notifications" + needs: checkpatch + # only for the official repo (patchew) + if: github.repository_owner == 'multipath-tcp' && startswith(github.ref, 'refs/tags/patchew/') && (needs.checkpatch.result == 'success' || needs.checkpatch.result == 'failure') + runs-on: ubuntu-latest + steps: + - name: "Get Results" + uses: actions/download-artifact@v4 + with: + name: results + + - name: "Patchwork" + run: | + # $1: mid, $2: status, $3: desc, $4: context + _send() { local check_url + check_url="$(curl "${URL_PW}${1}" | jq -r 'last(.[].checks)')" + if [ -z "${check_url}" ] || [ "${check_url}" = "null" ]; then + echo "URL not found: '${check_url}' '${URL_PW}${1}'" + return 1 + fi + + curl ${CURL_OPT} \ + -X POST \ + -H "Authorization: Token ${{ secrets.PW_TOKEN }}" \ + -F "state=${2}" \ + -F "target_url=${URL_GH}" \ + -F "context=${4}" \ + -F "description=${3}" \ + "${check_url}" | jq '.' + } + + FIRST=1 + send() { local i + # patches can take a bit of time to appear: retry the first time + if [ "${FIRST}" = "1" ]; then + FIRST=0 + + for i in $(seq 45); do + if _send "${@}"; then + echo "Successful sent after ${i} attempts" + return 0 + fi + sleep 1m + done + + curl "${URL_PW}${1}" + return 1 + else + _send "${@}" + fi + } + + # $1: file, $2: context + parse_results() { + if [ ! -s "${1}" ]; then + echo "Strange, no results, please check why" + return 1 + fi + + while read -r mid status desc; do + echo "Sending: '${mid}' '${status}' '${desc}' '${2}'" + send "${mid}" "${status}" "${desc}" "${2}" + done < "${1}" + } + + rc=0 + parse_results "${CHECKPATCH_RESULTS}" "checkpatch" || rc=1 + parse_results "${SHELLCHECK_RESULTS}" "shellcheck" || rc=1 + exit ${rc} + + env: + URL_GH: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + URL_PW: "${{ env.PW }}/patches/?project=mptcp&msgid=" + + status: + name: "Status" + needs: checkpatch + # for others, to report an error if patches were not OK + if: github.repository_owner != 'multipath-tcp' + runs-on: ubuntu-latest + steps: + - name: "Get Results" + uses: actions/download-artifact@v4 + with: + name: results + + - name: "Set exit status" + run: | + # $1: result file, $2: context + check() { + if [ ! -s "${1}" ]; then + echo "Strange, no results, please check why" + exit 1 + fi + + if awk '{ if ($2 != "success") exit 1 }' "${1}"; then + echo " *** Everything OK with ${2}, good job!" + return 0 + fi + + echo " *** ${2} detected some issues:" + cat "${1}" + echo " *** End of the issues detected by ${2}" + + return 1 + } + + echo + rc=0 + check "${CHECKPATCH_RESULTS}" "CheckPatch" || rc=1 + check "${SHELLCHECK_RESULTS}" "ShellCheck" || rc=1 + [ ${rc} -eq 0 ] && exit 0 + + echo + echo "Please check the summary page for more details about these issues:" + echo " ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + exit ${rc} diff --git a/.github/workflows/notif.yml b/.github/workflows/notif.yml new file mode 100644 index 0000000000000..c4ca514ec0cbd --- /dev/null +++ b/.github/workflows/notif.yml @@ -0,0 +1,56 @@ +name: "Notifications" +on: + push: + tags: + - 'export/**' # exclude patchew/** tags and branches + - 'export-net/**' + issues: + types: [opened, reopened, closed, assigned, unassigned] + +permissions: {} + +jobs: + tag: + name: "Tag" + if: github.repository_owner == 'multipath-tcp' && github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - name: tag shortner + id: tag + run: | + echo "tag=${REF:10}" >> ${GITHUB_OUTPUT} + env: + REF: ${{ github.event.ref }} + - name: irc tag + uses: rectalogic/notify-irc@v2 + with: + server: irc.libera.chat + channel: "#mptcp-ci" + nickname: gh-tag-bot + verbose: true + message: "New tag available: ${{ steps.tag.outputs.tag }} (by ${{ github.actor }})" + + issues: + name: "Issues" + if: github.repository_owner == 'multipath-tcp' && github.event_name == 'issues' + runs-on: ubuntu-latest + steps: + - name: issue info + id: info + if: github.event.action != 'opened' + run: | + echo "opener=, opened by ${OPENER}" >> ${GITHUB_OUTPUT} + echo "assignee=${ASSIGNEE:+ and assigned to ${ASSIGNEE}}" >> ${GITHUB_OUTPUT} + env: + OPENER: ${{ github.event.issue.user.login }} + ASSIGNEE: ${{ github.event.assignee.login }} + - name: irc issues + uses: rectalogic/notify-irc@v2 + with: + server: irc.libera.chat + channel: "#mptcp" + nickname: gh-issues-bot + verbose: true + message: |- + Issue #${{ github.event.issue.number }} ("${{ github.event.issue.title }}"${{ steps.info.outputs.opener }}${{ steps.info.outputs.assignee }}) has been ${{ github.event.action }} by ${{ github.actor }} + ${{ github.event.issue.html_url }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000000000..399377da967c3 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,562 @@ +name: "MPTCP Upstream Tests Validation" +on: + push: + branches-ignore: + - 'archived/**' # previous branches + - 't/**' # TopGit tree + - 'net' # part of the TopGit tree + - 'net-next' # part of the TopGit tree + - 'for-review' # part of the TopGit tree + - 'for-review-net' # part of the TopGit tree + tags: + - 'patchew/**' # patchew is using tags + # ideally, we would take 'export/**' but the cache is per branch... + # In other words, when using tags, we can only use the cache if we re-tag. + # https://github.com/actions/cache/issues/556 + # So we test the "export" branch and we try to find the tag later + +env: + CURL_OPT: "--no-progress-meter --connect-timeout 30 --retry 20 --retry-delay 10" + CURL_ACC: "Accept: application/vnd.github.v3+json" + URI: "https://api.github.com" + PW: "https://patchwork.kernel.org/api/1.2" + +permissions: {} + +jobs: + tests: + name: "Tests" + if: "! startswith(github.ref, 'refs/tags/patchew/') || contains(github.event.head_commit.message, 'Message-Id: ')" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + mode: ['normal', 'debug', 'btf-normal', 'btf-debug'] + permissions: + contents: read # to fetch code (actions/checkout) + + steps: + - name: "Checkout" + uses: actions/checkout@v4 + + #- name: "Collect Workflow Telemetry" + # uses: catchpoint/workflow-telemetry-action@v2 + + - name: "Find base branch" + id: branch + run: | + if [ "$(cat .git_markup)" = "MPTCP-related modifications only needed for our tests suite (mptcp-net)." ]; then + echo "name=export-net" >> ${GITHUB_OUTPUT} + else + echo "name=export" >> ${GITHUB_OUTPUT} + fi + + mode="${{ matrix.mode }}" + echo "mode=${mode//-/_}" >> ${GITHUB_OUTPUT} + + - name: "Restore cache for CCache" + uses: actions/cache/restore@v4 + id: restore-ccache + with: + path: ${{ github.workspace }}/.virtme/ccache + key: ${{ runner.os }}_tests_${{ steps.branch.outputs.name }}-${{ steps.branch.outputs.mode }}-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}_tests_${{ steps.branch.outputs.name }}-${{ steps.branch.outputs.mode }}-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.sha }} + ${{ runner.os }}_tests_${{ steps.branch.outputs.name }}-${{ steps.branch.outputs.mode }}-${{ github.run_id }}-${{ github.run_attempt }}- + ${{ runner.os }}_tests_${{ steps.branch.outputs.name }}-${{ steps.branch.outputs.mode }}-${{ github.run_id }}- + ${{ runner.os }}_tests_${{ steps.branch.outputs.name }}-${{ steps.branch.outputs.mode }}- + ${{ runner.os }}_tests_${{ steps.branch.outputs.name }}- + + - name: "Docker image" + run: | + /usr/bin/docker pull ghcr.io/multipath-tcp/mptcp-upstream-virtme-docker:${{ steps.branch.outputs.name == 'export' && 'latest' || 'net' }} + + - name: "Tests" + timeout-minutes: 120 + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + # remove old cache if any + rm -rvf "${{ github.workspace }}/.virtme/ccache-"* 2>/dev/null + + set -x + /usr/bin/docker run --privileged --rm \ + -e "INPUT_CCACHE_MAXSIZE=500M" \ + -e "INPUT_CCACHE_DIR=ccache" \ + -e "INPUT_PACKETDRILL_STABLE=${{ steps.branch.outputs.name == 'export-net' && '1' || '0' }}" \ + -e "INPUT_EXTRA_ENV=${{ startsWith(matrix.mode, 'btf-') && 'INPUT_RUN_TESTS_ONLY=bpftest_all' || '' }}" \ + -e "INPUT_TRACE=${RUNNER_DEBUG}" \ + -e "INPUT_GCOV=1" \ + -e "GITHUB_SHA" -e "GITHUB_REF_NAME" -e "GITHUB_RUN_ID" \ + -e GITHUB_ACTIONS=true -e CI=true \ + --workdir "${PWD}" \ + -v "${PWD}:${PWD}" \ + ghcr.io/multipath-tcp/mptcp-upstream-virtme-docker:${{ steps.branch.outputs.name == 'export' && 'latest' || 'net' }} \ + auto-${{ matrix.mode }} + + - name: "Publish conclusion" + if: always() + run: | + set +e + if [ -s "conclusion.txt" ]; then + { + echo '## Mode ${{ matrix.mode }}' + echo '### Conclusion (${{ matrix.mode }})' + cat "conclusion.txt" + echo '' + echo '### Summary (${{ matrix.mode }})' + echo '```' + cat "summary.txt" + echo '```' + echo '' + echo '### Coverage (${{ matrix.mode }})' + echo '```' + cat "coverage.txt" || echo "No coverage" + echo '```' + } >> "${GITHUB_STEP_SUMMARY}" + fi + touch kernel.lcov || true + + - name: "Artifacts (always)" + if: always() + uses: actions/upload-artifact@v4 + with: + name: results-${{ matrix.mode }} + path: | + conclusion.txt + summary.txt + coverage.txt + *.tap + config.zstd + *.tap.xml + results.json + + - name: "Artifacts (failure)" + if: failure() + uses: actions/upload-artifact@v4 + with: + name: debug-info-${{ matrix.mode }} + path: | + vmlinux.zstd + kmemleak.txt + + - name: "Artifacts (LCov)" + uses: actions/upload-artifact@v4 + with: + name: lcov-${{ matrix.mode }} + compression-level: 9 + path: | + kernel.lcov + + - name: "Artifacts (code)" + uses: actions/upload-artifact@v4 + if: github.repository_owner == 'multipath-tcp' && matrix.mode == 'normal' && (github.ref_name == 'export' || github.ref_name == 'export-net') + with: + name: code + compression-level: 9 + path: | + net/mptcp/*.[ch] + + - name: Coveralls Parallel + uses: coverallsapp/github-action@v2 + if: always() && (github.repository_owner != 'multipath-tcp' || github.ref_name == 'export' || github.ref_name == 'export-net') + with: + flag-name: ${{ matrix.mode }} + parallel: true + file: kernel.lcov + format: lcov + allow-empty: true + compare-ref: ${{ steps.branch.outputs.name }} + + - name: "Publish Test Results" + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + compare_to_earlier_commit: false + check_run: false + check_name: "Test Results (${{ matrix.mode }})" + files: | + *.tap.xml + + - name: "Save cache for CCache" + if: always() && (github.repository_owner != 'multipath-tcp' || github.ref_name == 'export' || github.ref_name == 'export-net') + uses: actions/cache/save@v4 + with: + path: ${{ github.workspace }}/.virtme/ccache + key: ${{ steps.restore-ccache.outputs.cache-primary-key }} + + publish-test-results: + name: "Publish Tests Results" + needs: tests + if: always() + runs-on: ubuntu-latest + permissions: + checks: write + + steps: + - name: "Get results" + uses: actions/download-artifact@v4 + with: + pattern: results-* + merge-multiple: false + + - name: "Publish Test Results" + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + check_run_annotations_branch: "${{ steps.branch.outputs.name }}" + files: | + results-*/*.tap.xml + + - name: Coveralls Finished + uses: coverallsapp/github-action@v2 + if: github.repository_owner != 'multipath-tcp' || github.ref_name == 'export' || github.ref_name == 'export-net' + with: + parallel-finished: true + carryforward: "normal,debug,btf-normal,btf-debug" + + notif: + name: "Notifications" + needs: tests + # only for the official repo (patchew and export) + if: always() && github.repository_owner == 'multipath-tcp' && (needs.tests.result == 'success' || needs.tests.result == 'failure') + concurrency: + group: ${{ startswith(github.ref, 'refs/heads/export') && 'ci-notif' || github.sha }} + cancel-in-progress: false + runs-on: ubuntu-latest + steps: + - name: get results + uses: actions/download-artifact@v4 + with: + pattern: results-* + merge-multiple: false + + - name: get test info + id: test + run: | + for mode in normal debug btf-normal btf-debug; do + ccl="$(cat "results-${mode}/conclusion.txt")" + echo "ccl_${mode//-/_}=${ccl:-"KVM Validation: ${mode}: Critical: No conclusion ❓"}" >> ${GITHUB_OUTPUT} + echo "ccl_title_${mode//-/_}=$(echo "${ccl}" | cut -d: -f1-2)" >> ${GITHUB_OUTPUT} + echo "ccl_status_${mode//-/_}=$(echo "${ccl}" | cut -d: -f3- | sed 's/^ //')" >> ${GITHUB_OUTPUT} + done + echo "url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> ${GITHUB_OUTPUT} + + - name: get linked tag + if: github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net' + id: tag + run: | + TAG=$(curl ${CURL_OPT} -H "${CURL_ACC}" -H "${CURL_AUTH}" "${URL}" | jq -r ".[] | select(.object.sha == \"${SHA}\").ref" | grep "^refs/tags/export" | tail -n1) + echo "Found: ${TAG} (${SHA} - ${BRANCH})" + TAG="${TAG:10}" + echo "tag=${TAG:-${BRANCH}}" >> ${GITHUB_OUTPUT} + env: + CURL_AUTH: "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" + URL: "${{ env.URI }}/repos/${{ github.repository }}/git/matching-refs/tags/" + SHA: "${{ github.sha }}" + BRANCH: "${{ github.ref_name }}" + + - name: irc tests + if: github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net' + uses: rectalogic/notify-irc@v2 + with: + server: irc.libera.chat + channel: "#mptcp-ci" + nickname: gh-tests-bot + verbose: true + message: |- + New GH Actions Tests job validating ${{ steps.tag.outputs.tag }} (by ${{ github.actor }}) just ended: + - ${{ steps.test.outputs.ccl_normal }} + - ${{ steps.test.outputs.ccl_debug }} + - ${{ steps.test.outputs.ccl_btf_normal }} + - ${{ steps.test.outputs.ccl_btf_debug }} + - Task: ${{ steps.test.outputs.url }} + + - name: Checkout results repo + if: github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net' + uses: actions/checkout@v4 + with: + repository: "multipath-tcp/mptcp-upstream-tests-results" + token: '${{ secrets.PAT_MATTTBE }}' + path: results + + - name: setup results repo + if: github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net' + run: | + cd results + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: save flakes results + if: github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net' + run: | + for mode in normal debug btf-normal btf-debug; do + new="results-${mode}/results.json" + all="results/html/results/${{ github.ref_name }}/${mode}.json" + if [ ! -s "${new}" ]; then + echo '{"error": "all", "run_id": "${{ github.run_id }}"}' > "${new}" + fi + # append tag, merge results, limit + jq -c '.tag += "${{ steps.tag.outputs.tag }}"' "${new}" > "${new}.tag" + jq -c '. += [input]' "${all}" "${new}.tag" > "${new}.all" + jq --indent 1 '.[-100:]' "${new}.all" > "${all}" + done + + cd results + git add html/results/${{ github.ref_name }}/*.json + git commit -m "json: new: ${{ steps.tag.outputs.tag }}" + + - name: get lcov + if: needs.tests.result == 'success' && (github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net') + uses: actions/download-artifact@v4 + with: + pattern: lcov-* + merge-multiple: false + + - name: get code + if: needs.tests.result == 'success' && (github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net') + uses: actions/download-artifact@v4 + with: + name: code + path: net/mptcp + + - name: lcov to html and publish results + if: needs.tests.result == 'success' && (github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net') + run: | + out="results/html/lcov/${{ github.ref_name }}" + rm -rf "${out}" + mkdir -p "${out}" + /usr/bin/docker run --pull always --rm \ + --workdir "${PWD}" \ + -v "${PWD}:${PWD}" \ + mptcp/docker-lcov-alpine:latest \ + genhtml -j "$(nproc)" -t "${{ github.ref_name }}" \ + --dark-mode --legend \ + --include '/net/mptcp/' --flat \ + --function-coverage --branch-coverage --keep-going \ + -o "${out}" lcov-*/kernel.lcov | tee genhtml.log + + { + echo '' + echo '## Coverage (All)' + echo '```' + tail -n4 genhtml.log + echo '```' + } >> "${GITHUB_STEP_SUMMARY}" + + cd results + git add html/lcov/${{ github.ref_name }} + git commit -m "lcov: new: ${{ steps.tag.outputs.tag }}" || true + + - name: push results + if: github.ref == 'refs/heads/export' || github.ref == 'refs/heads/export-net' + run: | + cd results + git push + + - name: get commit info + id: commit + if: startswith(github.ref, 'refs/tags/patchew/') + run: | + cat <<'EOF' > commit.json + ${{ toJSON(github.event.head_commit) }} + EOF + + # ignore error, just in case the MID has not been added by the author + read -r TAG MID < <(jq -r '.message' commit.json | grep -i "^Message-Id: " | tail -n1) || true + echo "Found message ID: '${TAG}' '${MID}'" + echo "mid=${MID:1:-1}" >> ${GITHUB_OUTPUT} + + # Guess the subject from the last commit + SUBJECT=$(jq -r '.message' commit.json | head -n1) + echo "Found subject: '${SUBJECT}'" + echo "subject=${SUBJECT}" >> ${GITHUB_OUTPUT} + + NAME=$(jq -r '.author.name' commit.json) + EMAIL=$(jq -r '.author.email' commit.json) + echo "Found author: '${NAME}' '${EMAIL}'" + echo "name=${NAME%% *}" >> ${GITHUB_OUTPUT} + echo "author=${NAME} <${EMAIL}>" >> ${GITHUB_OUTPUT} + + SHA=$(jq -r '.id' commit.json) + echo "Found SHA: '${SHA}' ('${SHA:0:12}')" + echo "sha=${SHA:0:12}" >> ${GITHUB_OUTPUT} + + COMMITTER=$(jq -r '.committer.name' commit.json) + echo "Found committer: '${COMMITTER}'" + echo "committer=${COMMITTER}" >> ${GITHUB_OUTPUT} + + - name: set patchwork check + if: startswith(github.ref, 'refs/tags/patchew/') + run: | + CHECK_URLS=() + set_url() { local series_url + series_url=$(curl ${CURL_OPT} "${URL}" | jq -r 'last(last(.[].series)[].url)') + if [ -z "${series_url}" ] || [ "${series_url}" = "null" ]; then + echo "Series not found: '${series_url}' '${URL}'" + return 1 + fi + + echo "Found Series: '${series_url}'" + + readarray -t CHECK_URLS < <(curl ${CURL_OPT} "${series_url}" | jq -r '.patches[].url + "checks/"') + } + + # $1: title, $2: status, $3: url + submit() { local check_url + if [[ "${2}" == "Success"* ]]; then + STATE="success" + elif [[ "${2}" == "Unstable"* ]]; then + STATE="warning" + else + STATE="fail" + fi + + for check_url in "${CHECK_URLS[@]}"; do + curl ${CURL_OPT} \ + -X POST \ + -H "Authorization: Token ${{ secrets.PW_TOKEN }}" \ + -F "state=${STATE}" \ + -F "target_url=${3}" \ + -F "context=${1//[ :()]/_}" \ + -F "description=${2}" \ + "${check_url}" | jq '.' + done + } + + for i in $(seq 30); do # patches can take a bit of time to appear + set_url && break + sleep 1m + done + + if [ "${#CHECK_URLS[@]}" -eq 0 ]; then + echo "Error: didn't find any URLs after ${i} attempts" + exit 1 + fi + echo "Found: ${#CHECK_URLS[@]} urls after ${i} attempts: ${CHECK_URLS[@]}" + + submit "${{ steps.test.outputs.ccl_title_normal }}" "${{ steps.test.outputs.ccl_status_normal }}" "${{ steps.test.outputs.url }}" + submit "${{ steps.test.outputs.ccl_title_debug }}" "${{ steps.test.outputs.ccl_status_debug }}" "${{ steps.test.outputs.url }}" + submit "${{ steps.test.outputs.ccl_title_btf_normal }}" "${{ steps.test.outputs.ccl_status_btf_normal }}" "${{ steps.test.outputs.url }}" + submit "${{ steps.test.outputs.ccl_title_btf_debug }}" "${{ steps.test.outputs.ccl_status_btf_debug }}" "${{ steps.test.outputs.url }}" + env: + URL: "${{ env.PW }}/patches/?project=mptcp&msgid=${{ steps.commit.outputs.mid }}" + + # do that after having set patchwork checks, so we already waited for it to be ready + - name: get series info + id: series + if: startswith(github.ref, 'refs/tags/patchew/') + run: | + if [ -n "${MID:1:-1}" ]; then + # get cover-letter and series' name if any + URL_PW_SERIES_API=$(curl "${URL_PW}${MID:1:-1}" | jq -er 'last(last(.[].series)[].url)' || true) + if [ -n "${URL_PW_SERIES_API}" ] && [ "${URL_PW_SERIES_API}" != "null" ]; then + echo "series=${URL_PW_SERIES}$(basename "${URL_PW_SERIES_API}")" >> ${GITHUB_OUTPUT} + if curl "${URL_PW_SERIES_API}" > pw_series.json && [ -s pw_series.json ]; then + CL="$(jq '.cover_letter' pw_series.json || true)" + if [ -n "${CL}" ] && [ "${CL}" != "null" ] && [ "${CL}" != "{}" ]; then + MID=$(echo "${CL}" | jq -er '.msgid' || echo "${MID}") + SUBJECT=$(jq -er '.name' pw_series.json || echo "${SUBJECT}") + fi + fi + fi + # get tags from Lore: not fully available from Patchwork + SUBJECT="$(curl "${URL_LORE//MID/${MID:1:-1}}" | grep '^Subject: ' | head -n1 | sed 's/^Subject: \(\[.*\] \).*/\1/')${SUBJECT}" + fi + + echo "Found message ID: '${MID}'" + echo "mid=${MID:1:-1}" >> ${GITHUB_OUTPUT} + echo "Found subject: '${SUBJECT}'" + echo "subject=${SUBJECT}" >> ${GITHUB_OUTPUT} + env: + URL_PW: "${{ env.PW }}/patches/?project=mptcp&msgid=" + URL_PW_SERIES: "https://patchwork.kernel.org/project/mptcp/list/?series=" + URL_LORE: "https://lore.kernel.org/mptcp/MID/raw" + MID: "<${{ steps.commit.outputs.mid }}>" + SUBJECT: "${{ steps.commit.outputs.subject }}" + + - name: send email + if: startswith(github.ref, 'refs/tags/patchew/') + uses: dawidd6/action-send-mail@v3 + with: + server_address: smtp.gmail.com + server_port: 465 + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + to: ${{ steps.commit.outputs.author }} + cc: mptcp@lists.linux.dev + from: MPTCP CI + reply_to: mptcp@lists.linux.dev + in_reply_to: "<${{ steps.series.outputs.mid }}>" + subject: "Re: ${{ steps.series.outputs.subject }}" + body: | + Hi ${{ steps.commit.outputs.name }}, + + Thank you for your modifications, that's great! + + Our CI did some validations and here is its report: + + - ${{ steps.test.outputs.ccl_title_normal }}: ${{ steps.test.outputs.ccl_status_normal }} + - ${{ steps.test.outputs.ccl_title_debug }}: ${{ steps.test.outputs.ccl_status_debug }} + - ${{ steps.test.outputs.ccl_title_btf_normal }}: ${{ steps.test.outputs.ccl_status_btf_normal }} + - ${{ steps.test.outputs.ccl_title_btf_debug }}: ${{ steps.test.outputs.ccl_status_btf_debug }} + - Task: ${{ steps.test.outputs.url }} + + Initiator: ${{ steps.commit.outputs.committer }} + Commits: ${{ github.server_url }}/${{ github.repository }}/commits/${{ steps.commit.outputs.sha }} + Patchwork: ${{ steps.series.outputs.series }} + + + If there are some issues, you can reproduce them using the same environment as + the one used by the CI thanks to a docker image, e.g.: + + $ cd [kernel source code] + $ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm -it \ + --pull always mptcp/mptcp-upstream-virtme-docker:latest \ + auto-normal + + For more details: + + https://github.com/multipath-tcp/mptcp-upstream-virtme-docker + + + Please note that despite all the efforts that have been already done to have a + stable tests suite when executed on a public CI like here, it is possible some + reported issues are not due to your modifications. Still, do not hesitate to + help us improve that ;-) + + Cheers, + MPTCP GH Action bot + Bot operated by Matthieu Baerts (NGI0 Core) + + status: + name: "Status" + needs: tests + # only for the non official repos + if: always() && github.repository_owner != 'multipath-tcp' + runs-on: ubuntu-latest + steps: + - name: Get Results + uses: actions/download-artifact@v4 + with: + pattern: results-* + merge-multiple: false + + - name: Check Status + run: | + issues=() + for mode in normal debug btf-normal btf-debug; do + ccl="results-${mode}/conclusion.txt" + if [ ! -f "${ccl}" ] || ! grep -q "Success" "${ccl}"; then + issues+=("${mode}") + fi + done + if [ ${#issues[@]} -eq 0 ]; then + echo "Great, no issues!" + exit 0 + fi + echo "Issues have been found during the tests in: ${issues[*]}." + echo "Please check the summary page for more details:" + echo " ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + exit 1 diff --git a/.github/workflows/update-tg-tree.yml b/.github/workflows/update-tg-tree.yml new file mode 100644 index 0000000000000..767de620ee2cf --- /dev/null +++ b/.github/workflows/update-tg-tree.yml @@ -0,0 +1,54 @@ +name: "Update TopGit tree" + +on: + workflow_dispatch: + inputs: + keep_base_untouched: + description: 'Set it to 1 to force a sync without updating the base from upstream' + required: true + default: '0' + force_sync: + description: 'Set it to 1 to force a sync even if net-next is not updated' + required: true + default: '0' + force_upd_net: + description: 'Set it to 1 to force updating the -net base with upstream instead of the merge-base with net-next' + required: true + default: '1' + + schedule: + - cron: '33 5 * * 1-5' # in UTC: after US West coast's work day + +permissions: {} + +jobs: + export: + if: github.repository_owner == 'multipath-tcp' + runs-on: ubuntu-latest + permissions: + contents: write # for git push (multipath-tcp/mptcp-upstream-topgit-action) + + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 # we need all commits for TopGit + token: '${{ secrets.PAT_MATTTBE }}' + + - name: "Update TG tree" + uses: multipath-tcp/mptcp-upstream-topgit-action@main + with: + not_base: "${{ github.event.inputs.keep_base_untouched || '0' }}" + force_sync: "${{ github.event.inputs.force_sync || '0' }}" + force_upd_net: "${{ github.event.inputs.force_upd_net || '1' }}" + + - name: irc topgit + if: failure() && github.repository_owner == 'multipath-tcp' + uses: rectalogic/notify-irc@v2 + with: + server: irc.libera.chat + channel: "#mptcp" + nickname: gh-topgit-bot + verbose: true + message: |- + New sync with latest net and net-next failed (initiated by ${{ github.actor }}): ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/MAINTAINERS b/MAINTAINERS index 8019d5a975468..a5b0d1c7b1186 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2877,7 +2877,7 @@ F: drivers/pinctrl/nxp/ ARM/NXP S32G/S32R DWMAC ETHERNET DRIVER M: Jan Petrous -L: NXP S32 Linux Team +R: s32@nxp.com S: Maintained F: Documentation/devicetree/bindings/net/nxp,s32-dwmac.yaml F: drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..bcf5c945a0a8d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Security Policy + +## Supported Versions + +The ones mentioned on [kernel.org](https://www.kernel.org). + +## Reporting a Vulnerability + +Please report any issues to us, either via +[GitHub](https://github.com/multipath-tcp/mptcp_net-next/security/advisories/new), +or via emails to the MPTCP maintainers: + + - Matthieu Baerts + - Mat Martineau + +(Check the [MAINTAINERS](https://github.com/multipath-tcp/mptcp_net-next/blob/export/MAINTAINERS) +file to get the up-to-date list.) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 90966dfbd2781..8149e53fd0a76 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2102,7 +2102,8 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) return submit_or_queue_tx_urb(hdev, urb); case HCI_SCODATA_PKT: - if (hci_conn_num(hdev, SCO_LINK) < 1) + if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && + hci_conn_num(hdev, SCO_LINK) < 1) return -ENODEV; urb = alloc_isoc_urb(hdev, skb); @@ -2576,7 +2577,8 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb) return submit_or_queue_tx_urb(hdev, urb); case HCI_SCODATA_PKT: - if (hci_conn_num(hdev, SCO_LINK) < 1) + if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && + hci_conn_num(hdev, SCO_LINK) < 1) return -ENODEV; urb = alloc_isoc_urb(hdev, skb); diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig index 6989972eebc30..10687722d14c0 100644 --- a/drivers/net/dsa/realtek/Kconfig +++ b/drivers/net/dsa/realtek/Kconfig @@ -43,4 +43,10 @@ config NET_DSA_REALTEK_RTL8366RB help Select to enable support for Realtek RTL8366RB. +config NET_DSA_REALTEK_RTL8366RB_LEDS + bool "Support RTL8366RB LED control" + depends on (LEDS_CLASS=y || LEDS_CLASS=NET_DSA_REALTEK_RTL8366RB) + depends on NET_DSA_REALTEK_RTL8366RB + default NET_DSA_REALTEK_RTL8366RB + endif diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile index 35491dc20d6d6..17367bcba496c 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -12,4 +12,7 @@ endif obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o rtl8366-objs := rtl8366-core.o rtl8366rb.o +ifdef CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS +rtl8366-objs += rtl8366rb-leds.o +endif obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o diff --git a/drivers/net/dsa/realtek/rtl8366rb-leds.c b/drivers/net/dsa/realtek/rtl8366rb-leds.c new file mode 100644 index 0000000000000..99c890681ae60 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8366rb-leds.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include "rtl83xx.h" +#include "rtl8366rb.h" + +static inline u32 rtl8366rb_led_group_port_mask(u8 led_group, u8 port) +{ + switch (led_group) { + case 0: + return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); + case 1: + return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); + case 2: + return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); + case 3: + return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); + default: + return 0; + } +} + +static int rb8366rb_get_port_led(struct rtl8366rb_led *led) +{ + struct realtek_priv *priv = led->priv; + u8 led_group = led->led_group; + u8 port_num = led->port_num; + int ret; + u32 val; + + ret = regmap_read(priv->map, RTL8366RB_LED_X_X_CTRL_REG(led_group), + &val); + if (ret) { + dev_err(priv->dev, "error reading LED on port %d group %d\n", + led_group, port_num); + return ret; + } + + return !!(val & rtl8366rb_led_group_port_mask(led_group, port_num)); +} + +static int rb8366rb_set_port_led(struct rtl8366rb_led *led, bool enable) +{ + struct realtek_priv *priv = led->priv; + u8 led_group = led->led_group; + u8 port_num = led->port_num; + int ret; + + ret = regmap_update_bits(priv->map, + RTL8366RB_LED_X_X_CTRL_REG(led_group), + rtl8366rb_led_group_port_mask(led_group, + port_num), + enable ? 0xffff : 0); + if (ret) { + dev_err(priv->dev, "error updating LED on port %d group %d\n", + led_group, port_num); + return ret; + } + + /* Change the LED group to manual controlled LEDs if required */ + ret = rb8366rb_set_ledgroup_mode(priv, led_group, + RTL8366RB_LEDGROUP_FORCE); + + if (ret) { + dev_err(priv->dev, "error updating LED GROUP group %d\n", + led_group); + return ret; + } + + return 0; +} + +static int +rtl8366rb_cled_brightness_set_blocking(struct led_classdev *ldev, + enum led_brightness brightness) +{ + struct rtl8366rb_led *led = container_of(ldev, struct rtl8366rb_led, + cdev); + + return rb8366rb_set_port_led(led, brightness == LED_ON); +} + +static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, + struct fwnode_handle *led_fwnode) +{ + struct rtl8366rb *rb = priv->chip_data; + struct led_init_data init_data = { }; + enum led_default_state state; + struct rtl8366rb_led *led; + u32 led_group; + int ret; + + ret = fwnode_property_read_u32(led_fwnode, "reg", &led_group); + if (ret) + return ret; + + if (led_group >= RTL8366RB_NUM_LEDGROUPS) { + dev_warn(priv->dev, "Invalid LED reg %d defined for port %d", + led_group, dp->index); + return -EINVAL; + } + + led = &rb->leds[dp->index][led_group]; + led->port_num = dp->index; + led->led_group = led_group; + led->priv = priv; + + state = led_init_default_state_get(led_fwnode); + switch (state) { + case LEDS_DEFSTATE_ON: + led->cdev.brightness = 1; + rb8366rb_set_port_led(led, 1); + break; + case LEDS_DEFSTATE_KEEP: + led->cdev.brightness = + rb8366rb_get_port_led(led); + break; + case LEDS_DEFSTATE_OFF: + default: + led->cdev.brightness = 0; + rb8366rb_set_port_led(led, 0); + } + + led->cdev.max_brightness = 1; + led->cdev.brightness_set_blocking = + rtl8366rb_cled_brightness_set_blocking; + init_data.fwnode = led_fwnode; + init_data.devname_mandatory = true; + + init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d", + dp->ds->index, dp->index, led_group); + if (!init_data.devicename) + return -ENOMEM; + + ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data); + if (ret) { + dev_warn(priv->dev, "Failed to init LED %d for port %d", + led_group, dp->index); + return ret; + } + + return 0; +} + +int rtl8366rb_setup_leds(struct realtek_priv *priv) +{ + struct dsa_switch *ds = &priv->ds; + struct device_node *leds_np; + struct dsa_port *dp; + int ret = 0; + + dsa_switch_for_each_port(dp, ds) { + if (!dp->dn) + continue; + + leds_np = of_get_child_by_name(dp->dn, "leds"); + if (!leds_np) { + dev_dbg(priv->dev, "No leds defined for port %d", + dp->index); + continue; + } + + for_each_child_of_node_scoped(leds_np, led_np) { + ret = rtl8366rb_setup_led(priv, dp, + of_fwnode_handle(led_np)); + if (ret) + break; + } + + of_node_put(leds_np); + if (ret) + return ret; + } + return 0; +} diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index 4c4a95d4380ce..f54771cab56d4 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -27,11 +27,7 @@ #include "realtek-smi.h" #include "realtek-mdio.h" #include "rtl83xx.h" - -#define RTL8366RB_PORT_NUM_CPU 5 -#define RTL8366RB_NUM_PORTS 6 -#define RTL8366RB_PHY_NO_MAX 4 -#define RTL8366RB_PHY_ADDR_MAX 31 +#include "rtl8366rb.h" /* Switch Global Configuration register */ #define RTL8366RB_SGCR 0x0000 @@ -176,39 +172,6 @@ */ #define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f -/* LED control registers */ -/* The LED blink rate is global; it is used by all triggers in all groups. */ -#define RTL8366RB_LED_BLINKRATE_REG 0x0430 -#define RTL8366RB_LED_BLINKRATE_MASK 0x0007 -#define RTL8366RB_LED_BLINKRATE_28MS 0x0000 -#define RTL8366RB_LED_BLINKRATE_56MS 0x0001 -#define RTL8366RB_LED_BLINKRATE_84MS 0x0002 -#define RTL8366RB_LED_BLINKRATE_111MS 0x0003 -#define RTL8366RB_LED_BLINKRATE_222MS 0x0004 -#define RTL8366RB_LED_BLINKRATE_446MS 0x0005 - -/* LED trigger event for each group */ -#define RTL8366RB_LED_CTRL_REG 0x0431 -#define RTL8366RB_LED_CTRL_OFFSET(led_group) \ - (4 * (led_group)) -#define RTL8366RB_LED_CTRL_MASK(led_group) \ - (0xf << RTL8366RB_LED_CTRL_OFFSET(led_group)) - -/* The RTL8366RB_LED_X_X registers are used to manually set the LED state only - * when the corresponding LED group in RTL8366RB_LED_CTRL_REG is - * RTL8366RB_LEDGROUP_FORCE. Otherwise, it is ignored. - */ -#define RTL8366RB_LED_0_1_CTRL_REG 0x0432 -#define RTL8366RB_LED_2_3_CTRL_REG 0x0433 -#define RTL8366RB_LED_X_X_CTRL_REG(led_group) \ - ((led_group) <= 1 ? \ - RTL8366RB_LED_0_1_CTRL_REG : \ - RTL8366RB_LED_2_3_CTRL_REG) -#define RTL8366RB_LED_0_X_CTRL_MASK GENMASK(5, 0) -#define RTL8366RB_LED_X_1_CTRL_MASK GENMASK(11, 6) -#define RTL8366RB_LED_2_X_CTRL_MASK GENMASK(5, 0) -#define RTL8366RB_LED_X_3_CTRL_MASK GENMASK(11, 6) - #define RTL8366RB_MIB_COUNT 33 #define RTL8366RB_GLOBAL_MIB_COUNT 1 #define RTL8366RB_MIB_COUNTER_PORT_OFFSET 0x0050 @@ -244,7 +207,6 @@ #define RTL8366RB_PORT_STATUS_AN_MASK 0x0080 #define RTL8366RB_NUM_VLANS 16 -#define RTL8366RB_NUM_LEDGROUPS 4 #define RTL8366RB_NUM_VIDS 4096 #define RTL8366RB_PRIORITYMAX 7 #define RTL8366RB_NUM_FIDS 8 @@ -351,46 +313,6 @@ #define RTL8366RB_GREEN_FEATURE_TX BIT(0) #define RTL8366RB_GREEN_FEATURE_RX BIT(2) -enum rtl8366_ledgroup_mode { - RTL8366RB_LEDGROUP_OFF = 0x0, - RTL8366RB_LEDGROUP_DUP_COL = 0x1, - RTL8366RB_LEDGROUP_LINK_ACT = 0x2, - RTL8366RB_LEDGROUP_SPD1000 = 0x3, - RTL8366RB_LEDGROUP_SPD100 = 0x4, - RTL8366RB_LEDGROUP_SPD10 = 0x5, - RTL8366RB_LEDGROUP_SPD1000_ACT = 0x6, - RTL8366RB_LEDGROUP_SPD100_ACT = 0x7, - RTL8366RB_LEDGROUP_SPD10_ACT = 0x8, - RTL8366RB_LEDGROUP_SPD100_10_ACT = 0x9, - RTL8366RB_LEDGROUP_FIBER = 0xa, - RTL8366RB_LEDGROUP_AN_FAULT = 0xb, - RTL8366RB_LEDGROUP_LINK_RX = 0xc, - RTL8366RB_LEDGROUP_LINK_TX = 0xd, - RTL8366RB_LEDGROUP_MASTER = 0xe, - RTL8366RB_LEDGROUP_FORCE = 0xf, - - __RTL8366RB_LEDGROUP_MODE_MAX -}; - -struct rtl8366rb_led { - u8 port_num; - u8 led_group; - struct realtek_priv *priv; - struct led_classdev cdev; -}; - -/** - * struct rtl8366rb - RTL8366RB-specific data - * @max_mtu: per-port max MTU setting - * @pvid_enabled: if PVID is set for respective port - * @leds: per-port and per-ledgroup led info - */ -struct rtl8366rb { - unsigned int max_mtu[RTL8366RB_NUM_PORTS]; - bool pvid_enabled[RTL8366RB_NUM_PORTS]; - struct rtl8366rb_led leds[RTL8366RB_NUM_PORTS][RTL8366RB_NUM_LEDGROUPS]; -}; - static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { { 0, 0, 4, "IfInOctets" }, { 0, 4, 4, "EtherStatsOctets" }, @@ -831,9 +753,10 @@ static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, return 0; } -static int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv, - u8 led_group, - enum rtl8366_ledgroup_mode mode) +/* This code is used also with LEDs disabled */ +int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv, + u8 led_group, + enum rtl8366_ledgroup_mode mode) { int ret; u32 val; @@ -850,144 +773,7 @@ static int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv, return 0; } -static inline u32 rtl8366rb_led_group_port_mask(u8 led_group, u8 port) -{ - switch (led_group) { - case 0: - return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); - case 1: - return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); - case 2: - return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); - case 3: - return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); - default: - return 0; - } -} - -static int rb8366rb_get_port_led(struct rtl8366rb_led *led) -{ - struct realtek_priv *priv = led->priv; - u8 led_group = led->led_group; - u8 port_num = led->port_num; - int ret; - u32 val; - - ret = regmap_read(priv->map, RTL8366RB_LED_X_X_CTRL_REG(led_group), - &val); - if (ret) { - dev_err(priv->dev, "error reading LED on port %d group %d\n", - led_group, port_num); - return ret; - } - - return !!(val & rtl8366rb_led_group_port_mask(led_group, port_num)); -} - -static int rb8366rb_set_port_led(struct rtl8366rb_led *led, bool enable) -{ - struct realtek_priv *priv = led->priv; - u8 led_group = led->led_group; - u8 port_num = led->port_num; - int ret; - - ret = regmap_update_bits(priv->map, - RTL8366RB_LED_X_X_CTRL_REG(led_group), - rtl8366rb_led_group_port_mask(led_group, - port_num), - enable ? 0xffff : 0); - if (ret) { - dev_err(priv->dev, "error updating LED on port %d group %d\n", - led_group, port_num); - return ret; - } - - /* Change the LED group to manual controlled LEDs if required */ - ret = rb8366rb_set_ledgroup_mode(priv, led_group, - RTL8366RB_LEDGROUP_FORCE); - - if (ret) { - dev_err(priv->dev, "error updating LED GROUP group %d\n", - led_group); - return ret; - } - - return 0; -} - -static int -rtl8366rb_cled_brightness_set_blocking(struct led_classdev *ldev, - enum led_brightness brightness) -{ - struct rtl8366rb_led *led = container_of(ldev, struct rtl8366rb_led, - cdev); - - return rb8366rb_set_port_led(led, brightness == LED_ON); -} - -static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, - struct fwnode_handle *led_fwnode) -{ - struct rtl8366rb *rb = priv->chip_data; - struct led_init_data init_data = { }; - enum led_default_state state; - struct rtl8366rb_led *led; - u32 led_group; - int ret; - - ret = fwnode_property_read_u32(led_fwnode, "reg", &led_group); - if (ret) - return ret; - - if (led_group >= RTL8366RB_NUM_LEDGROUPS) { - dev_warn(priv->dev, "Invalid LED reg %d defined for port %d", - led_group, dp->index); - return -EINVAL; - } - - led = &rb->leds[dp->index][led_group]; - led->port_num = dp->index; - led->led_group = led_group; - led->priv = priv; - - state = led_init_default_state_get(led_fwnode); - switch (state) { - case LEDS_DEFSTATE_ON: - led->cdev.brightness = 1; - rb8366rb_set_port_led(led, 1); - break; - case LEDS_DEFSTATE_KEEP: - led->cdev.brightness = - rb8366rb_get_port_led(led); - break; - case LEDS_DEFSTATE_OFF: - default: - led->cdev.brightness = 0; - rb8366rb_set_port_led(led, 0); - } - - led->cdev.max_brightness = 1; - led->cdev.brightness_set_blocking = - rtl8366rb_cled_brightness_set_blocking; - init_data.fwnode = led_fwnode; - init_data.devname_mandatory = true; - - init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d", - dp->ds->index, dp->index, led_group); - if (!init_data.devicename) - return -ENOMEM; - - ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data); - if (ret) { - dev_warn(priv->dev, "Failed to init LED %d for port %d", - led_group, dp->index); - return ret; - } - - return 0; -} - +/* This code is used also with LEDs disabled */ static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv) { int ret = 0; @@ -1008,38 +794,6 @@ static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv) return ret; } -static int rtl8366rb_setup_leds(struct realtek_priv *priv) -{ - struct dsa_switch *ds = &priv->ds; - struct device_node *leds_np; - struct dsa_port *dp; - int ret = 0; - - dsa_switch_for_each_port(dp, ds) { - if (!dp->dn) - continue; - - leds_np = of_get_child_by_name(dp->dn, "leds"); - if (!leds_np) { - dev_dbg(priv->dev, "No leds defined for port %d", - dp->index); - continue; - } - - for_each_child_of_node_scoped(leds_np, led_np) { - ret = rtl8366rb_setup_led(priv, dp, - of_fwnode_handle(led_np)); - if (ret) - break; - } - - of_node_put(leds_np); - if (ret) - return ret; - } - return 0; -} - static int rtl8366rb_setup(struct dsa_switch *ds) { struct realtek_priv *priv = ds->priv; diff --git a/drivers/net/dsa/realtek/rtl8366rb.h b/drivers/net/dsa/realtek/rtl8366rb.h new file mode 100644 index 0000000000000..685ff3275faa1 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8366rb.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _RTL8366RB_H +#define _RTL8366RB_H + +#include "realtek.h" + +#define RTL8366RB_PORT_NUM_CPU 5 +#define RTL8366RB_NUM_PORTS 6 +#define RTL8366RB_PHY_NO_MAX 4 +#define RTL8366RB_NUM_LEDGROUPS 4 +#define RTL8366RB_PHY_ADDR_MAX 31 + +/* LED control registers */ +/* The LED blink rate is global; it is used by all triggers in all groups. */ +#define RTL8366RB_LED_BLINKRATE_REG 0x0430 +#define RTL8366RB_LED_BLINKRATE_MASK 0x0007 +#define RTL8366RB_LED_BLINKRATE_28MS 0x0000 +#define RTL8366RB_LED_BLINKRATE_56MS 0x0001 +#define RTL8366RB_LED_BLINKRATE_84MS 0x0002 +#define RTL8366RB_LED_BLINKRATE_111MS 0x0003 +#define RTL8366RB_LED_BLINKRATE_222MS 0x0004 +#define RTL8366RB_LED_BLINKRATE_446MS 0x0005 + +/* LED trigger event for each group */ +#define RTL8366RB_LED_CTRL_REG 0x0431 +#define RTL8366RB_LED_CTRL_OFFSET(led_group) \ + (4 * (led_group)) +#define RTL8366RB_LED_CTRL_MASK(led_group) \ + (0xf << RTL8366RB_LED_CTRL_OFFSET(led_group)) + +/* The RTL8366RB_LED_X_X registers are used to manually set the LED state only + * when the corresponding LED group in RTL8366RB_LED_CTRL_REG is + * RTL8366RB_LEDGROUP_FORCE. Otherwise, it is ignored. + */ +#define RTL8366RB_LED_0_1_CTRL_REG 0x0432 +#define RTL8366RB_LED_2_3_CTRL_REG 0x0433 +#define RTL8366RB_LED_X_X_CTRL_REG(led_group) \ + ((led_group) <= 1 ? \ + RTL8366RB_LED_0_1_CTRL_REG : \ + RTL8366RB_LED_2_3_CTRL_REG) +#define RTL8366RB_LED_0_X_CTRL_MASK GENMASK(5, 0) +#define RTL8366RB_LED_X_1_CTRL_MASK GENMASK(11, 6) +#define RTL8366RB_LED_2_X_CTRL_MASK GENMASK(5, 0) +#define RTL8366RB_LED_X_3_CTRL_MASK GENMASK(11, 6) + +enum rtl8366_ledgroup_mode { + RTL8366RB_LEDGROUP_OFF = 0x0, + RTL8366RB_LEDGROUP_DUP_COL = 0x1, + RTL8366RB_LEDGROUP_LINK_ACT = 0x2, + RTL8366RB_LEDGROUP_SPD1000 = 0x3, + RTL8366RB_LEDGROUP_SPD100 = 0x4, + RTL8366RB_LEDGROUP_SPD10 = 0x5, + RTL8366RB_LEDGROUP_SPD1000_ACT = 0x6, + RTL8366RB_LEDGROUP_SPD100_ACT = 0x7, + RTL8366RB_LEDGROUP_SPD10_ACT = 0x8, + RTL8366RB_LEDGROUP_SPD100_10_ACT = 0x9, + RTL8366RB_LEDGROUP_FIBER = 0xa, + RTL8366RB_LEDGROUP_AN_FAULT = 0xb, + RTL8366RB_LEDGROUP_LINK_RX = 0xc, + RTL8366RB_LEDGROUP_LINK_TX = 0xd, + RTL8366RB_LEDGROUP_MASTER = 0xe, + RTL8366RB_LEDGROUP_FORCE = 0xf, + + __RTL8366RB_LEDGROUP_MODE_MAX +}; + +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS) + +struct rtl8366rb_led { + u8 port_num; + u8 led_group; + struct realtek_priv *priv; + struct led_classdev cdev; +}; + +int rtl8366rb_setup_leds(struct realtek_priv *priv); + +#else + +static inline int rtl8366rb_setup_leds(struct realtek_priv *priv) +{ + return 0; +} + +#endif /* IS_ENABLED(CONFIG_LEDS_CLASS) */ + +/** + * struct rtl8366rb - RTL8366RB-specific data + * @max_mtu: per-port max MTU setting + * @pvid_enabled: if PVID is set for respective port + * @leds: per-port and per-ledgroup led info + */ +struct rtl8366rb { + unsigned int max_mtu[RTL8366RB_NUM_PORTS]; + bool pvid_enabled[RTL8366RB_NUM_PORTS]; +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS) + struct rtl8366rb_led leds[RTL8366RB_NUM_PORTS][RTL8366RB_NUM_LEDGROUPS]; +#endif +}; + +/* This code is used also with LEDs disabled */ +int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv, + u8 led_group, + enum rtl8366_ledgroup_mode mode); + +#endif /* _RTL8366RB_H */ diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index f69b2b7c88027..2a3924f01bf44 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1277,6 +1277,8 @@ struct macb { struct clk *rx_clk; struct clk *tsu_clk; struct net_device *dev; + /* Protects hw_stats and ethtool_stats */ + spinlock_t stats_lock; union { struct macb_stats macb; struct gem_stats gem; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 5345f3e1a7957..6c462de81f200 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -1989,10 +1989,12 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) if (status & MACB_BIT(ISR_ROVR)) { /* We missed at least one packet */ + spin_lock(&bp->stats_lock); if (macb_is_gem(bp)) bp->hw_stats.gem.rx_overruns++; else bp->hw_stats.macb.rx_overruns++; + spin_unlock(&bp->stats_lock); if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) queue_writel(queue, ISR, MACB_BIT(ISR_ROVR)); @@ -3112,6 +3114,7 @@ static void gem_get_stats(struct macb *bp, struct rtnl_link_stats64 *nstat) { struct gem_stats *hwstat = &bp->hw_stats.gem; + spin_lock_irq(&bp->stats_lock); if (netif_running(bp->dev)) gem_update_stats(bp); @@ -3142,17 +3145,19 @@ static void gem_get_stats(struct macb *bp, struct rtnl_link_stats64 *nstat) nstat->tx_aborted_errors = hwstat->tx_excessive_collisions; nstat->tx_carrier_errors = hwstat->tx_carrier_sense_errors; nstat->tx_fifo_errors = hwstat->tx_underrun; + spin_unlock_irq(&bp->stats_lock); } static void gem_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { - struct macb *bp; + struct macb *bp = netdev_priv(dev); - bp = netdev_priv(dev); + spin_lock_irq(&bp->stats_lock); gem_update_stats(bp); memcpy(data, &bp->ethtool_stats, sizeof(u64) * (GEM_STATS_LEN + QUEUE_STATS_LEN * MACB_MAX_QUEUES)); + spin_unlock_irq(&bp->stats_lock); } static int gem_get_sset_count(struct net_device *dev, int sset) @@ -3205,6 +3210,7 @@ static void macb_get_stats(struct net_device *dev, } /* read stats from hardware */ + spin_lock_irq(&bp->stats_lock); macb_update_stats(bp); /* Convert HW stats into netdevice stats */ @@ -3238,6 +3244,7 @@ static void macb_get_stats(struct net_device *dev, nstat->tx_carrier_errors = hwstat->tx_carrier_errors; nstat->tx_fifo_errors = hwstat->tx_underruns; /* Don't know about heartbeat or window errors... */ + spin_unlock_irq(&bp->stats_lock); } static void macb_get_pause_stats(struct net_device *dev, @@ -5263,6 +5270,7 @@ static int macb_probe(struct platform_device *pdev) } } spin_lock_init(&bp->lock); + spin_lock_init(&bp->stats_lock); /* setup capabilities */ macb_configure_caps(bp, macb_config); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c index 60a4e3330ccd0..16fd24facb0ee 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c @@ -515,6 +515,19 @@ static int loongson_dwmac_acpi_config(struct pci_dev *pdev, return 0; } +/* Loongson's DWMAC device may take nearly two seconds to complete DMA reset */ +static int loongson_dwmac_fix_reset(void *priv, void __iomem *ioaddr) +{ + u32 value = readl(ioaddr + DMA_BUS_MODE); + + value |= DMA_BUS_MODE_SFT_RESET; + writel(value, ioaddr + DMA_BUS_MODE); + + return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value, + !(value & DMA_BUS_MODE_SFT_RESET), + 10000, 2000000); +} + static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct plat_stmmacenet_data *plat; @@ -565,6 +578,7 @@ static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id plat->bsp_priv = ld; plat->setup = loongson_dwmac_setup; + plat->fix_soc_reset = loongson_dwmac_fix_reset; ld->dev = &pdev->dev; ld->loongson_id = readl(res.addr + GMAC_VERSION) & 0xff; diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index fd591ddb3884d..ca62188a317ad 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -416,20 +416,25 @@ struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port, void *lyr3h, static noinline_for_stack int ipvlan_process_v4_outbound(struct sk_buff *skb) { - const struct iphdr *ip4h = ip_hdr(skb); struct net_device *dev = skb->dev; struct net *net = dev_net(dev); - struct rtable *rt; int err, ret = NET_XMIT_DROP; + const struct iphdr *ip4h; + struct rtable *rt; struct flowi4 fl4 = { .flowi4_oif = dev->ifindex, - .flowi4_tos = inet_dscp_to_dsfield(ip4h_dscp(ip4h)), .flowi4_flags = FLOWI_FLAG_ANYSRC, .flowi4_mark = skb->mark, - .daddr = ip4h->daddr, - .saddr = ip4h->saddr, }; + if (!pskb_network_may_pull(skb, sizeof(struct iphdr))) + goto err; + + ip4h = ip_hdr(skb); + fl4.daddr = ip4h->daddr; + fl4.saddr = ip4h->saddr; + fl4.flowi4_tos = inet_dscp_to_dsfield(ip4h_dscp(ip4h)); + rt = ip_route_output_flow(net, &fl4, NULL); if (IS_ERR(rt)) goto err; @@ -488,6 +493,12 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) struct net_device *dev = skb->dev; int err, ret = NET_XMIT_DROP; + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) { + DEV_STATS_INC(dev, tx_errors); + kfree_skb(skb); + return ret; + } + err = ipvlan_route_v6_outbound(dev, skb); if (unlikely(err)) { DEV_STATS_INC(dev, tx_errors); diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index c8840c3b9a1bc..f1d68153987e1 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -244,8 +244,22 @@ static netdev_tx_t blackhole_netdev_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } +static int blackhole_neigh_output(struct neighbour *n, struct sk_buff *skb) +{ + kfree_skb(skb); + return 0; +} + +static int blackhole_neigh_construct(struct net_device *dev, + struct neighbour *n) +{ + n->output = blackhole_neigh_output; + return 0; +} + static const struct net_device_ops blackhole_netdev_ops = { .ndo_start_xmit = blackhole_netdev_xmit, + .ndo_neigh_construct = blackhole_neigh_construct, }; /* This is a dst-dummy device used specifically for invalidated diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c index 5c80fbee79138..7ab358616e035 100644 --- a/drivers/net/netdevsim/ethtool.c +++ b/drivers/net/netdevsim/ethtool.c @@ -184,9 +184,11 @@ static const struct ethtool_ops nsim_ethtool_ops = { static void nsim_ethtool_ring_init(struct netdevsim *ns) { + ns->ethtool.ring.rx_pending = 512; ns->ethtool.ring.rx_max_pending = 4096; ns->ethtool.ring.rx_jumbo_max_pending = 4096; ns->ethtool.ring.rx_mini_max_pending = 4096; + ns->ethtool.ring.tx_pending = 512; ns->ethtool.ring.tx_max_pending = 4096; } diff --git a/drivers/net/phy/qcom/qca807x.c b/drivers/net/phy/qcom/qca807x.c index 3279de857b474..2ad8c2586d643 100644 --- a/drivers/net/phy/qcom/qca807x.c +++ b/drivers/net/phy/qcom/qca807x.c @@ -774,7 +774,7 @@ static int qca807x_config_init(struct phy_device *phydev) control_dac &= ~QCA807X_CONTROL_DAC_MASK; if (!priv->dac_full_amplitude) control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE; - if (!priv->dac_full_amplitude) + if (!priv->dac_full_bias_current) control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT; if (!priv->dac_disable_bias_current_tweak) control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK; diff --git a/fs/afs/server.c b/fs/afs/server.c index 038f9d0ae3af8..4504e16b458cc 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -163,6 +163,8 @@ static struct afs_server *afs_install_server(struct afs_cell *cell, rb_insert_color(&server->uuid_rb, &net->fs_servers); hlist_add_head_rcu(&server->proc_link, &net->fs_proc); + afs_get_cell(cell, afs_cell_trace_get_server); + added_dup: write_seqlock(&net->fs_addr_lock); estate = rcu_dereference_protected(server->endpoint_state, @@ -442,6 +444,7 @@ static void afs_server_rcu(struct rcu_head *rcu) atomic_read(&server->active), afs_server_trace_free); afs_put_endpoint_state(rcu_access_pointer(server->endpoint_state), afs_estate_trace_put_server); + afs_put_cell(server->cell, afs_cell_trace_put_server); kfree(server); } diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index 7e7e567a7f8a2..d20cd902ef949 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -97,8 +97,8 @@ struct afs_server_list *afs_alloc_server_list(struct afs_volume *volume, break; if (j < slist->nr_servers) { if (slist->servers[j].server == server) { - afs_put_server(volume->cell->net, server, - afs_server_trace_put_slist_isort); + afs_unuse_server(volume->cell->net, server, + afs_server_trace_put_slist_isort); continue; } diff --git a/include/net/sock.h b/include/net/sock.h index efc031163c33d..e771d99f81b0d 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1742,6 +1742,7 @@ static inline bool sock_allow_reclassification(const struct sock *csk) struct sock *sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot, int kern); void sk_free(struct sock *sk); +void sk_net_refcnt_upgrade(struct sock *sk); void sk_destruct(struct sock *sk); struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority); void sk_free_unlock_clone(struct sock *sk); diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index b0db89058c911..958a2460330c0 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -174,6 +174,7 @@ enum yfs_cm_operation { EM(afs_cell_trace_get_queue_dns, "GET q-dns ") \ EM(afs_cell_trace_get_queue_manage, "GET q-mng ") \ EM(afs_cell_trace_get_queue_new, "GET q-new ") \ + EM(afs_cell_trace_get_server, "GET server") \ EM(afs_cell_trace_get_vol, "GET vol ") \ EM(afs_cell_trace_insert, "INSERT ") \ EM(afs_cell_trace_manage, "MANAGE ") \ @@ -182,6 +183,7 @@ enum yfs_cm_operation { EM(afs_cell_trace_put_destroy, "PUT destry") \ EM(afs_cell_trace_put_queue_work, "PUT q-work") \ EM(afs_cell_trace_put_queue_fail, "PUT q-fail") \ + EM(afs_cell_trace_put_server, "PUT server") \ EM(afs_cell_trace_put_vol, "PUT vol ") \ EM(afs_cell_trace_see_source, "SEE source") \ EM(afs_cell_trace_see_ws, "SEE ws ") \ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fec11e576f310..b22078b679726 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -632,7 +632,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) test_bit(FLAG_HOLD_HCI_CONN, &chan->flags)) hci_conn_hold(conn->hcon); - list_add(&chan->list, &conn->chan_l); + /* Append to the list since the order matters for ECRED */ + list_add_tail(&chan->list, &conn->chan_l); } void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) @@ -3771,7 +3772,11 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data) struct l2cap_ecred_conn_rsp *rsp_flex = container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr); - if (test_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) + /* Check if channel for outgoing connection or if it wasn't deferred + * since in those cases it must be skipped. + */ + if (test_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags) || + !test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags)) return; /* Reset ident so only one response is sent */ diff --git a/net/core/sock.c b/net/core/sock.c index 5ac445f8244b2..23bce41f7f1ff 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2261,6 +2261,7 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority, get_net_track(net, &sk->ns_tracker, priority); sock_inuse_add(net, 1); } else { + net_passive_inc(net); __netns_tracker_alloc(net, &sk->ns_tracker, false, priority); } @@ -2285,6 +2286,7 @@ EXPORT_SYMBOL(sk_alloc); static void __sk_destruct(struct rcu_head *head) { struct sock *sk = container_of(head, struct sock, sk_rcu); + struct net *net = sock_net(sk); struct sk_filter *filter; if (sk->sk_destruct) @@ -2316,14 +2318,28 @@ static void __sk_destruct(struct rcu_head *head) put_cred(sk->sk_peer_cred); put_pid(sk->sk_peer_pid); - if (likely(sk->sk_net_refcnt)) - put_net_track(sock_net(sk), &sk->ns_tracker); - else - __netns_tracker_free(sock_net(sk), &sk->ns_tracker, false); - + if (likely(sk->sk_net_refcnt)) { + put_net_track(net, &sk->ns_tracker); + } else { + __netns_tracker_free(net, &sk->ns_tracker, false); + net_passive_dec(net); + } sk_prot_free(sk->sk_prot_creator, sk); } +void sk_net_refcnt_upgrade(struct sock *sk) +{ + struct net *net = sock_net(sk); + + WARN_ON_ONCE(sk->sk_net_refcnt); + __netns_tracker_free(net, &sk->ns_tracker, false); + net_passive_dec(net); + sk->sk_net_refcnt = 1; + get_net_track(net, &sk->ns_tracker, GFP_KERNEL); + sock_inuse_add(net, 1); +} +EXPORT_SYMBOL_GPL(sk_net_refcnt_upgrade); + void sk_destruct(struct sock *sk) { bool use_call_rcu = sock_flag(sk, SOCK_RCU_FREE); @@ -2420,6 +2436,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) * is not properly dismantling its kernel sockets at netns * destroy time. */ + net_passive_inc(sock_net(newsk)); __netns_tracker_alloc(sock_net(newsk), &newsk->ns_tracker, false, priority); } diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index ad2741f1346af..c7769ee0d9c55 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -34,6 +34,7 @@ static int min_sndbuf = SOCK_MIN_SNDBUF; static int min_rcvbuf = SOCK_MIN_RCVBUF; static int max_skb_frags = MAX_SKB_FRAGS; static int min_mem_pcpu_rsv = SK_MEMORY_PCPU_RESERVE; +static int netdev_budget_usecs_min = 2 * USEC_PER_SEC / HZ; static int net_msg_warn; /* Unused, but still a sysctl */ @@ -587,7 +588,7 @@ static struct ctl_table net_core_table[] = { .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = SYSCTL_ZERO, + .extra1 = &netdev_budget_usecs_min, }, { .procname = "fb_tunnels_only_for_init_net", diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 7149d07e90c69..ac8b6107863e5 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "netlink.h" #include "common.h" @@ -813,6 +814,21 @@ int ethtool_check_ops(const struct ethtool_ops *ops) return 0; } +void ethtool_ringparam_get_cfg(struct net_device *dev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kparam, + struct netlink_ext_ack *extack) +{ + memset(param, 0, sizeof(*param)); + memset(kparam, 0, sizeof(*kparam)); + + param->cmd = ETHTOOL_GRINGPARAM; + dev->ethtool_ops->get_ringparam(dev, param, kparam, extack); + + /* Driver gives us current state, we want to return current config */ + kparam->tcp_data_split = dev->cfg->hds_config; +} + static void ethtool_init_tsinfo(struct kernel_ethtool_ts_info *info) { memset(info, 0, sizeof(*info)); diff --git a/net/ethtool/common.h b/net/ethtool/common.h index 58e9e7db06f90..a1088c2441d0a 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -51,6 +51,12 @@ int ethtool_check_max_channel(struct net_device *dev, struct ethtool_channels channels, struct genl_info *info); int ethtool_check_rss_ctx_busy(struct net_device *dev, u32 rss_context); + +void ethtool_ringparam_get_cfg(struct net_device *dev, + struct ethtool_ringparam *param, + struct kernel_ethtool_ringparam *kparam, + struct netlink_ext_ack *extack); + int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info); int ethtool_get_ts_info_by_phc(struct net_device *dev, struct kernel_ethtool_ts_info *info, diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 271c7cef9ef3e..891b7a17f23b9 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2065,8 +2065,8 @@ static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr) static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr) { - struct ethtool_ringparam ringparam, max = { .cmd = ETHTOOL_GRINGPARAM }; struct kernel_ethtool_ringparam kernel_ringparam; + struct ethtool_ringparam ringparam, max; int ret; if (!dev->ethtool_ops->set_ringparam || !dev->ethtool_ops->get_ringparam) @@ -2075,7 +2075,7 @@ static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr) if (copy_from_user(&ringparam, useraddr, sizeof(ringparam))) return -EFAULT; - dev->ethtool_ops->get_ringparam(dev, &max, &kernel_ringparam, NULL); + ethtool_ringparam_get_cfg(dev, &max, &kernel_ringparam, NULL); /* ensure new ring parameters are within the maximums */ if (ringparam.rx_pending > max.rx_max_pending || diff --git a/net/ethtool/rings.c b/net/ethtool/rings.c index 7839bfd1ac6a0..aeedd5ec6b8cd 100644 --- a/net/ethtool/rings.c +++ b/net/ethtool/rings.c @@ -215,17 +215,16 @@ ethnl_set_rings_validate(struct ethnl_req_info *req_info, static int ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info) { - struct kernel_ethtool_ringparam kernel_ringparam = {}; - struct ethtool_ringparam ringparam = {}; + struct kernel_ethtool_ringparam kernel_ringparam; struct net_device *dev = req_info->dev; + struct ethtool_ringparam ringparam; struct nlattr **tb = info->attrs; const struct nlattr *err_attr; bool mod = false; int ret; - dev->ethtool_ops->get_ringparam(dev, &ringparam, - &kernel_ringparam, info->extack); - kernel_ringparam.tcp_data_split = dev->cfg->hds_config; + ethtool_ringparam_get_cfg(dev, &ringparam, &kernel_ringparam, + info->extack); ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod); ethnl_update_u32(&ringparam.rx_mini_pending, diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index ef85a60151ad7..d4328443d844d 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -1521,11 +1521,6 @@ static int mptcp_nl_remove_subflow_and_signal_addr(struct net *net, if (mptcp_pm_is_userspace(msk)) goto next; - if (list_empty(&msk->conn_list)) { - mptcp_pm_remove_anno_addr(msk, addr, false); - goto next; - } - lock_sock(sk); remove_subflow = mptcp_lookup_subflow_by_saddr(&msk->conn_list, addr); mptcp_pm_remove_anno_addr(msk, addr, remove_subflow && diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 256677c43ca65..abafc99496b91 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -1194,6 +1194,8 @@ static inline void __mptcp_do_fallback(struct mptcp_sock *msk) pr_debug("TCP fallback already done (msk=%p)\n", msk); return; } + if (WARN_ON_ONCE(!READ_ONCE(msk->allow_infinite_fallback))) + return; set_bit(MPTCP_FALLBACK_DONE, &msk->flags); } diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index d2caffa56bdd9..efe8d86496dbd 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1139,7 +1139,6 @@ static enum mapping_status get_mapping_status(struct sock *ssk, if (data_len == 0) { pr_debug("infinite mapping received\n"); MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPRX); - subflow->map_data_len = 0; return MAPPING_INVALID; } @@ -1298,18 +1297,6 @@ static void subflow_sched_work_if_closed(struct mptcp_sock *msk, struct sock *ss mptcp_schedule_work(sk); } -static bool subflow_can_fallback(struct mptcp_subflow_context *subflow) -{ - struct mptcp_sock *msk = mptcp_sk(subflow->conn); - - if (subflow->mp_join) - return false; - else if (READ_ONCE(msk->csum_enabled)) - return !subflow->valid_csum_seen; - else - return READ_ONCE(msk->allow_infinite_fallback); -} - static void mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); @@ -1405,7 +1392,7 @@ static bool subflow_check_data_avail(struct sock *ssk) return true; } - if (!subflow_can_fallback(subflow) && subflow->map_data_len) { + if (!READ_ONCE(msk->allow_infinite_fallback)) { /* fatal protocol error, close the socket. * subflow_error_report() will introduce the appropriate barriers */ @@ -1784,10 +1771,7 @@ int mptcp_subflow_create_socket(struct sock *sk, unsigned short family, * needs it. * Update ns_tracker to current stack trace and refcounted tracker. */ - __netns_tracker_free(net, &sf->sk->ns_tracker, false); - sf->sk->sk_net_refcnt = 1; - get_net_track(net, &sf->sk->ns_tracker, GFP_KERNEL); - sock_inuse_add(net, 1); + sk_net_refcnt_upgrade(sf->sk); err = tcp_set_ulp(sf->sk, "mptcp"); if (err) goto err_free; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index f8f13058a46ed..e8972a857e51e 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -796,16 +796,6 @@ static int netlink_release(struct socket *sock) sock_prot_inuse_add(sock_net(sk), &netlink_proto, -1); - /* Because struct net might disappear soon, do not keep a pointer. */ - if (!sk->sk_net_refcnt && sock_net(sk) != &init_net) { - __netns_tracker_free(sock_net(sk), &sk->ns_tracker, false); - /* Because of deferred_put_nlk_sk and use of work queue, - * it is possible netns will be freed before this socket. - */ - sock_net_set(sk, &init_net); - __netns_tracker_alloc(&init_net, &sk->ns_tracker, - false, GFP_KERNEL); - } call_rcu(&nlk->rcu, deferred_put_nlk_sk); return 0; } diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 0581c53e65170..3cc2f303bf786 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -504,12 +504,8 @@ bool rds_tcp_tune(struct socket *sock) release_sock(sk); return false; } - /* Update ns_tracker to current stack trace and refcounted tracker */ - __netns_tracker_free(net, &sk->ns_tracker, false); - - sk->sk_net_refcnt = 1; - netns_tracker_alloc(net, &sk->ns_tracker, GFP_KERNEL); - sock_inuse_add(net, 1); + sk_net_refcnt_upgrade(sk); + put_net(net); } rtn = net_generic(net, rds_tcp_netid); if (rtn->sndbuf_size > 0) { diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 5e740c4862034..a64a0cab1bf7f 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -360,7 +360,6 @@ struct rxrpc_peer { u8 pmtud_jumbo; /* Max jumbo packets for the MTU */ bool ackr_adv_pmtud; /* T if the peer advertises path-MTU */ unsigned int ackr_max_data; /* Maximum data advertised by peer */ - seqcount_t mtu_lock; /* Lockless MTU access management */ unsigned int if_mtu; /* Local interface MTU (- hdrsize) for this peer */ unsigned int max_data; /* Maximum packet data capacity for this peer */ unsigned short hdrsize; /* header size (IP + UDP + RxRPC) */ diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 9047ba13bd31e..24aceb183c2c3 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -810,9 +810,7 @@ static void rxrpc_input_ack_trailer(struct rxrpc_call *call, struct sk_buff *skb if (max_mtu < peer->max_data) { trace_rxrpc_pmtud_reduce(peer, sp->hdr.serial, max_mtu, rxrpc_pmtud_reduce_ack); - write_seqcount_begin(&peer->mtu_lock); peer->max_data = max_mtu; - write_seqcount_end(&peer->mtu_lock); } max_data = umin(max_mtu, peer->max_data); diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index bc283da9ee402..7f4729234957e 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -130,9 +130,7 @@ static void rxrpc_adjust_mtu(struct rxrpc_peer *peer, unsigned int mtu) peer->pmtud_bad = max_data + 1; trace_rxrpc_pmtud_reduce(peer, 0, max_data, rxrpc_pmtud_reduce_icmp); - write_seqcount_begin(&peer->mtu_lock); peer->max_data = max_data; - write_seqcount_end(&peer->mtu_lock); } } @@ -408,13 +406,8 @@ void rxrpc_input_probe_for_pmtud(struct rxrpc_connection *conn, rxrpc_serial_t a } max_data = umin(max_data, peer->ackr_max_data); - if (max_data != peer->max_data) { - preempt_disable(); - write_seqcount_begin(&peer->mtu_lock); + if (max_data != peer->max_data) peer->max_data = max_data; - write_seqcount_end(&peer->mtu_lock); - preempt_enable(); - } jumbo = max_data + sizeof(struct rxrpc_jumbo_header); jumbo /= RXRPC_JUMBO_SUBPKTLEN; diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index 0fcc87f0409f9..56e09d161a97f 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -235,7 +235,6 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp, peer->service_conns = RB_ROOT; seqlock_init(&peer->service_conn_lock); spin_lock_init(&peer->lock); - seqcount_init(&peer->mtu_lock); peer->debug_id = atomic_inc_return(&rxrpc_debug_id); peer->recent_srtt_us = UINT_MAX; peer->cong_ssthresh = RXRPC_TX_MAX_WINDOW; @@ -325,10 +324,10 @@ void rxrpc_new_incoming_peer(struct rxrpc_local *local, struct rxrpc_peer *peer) hash_key = rxrpc_peer_hash_key(local, &peer->srx); rxrpc_init_peer(local, peer, hash_key); - spin_lock_bh(&rxnet->peer_hash_lock); + spin_lock(&rxnet->peer_hash_lock); hash_add_rcu(rxnet->peer_hash, &peer->hash_link, hash_key); list_add_tail(&peer->keepalive_link, &rxnet->peer_keepalive_new); - spin_unlock_bh(&rxnet->peer_hash_lock); + spin_unlock(&rxnet->peer_hash_lock); } /* diff --git a/net/rxrpc/rxperf.c b/net/rxrpc/rxperf.c index 7ef93407be830..e848a4777b8c7 100644 --- a/net/rxrpc/rxperf.c +++ b/net/rxrpc/rxperf.c @@ -478,6 +478,18 @@ static int rxperf_deliver_request(struct rxperf_call *call) call->unmarshal++; fallthrough; case 2: + ret = rxperf_extract_data(call, true); + if (ret < 0) + return ret; + + /* Deal with the terminal magic cookie. */ + call->iov_len = 4; + call->kvec[0].iov_len = call->iov_len; + call->kvec[0].iov_base = call->tmp; + iov_iter_kvec(&call->iter, READ, call->kvec, 1, call->iov_len); + call->unmarshal++; + fallthrough; + case 3: ret = rxperf_extract_data(call, false); if (ret < 0) return ret; diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index ca6984541edbd..3e6cb35baf25a 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -3337,10 +3337,7 @@ int smc_create_clcsk(struct net *net, struct sock *sk, int family) * which need net ref. */ sk = smc->clcsock->sk; - __netns_tracker_free(net, &sk->ns_tracker, false); - sk->sk_net_refcnt = 1; - get_net_track(net, &sk->ns_tracker, GFP_KERNEL); - sock_inuse_add(net, 1); + sk_net_refcnt_upgrade(sk); return 0; } diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index cb3bd12f5818b..72e5a01df3d35 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1541,10 +1541,7 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv, newlen = error; if (protocol == IPPROTO_TCP) { - __netns_tracker_free(net, &sock->sk->ns_tracker, false); - sock->sk->sk_net_refcnt = 1; - get_net_track(net, &sock->sk->ns_tracker, GFP_KERNEL); - sock_inuse_add(net, 1); + sk_net_refcnt_upgrade(sock->sk); if ((error = kernel_listen(sock, 64)) < 0) goto bummer; } diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index c60936d8cef71..940fe65b2a351 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1941,12 +1941,8 @@ static struct socket *xs_create_sock(struct rpc_xprt *xprt, goto out; } - if (protocol == IPPROTO_TCP) { - __netns_tracker_free(xprt->xprt_net, &sock->sk->ns_tracker, false); - sock->sk->sk_net_refcnt = 1; - get_net_track(xprt->xprt_net, &sock->sk->ns_tracker, GFP_KERNEL); - sock_inuse_add(xprt->xprt_net, 1); - } + if (protocol == IPPROTO_TCP) + sk_net_refcnt_upgrade(sock->sk); filp = sock_alloc_file(sock, O_NONBLOCK, NULL); if (IS_ERR(filp)) diff --git a/tools/testing/selftests/drivers/net/hds.py b/tools/testing/selftests/drivers/net/hds.py index 394971b25c0b1..873f5219e41d7 100755 --- a/tools/testing/selftests/drivers/net/hds.py +++ b/tools/testing/selftests/drivers/net/hds.py @@ -2,17 +2,54 @@ # SPDX-License-Identifier: GPL-2.0 import errno +import os from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises, KsftSkipEx -from lib.py import EthtoolFamily, NlError +from lib.py import CmdExitFailure, EthtoolFamily, NlError from lib.py import NetDrvEnv +from lib.py import defer, ethtool, ip -def get_hds(cfg, netnl) -> None: + +def _get_hds_mode(cfg, netnl) -> str: try: rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}}) except NlError as e: raise KsftSkipEx('ring-get not supported by device') if 'tcp-data-split' not in rings: raise KsftSkipEx('tcp-data-split not supported by device') + return rings['tcp-data-split'] + + +def _xdp_onoff(cfg): + test_dir = os.path.dirname(os.path.realpath(__file__)) + prog = test_dir + "/../../net/lib/xdp_dummy.bpf.o" + ip("link set dev %s xdp obj %s sec xdp" % + (cfg.ifname, prog)) + ip("link set dev %s xdp off" % cfg.ifname) + + +def _ioctl_ringparam_modify(cfg, netnl) -> None: + """ + Helper for performing a hopefully unimportant IOCTL SET. + IOCTL does not support HDS, so it should not affect the HDS config. + """ + try: + rings = netnl.rings_get({'header': {'dev-index': cfg.ifindex}}) + except NlError as e: + raise KsftSkipEx('ring-get not supported by device') + + if 'tx' not in rings: + raise KsftSkipEx('setting Tx ring size not supported') + + try: + ethtool(f"--disable-netlink -G {cfg.ifname} tx {rings['tx'] // 2}") + except CmdExitFailure as e: + ethtool(f"--disable-netlink -G {cfg.ifname} tx {rings['tx'] * 2}") + defer(ethtool, f"-G {cfg.ifname} tx {rings['tx']}") + + +def get_hds(cfg, netnl) -> None: + _get_hds_mode(cfg, netnl) + def get_hds_thresh(cfg, netnl) -> None: try: @@ -104,6 +141,103 @@ def set_hds_thresh_gt(cfg, netnl) -> None: netnl.rings_set({'header': {'dev-index': cfg.ifindex}, 'hds-thresh': hds_gt}) ksft_eq(e.exception.nl_msg.error, -errno.EINVAL) + +def set_xdp(cfg, netnl) -> None: + """ + Enable single-buffer XDP on the device. + When HDS is in "auto" / UNKNOWN mode, XDP installation should work. + """ + mode = _get_hds_mode(cfg, netnl) + if mode == 'enabled': + netnl.rings_set({'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'unknown'}) + + _xdp_onoff(cfg) + + +def enabled_set_xdp(cfg, netnl) -> None: + """ + Enable single-buffer XDP on the device. + When HDS is in "enabled" mode, XDP installation should not work. + """ + _get_hds_mode(cfg, netnl) + netnl.rings_set({'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'enabled'}) + + defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'unknown'}) + + with ksft_raises(CmdExitFailure) as e: + _xdp_onoff(cfg) + + +def set_xdp(cfg, netnl) -> None: + """ + Enable single-buffer XDP on the device. + When HDS is in "auto" / UNKNOWN mode, XDP installation should work. + """ + mode = _get_hds_mode(cfg, netnl) + if mode == 'enabled': + netnl.rings_set({'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'unknown'}) + + _xdp_onoff(cfg) + + +def enabled_set_xdp(cfg, netnl) -> None: + """ + Enable single-buffer XDP on the device. + When HDS is in "enabled" mode, XDP installation should not work. + """ + _get_hds_mode(cfg, netnl) # Trigger skip if not supported + + netnl.rings_set({'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'enabled'}) + defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'unknown'}) + + with ksft_raises(CmdExitFailure) as e: + _xdp_onoff(cfg) + + +def ioctl(cfg, netnl) -> None: + mode1 = _get_hds_mode(cfg, netnl) + _ioctl_ringparam_modify(cfg, netnl) + mode2 = _get_hds_mode(cfg, netnl) + + ksft_eq(mode1, mode2) + + +def ioctl_set_xdp(cfg, netnl) -> None: + """ + Like set_xdp(), but we perturb the settings via the legacy ioctl. + """ + mode = _get_hds_mode(cfg, netnl) + if mode == 'enabled': + netnl.rings_set({'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'unknown'}) + + _ioctl_ringparam_modify(cfg, netnl) + + _xdp_onoff(cfg) + + +def ioctl_enabled_set_xdp(cfg, netnl) -> None: + """ + Enable single-buffer XDP on the device. + When HDS is in "enabled" mode, XDP installation should not work. + """ + _get_hds_mode(cfg, netnl) # Trigger skip if not supported + + netnl.rings_set({'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'enabled'}) + defer(netnl.rings_set, {'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'unknown'}) + + with ksft_raises(CmdExitFailure) as e: + _xdp_onoff(cfg) + + def main() -> None: with NetDrvEnv(__file__, queue_count=3) as cfg: ksft_run([get_hds, @@ -112,7 +246,12 @@ def main() -> None: set_hds_enable, set_hds_thresh_zero, set_hds_thresh_max, - set_hds_thresh_gt], + set_hds_thresh_gt, + set_xdp, + enabled_set_xdp, + ioctl, + ioctl_set_xdp, + ioctl_enabled_set_xdp], args=(cfg, EthtoolFamily())) ksft_exit() diff --git a/tools/testing/selftests/net/lib/Makefile b/tools/testing/selftests/net/lib/Makefile index bc6b6762baf3e..c22623b9a2a5f 100644 --- a/tools/testing/selftests/net/lib/Makefile +++ b/tools/testing/selftests/net/lib/Makefile @@ -9,7 +9,10 @@ TEST_FILES := ../../../../../Documentation/netlink/specs TEST_FILES += ../../../../net/ynl TEST_GEN_FILES += csum +TEST_GEN_FILES += $(patsubst %.c,%.o,$(wildcard *.bpf.c)) TEST_INCLUDES := $(wildcard py/*.py sh/*.sh) include ../../lib.mk + +include ../bpf.mk diff --git a/tools/testing/selftests/net/lib/xdp_dummy.bpf.c b/tools/testing/selftests/net/lib/xdp_dummy.bpf.c new file mode 100644 index 0000000000000..d988b2e0cee84 --- /dev/null +++ b/tools/testing/selftests/net/lib/xdp_dummy.bpf.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define KBUILD_MODNAME "xdp_dummy" +#include +#include + +SEC("xdp") +int xdp_dummy_prog(struct xdp_md *ctx) +{ + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL";