diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index a5856bcc8..154430db3 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -6,6 +6,7 @@ alloc alltgpt alua aptpl +ASAN ascq ata autoconfig @@ -13,6 +14,7 @@ autodetected autoresize barbie BINDIR +bitfield blkid bmarzins Bsymbolic @@ -58,14 +60,15 @@ EAGAIN ECKD emc Engenio -Equal EVPD +Exos failback failover fds fexceptions FFFFFFFF fge +fno followover forcequeueing fpin @@ -77,6 +80,7 @@ getrlimit getuid github gitlab +google GPT hbtl hds @@ -122,6 +126,7 @@ libudevdir liburcu linux LIO +lld lpthread Lun lvm @@ -141,13 +146,13 @@ multipathing multipaths multiqueue mwilck +NFINIDAT NOLOG nompath NOSCAN Nosync -NVMe nvme -NFINIDAT +Nytro OBJDEPS oneshot ontap @@ -157,8 +162,10 @@ OPTFLAGS paramp partx pathgroup +pathlist petabytes pgpolicy +pgvec plugindir PNR ppc @@ -193,6 +200,7 @@ rpmbuild rport rtpi rtprio +sanitizers sas sbp scsi @@ -219,11 +227,11 @@ suse svg switchgroup sys +SYSDIR sysfs sysinit tcp terabytes -SYSDIR TESTDEPS testname tgill @@ -240,7 +248,6 @@ udevd uevent uid unitdir -unregistering unsetmarginal unsetprkey unsetprstatus @@ -263,6 +270,7 @@ weightedpath wholedisk Wilck wildcards +Wno workflows wrt wwid diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt index f2ef14240..55d4742eb 100644 --- a/.github/actions/spelling/patterns.txt +++ b/.github/actions/spelling/patterns.txt @@ -10,6 +10,11 @@ #commit \b[0-9a-f]{7}\b +# Generic Hex numbers +\b0x[0-9a-f]{4}\b +\b0x[0-9a-f]{8}\b +\b0x[0-9a-f]{16}\b + # WWNN/WWPN (NAA identifiers) \b(?:0x)?10[0-9a-f]{14}\b \b(?:0x|3)?[25][0-9a-f]{15}\b diff --git a/.github/workflows/abi-stable.yaml b/.github/workflows/abi-stable.yaml index 8a045b05c..8f9a9dbf3 100644 --- a/.github/workflows/abi-stable.yaml +++ b/.github/workflows/abi-stable.yaml @@ -15,12 +15,18 @@ on: jobs: reference-abi: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - name: get parent tag + - name: get parent tag (push) run: > echo ${{ github.ref }} | sed -E 's,refs/heads/stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: ${{ github.event_name == 'push' }} + - name: get parent tag (PR) + run: > + echo ${{ github.base_ref }} | + sed -E 's,stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: ${{ github.event_name == 'pull_request' }} - name: assert parent tag run: /bin/false if: ${{ env.PARENT_TAG == '' }} @@ -32,12 +38,13 @@ jobs: gcc make pkg-config abigail-tools libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev - name: checkout ${{ env.PARENT_TAG }} uses: actions/checkout@v4 with: ref: ${{ env.PARENT_TAG }} - name: build ABI for ${{ env.PARENT_TAG }} - run: make -j$(grep -c ^processor /proc/cpuinfo) -Orecurse abi + run: make -j$(nproc) -Orecurse abi - name: save ABI uses: actions/upload-artifact@v4 with: @@ -45,20 +52,26 @@ jobs: path: abi check-abi: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: reference-abi steps: - - name: get parent tag + - name: get parent tag (push) run: > echo ${{ github.ref }} | sed -E 's,refs/heads/stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: ${{ github.event_name == 'push' }} + - name: get parent tag (PR) + run: > + echo ${{ github.base_ref }} | + sed -E 's,stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: ${{ github.event_name == 'pull_request' }} - name: assert parent tag run: /bin/false if: ${{ env.PARENT_TAG == '' }} - - name: checkout ${{ env.PARENT_TAG }} + - name: checkout ${{ github.ref }} uses: actions/checkout@v4 with: - ref: ${{ env.PARENT_TAG }} + ref: ${{ github.ref }} - name: download ABI for ${{ env.PARENT_TAG }} id: download_abi uses: actions/download-artifact@v4 @@ -74,9 +87,10 @@ jobs: gcc make pkg-config abigail-tools libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev - name: check ABI of ${{ github.ref }} against ${{ env.PARENT_TAG }} id: check_abi - run: make -j$(grep -c ^processor /proc/cpuinfo) -Orecurse abi-test + run: make -j$(nproc) -Orecurse abi-test continue-on-error: true - name: save differences if: ${{ steps.check_abi.outcome != 'success' }} diff --git a/.github/workflows/abi.yaml b/.github/workflows/abi.yaml index b1282493f..adc2081f1 100644 --- a/.github/workflows/abi.yaml +++ b/.github/workflows/abi.yaml @@ -18,7 +18,7 @@ env: jobs: save-and-test-ABI: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: set ABI branch if: ${{ env.ABI_BRANCH == '' }} @@ -42,8 +42,9 @@ jobs: gcc make pkg-config abigail-tools libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev - name: create ABI - run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) abi.tar.gz + run: make -Orecurse -j$(nproc) abi.tar.gz - name: save ABI uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/build-and-unittest.yaml b/.github/workflows/build-and-unittest.yaml index 4f568131c..2fb5327d4 100644 --- a/.github/workflows/build-and-unittest.yaml +++ b/.github/workflows/build-and-unittest.yaml @@ -13,7 +13,7 @@ on: - 'stable-*' jobs: jammy: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -46,16 +46,16 @@ jobs: if: ${{ matrix.cc == 'clang' }} - name: build run: > - make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) + make -Orecurse -j$(nproc) READLINE=${{ matrix.rl }} OPTFLAGS="$OPT" - name: test run: > - make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) + make -Orecurse -j$(nproc) OPTFLAGS="$OPT" test - name: valgrind-test id: valgrind run: > - make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) + make -Orecurse -j$(nproc) OPTFLAGS="$OPT" valgrind-test continue-on-error: true - name: valgrind-results @@ -92,12 +92,12 @@ jobs: - name: set CC run: echo CC=${{ matrix.cc }} >> $GITHUB_ENV - name: build - run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) READLINE=${{ matrix.rl }} + run: make -Orecurse -j$(nproc) READLINE=${{ matrix.rl }} - name: test - run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) test + run: make -Orecurse -j$(nproc) test - name: valgrind-test id: valgrind - run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) valgrind-test + run: make -Orecurse -j$(nproc) valgrind-test continue-on-error: true - name: valgrind-results run: cat tests/*.vgr diff --git a/.github/workflows/coverity.yaml b/.github/workflows/coverity.yaml index 678d0d2a3..30e5471d3 100644 --- a/.github/workflows/coverity.yaml +++ b/.github/workflows/coverity.yaml @@ -6,7 +6,7 @@ on: jobs: upload-coverity-scan: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v4 @@ -16,6 +16,7 @@ jobs: gcc make pkg-config libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev - name: download coverity run: > curl -o cov-analysis-linux64.tar.gz @@ -32,7 +33,7 @@ jobs: - name: build with cov-build run: > PATH="$PWD/coverity/bin:$PATH" - cov-build --dir cov-int make -Orecurse -j"$(grep -c ^processor /proc/cpuinfo)" + cov-build --dir cov-int make -Orecurse -j"$(nproc)" - name: pack results run: tar cfz multipath-tools.tgz cov-int - name: submit results diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml index a25179710..b232b60c1 100644 --- a/.github/workflows/foreign.yaml +++ b/.github/workflows/foreign.yaml @@ -27,7 +27,7 @@ on: jobs: cross-build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -43,7 +43,7 @@ jobs: - name: checkout uses: actions/checkout@v4 - name: build - run: make -j -Orecurse test-progs.tar + run: make -j$(nproc) -Orecurse test-progs.tar - name: upload binary archive uses: actions/upload-artifact@v4 with: @@ -52,7 +52,7 @@ jobs: overwrite: true test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: cross-build strategy: fail-fast: false @@ -94,7 +94,7 @@ jobs: pull-params: "--platform linux/${{ env.CONTAINER_ARCH }}" root-test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: cross-build strategy: fail-fast: false diff --git a/.github/workflows/multiarch-stable.yaml b/.github/workflows/multiarch-stable.yaml index 2a30a8605..b8c770e40 100644 --- a/.github/workflows/multiarch-stable.yaml +++ b/.github/workflows/multiarch-stable.yaml @@ -26,7 +26,7 @@ on: jobs: build-old: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -37,14 +37,10 @@ jobs: - ubuntu-trusty arch: [386, arm/v7] include: - - os: debian-trixie - arch: aarch64 - os: debian-trixie arch: s390x - os: debian-trixie arch: ppc64le - - os: debian-bookworm - arch: aarch64 - os: debian-bookworm arch: s390x - os: debian-bookworm @@ -57,6 +53,7 @@ jobs: with: image: tonistiigi/binfmt:latest - name: compile and run unit tests + id: test uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} @@ -65,3 +62,23 @@ jobs: command: test params: "--platform linux/${{ matrix.arch }}" pull-params: "--platform linux/${{ matrix.arch }}" + continue-on-error: true + - name: create binary archive + uses: mosteo-actions/docker-run@v1 + with: + image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} + guest-dir: /build + host-dir: ${{ github.workspace }} + command: test-progs.tar + params: "--platform linux/${{ matrix.arch }}" + pull-params: "--platform linux/${{ matrix.arch }}" + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ matrix.arch }} + path: test-progs.tar + if: steps.test.outcome != 'success' + - name: fail + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/.github/workflows/multiarch.yaml b/.github/workflows/multiarch.yaml index c453446a6..806275a13 100644 --- a/.github/workflows/multiarch.yaml +++ b/.github/workflows/multiarch.yaml @@ -30,7 +30,7 @@ on: jobs: build-current: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -39,12 +39,15 @@ jobs: - debian-sid - fedora-rawhide - opensuse-tumbleweed - arch: [amd64, ppc64le, aarch64, s390x, 386, arm/v7] + arch: [ppc64le, s390x, 386, arm/v7] exclude: - os: fedora-rawhide arch: 386 - os: fedora-rawhide arch: arm/v7 + include: + - os: alpine + arch: aarch64 steps: - name: checkout uses: actions/checkout@v4 @@ -53,6 +56,7 @@ jobs: with: image: tonistiigi/binfmt:latest - name: compile and run unit tests + id: test uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} @@ -61,4 +65,23 @@ jobs: command: test params: "--platform linux/${{ matrix.arch }}" pull-params: "--platform linux/${{ matrix.arch }}" - + continue-on-error: true + - name: create binary archive + uses: mosteo-actions/docker-run@v1 + with: + image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} + guest-dir: /build + host-dir: ${{ github.workspace }} + command: test-progs.tar + params: "--platform linux/${{ matrix.arch }}" + pull-params: "--platform linux/${{ matrix.arch }}" + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ matrix.arch }} + path: test-progs.tar + if: steps.test.outcome != 'success' + - name: fail + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 6b38fa712..9e89780b1 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -26,7 +26,6 @@ on: jobs: stable: - runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: @@ -36,8 +35,15 @@ jobs: - debian-bullseye - debian-bookworm - debian-trixie - - fedora-42 - - opensuse-leap + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.variant.runner }} steps: - name: checkout uses: actions/checkout@v4 @@ -45,24 +51,29 @@ jobs: - name: set archive name # Leap containers have cpio but not tar run: echo ARCHIVE_TGT=test-progs.cpio >> $GITHUB_ENV - if: ${{ matrix.os == 'opensuse-leap' }} + if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} - name: set archive name run: echo ARCHIVE_TGT=test-progs.tar >> $GITHUB_ENV - if: ${{ matrix.os != 'opensuse-leap' }} + if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} - name: build and test + id: test if: ${{ matrix.os != 'debian-jessie' }} uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: -j -Orecurse test + command: -j$(nproc) -Orecurse test + continue-on-error: true + - name: build and test (jessie) + id: test_jessie # On jessie, we use libreadline 5 (no licensing issue) if: ${{ matrix.os == 'debian-jessie' }} uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: -j -Orecurse READLINE=libreadline test + command: -j$(nproc) -Orecurse READLINE=libreadline test + continue-on-error: true - name: create ${{ env.ARCHIVE_TGT }} if: ${{ matrix.os != 'debian-jessie' }} @@ -80,44 +91,113 @@ jobs: - name: upload binary archive uses: actions/upload-artifact@v4 with: - name: native-${{ matrix.os }} + name: native-${{ matrix.os }}-${{ matrix.variant.arch }} path: ${{ env.ARCHIVE_TGT }} overwrite: true + - name: fail + run: /bin/false + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} clang: - runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: os: - - debian-jessie - debian-buster - debian-bullseye - debian-bookworm - debian-trixie - - fedora-42 - - opensuse-leap + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + include: + - os: debian-jessie + variant: + arch: x86_64 + runner: ubuntu-24.04 + runs-on: ${{ matrix.variant.runner }} steps: - name: checkout uses: actions/checkout@v4 + - name: set archive name + # Leap containers have cpio but not tar + run: echo ARCHIVE_TGT=test-progs.cpio >> $GITHUB_ENV + if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} + - name: set archive name + run: echo ARCHIVE_TGT=test-progs.tar >> $GITHUB_ENV + if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} + - name: clang + id: test if: ${{ matrix.os != 'debian-jessie' }} uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} params: -e CC=clang - command: -j -Orecurse test + command: -j$(nproc) -Orecurse test + continue-on-error: true - name: clang (jessie) + id: test_jessie if: ${{ matrix.os == 'debian-jessie' }} uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} params: -e CC=clang - command: -j -Orecurse READLINE=libreadline test + command: -j$(nproc) -Orecurse READLINE=libreadline test + continue-on-error: true + + - name: create ${{ env.ARCHIVE_TGT }} + uses: mosteo-actions/docker-run@v1 + with: + image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} + command: ${{ env.ARCHIVE_TGT }} + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} + - name: create ${{ env.ARCHIVE_TGT }} (jessie) + uses: mosteo-actions/docker-run@v1 + with: + image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} + command: READLINE=libreadline ${{ env.ARCHIVE_TGT }} + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} + + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: clang-${{ matrix.os }}-${{ matrix.variant.arch }} + path: ${{ env.ARCHIVE_TGT }} + overwrite: true + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} + + - name: fail + run: /bin/false + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} root-test: - runs-on: ubuntu-22.04 needs: stable strategy: fail-fast: false @@ -128,8 +208,15 @@ jobs: - debian-bullseye - debian-bookworm - debian-trixie - - fedora-42 - - opensuse-leap + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.variant.runner }} steps: - name: mpath run: sudo modprobe dm_multipath @@ -142,13 +229,13 @@ jobs: - name: download binary archive uses: actions/download-artifact@v4 with: - name: native-${{ matrix.os }} + name: native-${{ matrix.os }}-${{ matrix.variant.arch }} - name: unpack binary archive run: cpio -idv < test-progs.cpio - if: ${{ matrix.os == 'opensuse-leap' }} + if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} - name: unpack binary archive run: tar xfmv test-progs.tar - if: ${{ matrix.os != 'opensuse-leap' }} + if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} - name: run root tests uses: mosteo-actions/docker-run@v1 diff --git a/.github/workflows/rolling.yaml b/.github/workflows/rolling.yaml index 503625e04..a4706e52e 100644 --- a/.github/workflows/rolling.yaml +++ b/.github/workflows/rolling.yaml @@ -29,24 +29,50 @@ on: jobs: rolling: - runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: os: - debian-sid - - alpine - opensuse-tumbleweed - fedora-rawhide + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + compiler: + - gcc + - clang + include: + - os: alpine + variant: + arch: x86_64 + runner: ubuntu-24.04 + compiler: gcc + - os: alpine + variant: + arch: x86_64 + runner: ubuntu-24.04 + compiler: clang + runs-on: ${{ matrix.variant.runner }} container: ghcr.io/mwilck/multipath-build-${{ matrix.os }} steps: - name: checkout uses: actions/checkout@v4 - name: build and test - run: make READLINE=libreadline -j -Orecurse test - - name: clean - run: make -j -Orecurse clean - - name: clang - env: - CC: clang - run: make READLINE=libedit -j -Orecurse test + id: test + run: make CC=${{ matrix.compiler }} READLINE=libreadline -j$(nproc) -Orecurse test + continue-on-error: true + - name: create binary archive + run: make test-progs.tar + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ matrix.variant.arch }}-${{ matrix.compiler }} + path: test-progs.tar + if: steps.test.outcome != 'success' + - name: fail + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/Makefile.inc b/Makefile.inc index 9e3dc4661..c6aa6eda2 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -9,6 +9,12 @@ # Uncomment to disable dmevents polling support # ENABLE_DMEVENTS_POLL = 0 # +# Use ASAN=1 on make command line to enable address sanitizer +ASAN := +# +# Override on make command line if desired +OPT := -O2 + # Readline library to use, libedit, libreadline, or empty # Caution: Using libreadline may make the multipathd binary undistributable, # see https://github.com/opensvc/multipath-tools/issues/36 @@ -102,7 +108,7 @@ SYSTEMD_LIBDEPS := $(if $(SYSTEMD),$(if $(shell test $(SYSTEMD) -gt 209 && echo MODPROBE_UNIT := $(shell test "0$(SYSTEMD)" -lt 245 2>/dev/null || \ echo "modprobe@dm_multipath.service") -OPTFLAGS := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4 +OPTFLAGS := $(OPT) -g $(STACKPROT) --param=ssp-buffer-size=4 # Set WARN_ONLY=1 to avoid compilation erroring out due to warnings. Useful during development. WARN_ONLY := @@ -112,7 +118,7 @@ WARNFLAGS := $(WERROR) -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -W$(ERROR)imp -W$(ERROR)implicit-function-declaration -W$(ERROR)format-security \ $(WNOCLOBBERED) -W$(ERROR)cast-qual $(ERROR_DISCARDED_QUALIFIERS) $(W_URCU_TYPE_LIMITS) -CPPFLAGS := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) \ +CPPFLAGS := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) $(D_CMOCKA_VERSION) \ -D_FILE_OFFSET_BITS=64 \ -DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(TGTDIR)$(plugindir)\" \ -DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(TGTDIR)$(configdir)\" \ @@ -122,11 +128,11 @@ CPPFLAGS := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) \ -DWSTRINGOP_TRUNCATION=$(if $(WSTRINGOP_TRUNCATION),1,0) \ -MMD -MP CFLAGS := -std=$(C_STD) $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \ - -fexceptions + -fexceptions -fno-strict-aliasing $(if $(ASAN),-fsanitize=address) BIN_CFLAGS := -fPIE -DPIE LIB_CFLAGS := -fPIC SHARED_FLAGS := -shared -LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now -Wl,-z,defs +LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now -Wl,-z,defs $(if $(ASAN),-lasan) BIN_LDFLAGS := -pie # Source code directories. Don't modify. diff --git a/NEWS.md b/NEWS.md index 6f0018e67..b8a34b679 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,47 @@ release. These bug fixes will be tracked in stable branches. See [README.md](README.md) for additional information. +## multipath-tools 0.13.1, 2026/01 + +### Bug fixes + +* Make sure multipathd registers keys all paths of a multipath map after + mpathpersist registered a key for a map. multipathd could fail to do so in + some cases, e.g. if paths become unavailable or available while the + registration was taking place. Fixes 0.13.0. +* Fix `mpathpersist --report-capabilities` output. Fixes 0.5.0. +* Improve error handling when retrying REGISTER AND IGNORE persistent + reservations commands. Fixes 0.13.0. +* Fix command descriptions in the multipathd man page. Fixes 0.9.2. +* Fix an undefined symbol error with the LLVM lld linker. + Fixes [#132](https://github.com/opensvc/multipath-tools/issues/132), 0.10.0. +* Fix ISO C23 compatibility issue causing errors with new compilers. +* Fix use-after-free error in free_pgvec(). + Fixes [#128](https://github.com/opensvc/multipath-tools/issues/128), 0.12.0. +* Fix memory leak caused by not joining the "init unwinder" thread. + Fixes 0.8.6. +* Fix memory leaks in kpartx. Fixes any version. +* Print the warning "setting scsi timeouts is unsupported for protocol" only + once per protocol. Fixes 0.9.0. +* Make sure multipath-tools is compiled with the compiler flag + `-fno-strict-aliasing`. This turns out to be necessary because our code + uses techniques like `container_of()` which don't work well with + strict aliasing rules. + Fixes [#130](https://github.com/opensvc/multipath-tools/issues/130). +* Fix initialization of paths that were offline during path detection. +* Fix printing the "path offline" log message for offline paths that don't + have a path checker configured. + +### Other changes + +* Hardware table: add Seagate Exos and Nytro series. +* Avoid joining threads twice with liburcu 0.14.0 and newer. +* CI updates (GitHub workflows). +* Fix CI for cmocka 2.0 by adding the `-Wno-error=deprecated-declarations` + compiler flag. + Fixes [#129](https://github.com/opensvc/multipath-tools/issues/129) +* Add the ASAN=1 and OPT= make variables (see README.md). + ## multipath-tools 0.13.0, 2025/10 ### Major rework of the SCSI Persistent Reservation code diff --git a/README.md b/README.md index 530caed7d..888998b7e 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,15 @@ See "Passing standard compiler flags" below for an exception. The following variables can be passed to the `make` command line: * `V=1`: enable verbose build. + * `OPT=`: set optimization flags. You may want to set `OPT="-O0"` for + debugging, for example. The default is `-O2`. Note that it is also + possible to set `OPTFLAGS`, which takes precedence over `OPT`. `OPTFLAGS` + sets additional options by default, which are intended for distribution + build environments to override. For quick customization of the optimization + level, use `OPT`. + * `ASAN=1`: Enable + [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer) + during build for debugging memory allocation. This is off by default. * `plugindir="/some/path"`: directory where libmultipath plugins (path checkers, prioritizers, and foreign multipath support) will be looked up. This used to be the run-time option `multipath_dir` in earlier versions. diff --git a/create-config.mk b/create-config.mk index 3c3ba069e..2d6ade856 100644 --- a/create-config.mk +++ b/create-config.mk @@ -77,6 +77,10 @@ URCU_VERSION = $(shell \ $(PKG_CONFIG) --modversion liburcu 2>/dev/null | \ awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }') +CMOCKA_VERSION = $(shell \ + ($(PKG_CONFIG) --modversion cmocka 2>/dev/null || echo "1.1.0" ) | \ + awk -F. '{ printf("%d", 256 * ( 256 * $$1 + $$2) + $$3); }') + DEFINES := ifneq ($(call check_func,dm_task_no_flush,$(devmapper_incdir)/libdevmapper.h),0) @@ -185,6 +189,7 @@ $(TOPDIR)/config.mk: $(multipathdir)/autoconfig.h @echo "FPIN_SUPPORT := $(FPIN_SUPPORT)" >$@ @echo "FORTIFY_OPT := $(FORTIFY_OPT)" >>$@ @echo "D_URCU_VERSION := $(call URCU_VERSION)" >>$@ + @echo "CMOCKA_VERSION := $(call CMOCKA_VERSION)" >>$@ @echo "SYSTEMD := $(SYSTEMD)" >>$@ @echo "ANA_SUPPORT := $(ANA_SUPPORT)" >>$@ @echo "STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)" >>$@ diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index a1495e55e..6f2ec4ea0 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -227,6 +227,11 @@ xmalloc (size_t size) { return t; } +static void cleanup_charp(char **p) +{ + free(*p); +} + int main(int argc, char **argv){ int i, j, m, n, op, off, arg, c, d, ro=0; @@ -237,10 +242,11 @@ main(int argc, char **argv){ char *type, *diskdevice, *device, *progname; int verbose = 0; char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16]; - char * loopdev = NULL; - char * delim = NULL; - char *uuid = NULL; - char *mapname = NULL; + char *loopdev __attribute__((cleanup(cleanup_charp))) = NULL; + char *delim __attribute__((cleanup(cleanup_charp))) = NULL; + char *uuid __attribute__((cleanup(cleanup_charp))) = NULL; + char *_mapname __attribute__((cleanup(cleanup_charp))) = NULL; + char *mapname; int hotplug = 0; int loopcreated = 0; struct stat buf; @@ -292,7 +298,9 @@ main(int argc, char **argv){ verbose = 1; break; case 'p': - delim = optarg; + delim = strdup(optarg); + if (!delim) + exit(1); break; case 'l': what = LIST; @@ -381,11 +389,15 @@ main(int argc, char **argv){ off = find_devname_offset(device); if (!loopdev) { - mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev)); - if (mapname) - uuid = dm_mapuuid(mapname); + _mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev)); + if (_mapname) + uuid = dm_mapuuid(_mapname); } + mapname = _mapname; + if (!mapname) + mapname = device + off; + /* * We are called for a non-DM device. * Make up a fake UUID for the device, unless "-d -f" is given. @@ -395,9 +407,6 @@ main(int argc, char **argv){ if (!uuid && !(what == DELETE && force_devmap)) uuid = nondm_create_uuid(buf.st_rdev); - if (!mapname) - mapname = device + off; - if (delim == NULL) { delim = xmalloc(DELIM_SIZE); memset(delim, 0, DELIM_SIZE); @@ -674,7 +683,6 @@ main(int argc, char **argv){ if (verbose) fprintf(stderr, "loop deleted : %s\n", device); } - end: dm_lib_exit(); diff --git a/libdmmp/docs/man/dmmp_context_free.3 b/libdmmp/docs/man/dmmp_context_free.3 index 2a9bbb10f..ce74cfb00 100644 --- a/libdmmp/docs/man/dmmp_context_free.3 +++ b/libdmmp/docs/man/dmmp_context_free.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_free" 3 "dmmp_context_free" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_free" 3 "dmmp_context_free" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_free \- Release the memory of struct dmmp_context. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_log_func_set.3 b/libdmmp/docs/man/dmmp_context_log_func_set.3 index f593eb57b..05b1cd0f1 100644 --- a/libdmmp/docs/man/dmmp_context_log_func_set.3 +++ b/libdmmp/docs/man/dmmp_context_log_func_set.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_log_func_set" 3 "dmmp_context_log_func_set" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_log_func_set" 3 "dmmp_context_log_func_set" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_log_func_set \- Set log handler function. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_log_priority_get.3 b/libdmmp/docs/man/dmmp_context_log_priority_get.3 index 6f7c3204f..62f671ff2 100644 --- a/libdmmp/docs/man/dmmp_context_log_priority_get.3 +++ b/libdmmp/docs/man/dmmp_context_log_priority_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_log_priority_get" 3 "dmmp_context_log_priority_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_log_priority_get" 3 "dmmp_context_log_priority_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_log_priority_get \- Get log priority. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_log_priority_set.3 b/libdmmp/docs/man/dmmp_context_log_priority_set.3 index c1ff3715e..991d5bf92 100644 --- a/libdmmp/docs/man/dmmp_context_log_priority_set.3 +++ b/libdmmp/docs/man/dmmp_context_log_priority_set.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_log_priority_set" 3 "dmmp_context_log_priority_set" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_log_priority_set" 3 "dmmp_context_log_priority_set" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_log_priority_set \- Set log priority. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_new.3 b/libdmmp/docs/man/dmmp_context_new.3 index 487ccde8c..25ce6ad9c 100644 --- a/libdmmp/docs/man/dmmp_context_new.3 +++ b/libdmmp/docs/man/dmmp_context_new.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_new" 3 "dmmp_context_new" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_new" 3 "dmmp_context_new" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_new \- Create struct dmmp_context. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_timeout_get.3 b/libdmmp/docs/man/dmmp_context_timeout_get.3 index b738fb97b..317b3f4c8 100644 --- a/libdmmp/docs/man/dmmp_context_timeout_get.3 +++ b/libdmmp/docs/man/dmmp_context_timeout_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_timeout_get" 3 "dmmp_context_timeout_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_timeout_get" 3 "dmmp_context_timeout_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_timeout_get \- Get IPC timeout. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_timeout_set.3 b/libdmmp/docs/man/dmmp_context_timeout_set.3 index af84be94d..9db1d2591 100644 --- a/libdmmp/docs/man/dmmp_context_timeout_set.3 +++ b/libdmmp/docs/man/dmmp_context_timeout_set.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_timeout_set" 3 "dmmp_context_timeout_set" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_timeout_set" 3 "dmmp_context_timeout_set" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_timeout_set \- Set IPC timeout. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_userdata_get.3 b/libdmmp/docs/man/dmmp_context_userdata_get.3 index 23e1da41d..ef5755e8e 100644 --- a/libdmmp/docs/man/dmmp_context_userdata_get.3 +++ b/libdmmp/docs/man/dmmp_context_userdata_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_userdata_get" 3 "dmmp_context_userdata_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_userdata_get" 3 "dmmp_context_userdata_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_userdata_get \- Get user data pointer. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_userdata_set.3 b/libdmmp/docs/man/dmmp_context_userdata_set.3 index 8a4a76561..cd21644a3 100644 --- a/libdmmp/docs/man/dmmp_context_userdata_set.3 +++ b/libdmmp/docs/man/dmmp_context_userdata_set.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_userdata_set" 3 "dmmp_context_userdata_set" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_userdata_set" 3 "dmmp_context_userdata_set" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_userdata_set \- Set user data pointer. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_flush_mpath.3 b/libdmmp/docs/man/dmmp_flush_mpath.3 index e5690edb2..f8cb83e8d 100644 --- a/libdmmp/docs/man/dmmp_flush_mpath.3 +++ b/libdmmp/docs/man/dmmp_flush_mpath.3 @@ -1,4 +1,4 @@ -.TH "dmmp_flush_mpath" 3 "dmmp_flush_mpath" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_flush_mpath" 3 "dmmp_flush_mpath" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_flush_mpath \- Flush specified multipath device map if unused. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_last_error_msg.3 b/libdmmp/docs/man/dmmp_last_error_msg.3 index 548e1a75a..7aa980bdf 100644 --- a/libdmmp/docs/man/dmmp_last_error_msg.3 +++ b/libdmmp/docs/man/dmmp_last_error_msg.3 @@ -1,4 +1,4 @@ -.TH "dmmp_last_error_msg" 3 "dmmp_last_error_msg" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_last_error_msg" 3 "dmmp_last_error_msg" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_last_error_msg \- Retrieves the last error message. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_log_priority_str.3 b/libdmmp/docs/man/dmmp_log_priority_str.3 index cf3155642..1252e17c8 100644 --- a/libdmmp/docs/man/dmmp_log_priority_str.3 +++ b/libdmmp/docs/man/dmmp_log_priority_str.3 @@ -1,4 +1,4 @@ -.TH "dmmp_log_priority_str" 3 "dmmp_log_priority_str" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_log_priority_str" 3 "dmmp_log_priority_str" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_log_priority_str \- Convert log priority to string. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_array_free.3 b/libdmmp/docs/man/dmmp_mpath_array_free.3 index 8d2090e64..2a1f1aee8 100644 --- a/libdmmp/docs/man/dmmp_mpath_array_free.3 +++ b/libdmmp/docs/man/dmmp_mpath_array_free.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_array_free" 3 "dmmp_mpath_array_free" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_array_free" 3 "dmmp_mpath_array_free" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_array_free \- Free 'struct dmmp_mpath' pointer array. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_array_get.3 b/libdmmp/docs/man/dmmp_mpath_array_get.3 index 4191571ee..2bb7b186e 100644 --- a/libdmmp/docs/man/dmmp_mpath_array_get.3 +++ b/libdmmp/docs/man/dmmp_mpath_array_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_array_get" 3 "dmmp_mpath_array_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_array_get" 3 "dmmp_mpath_array_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_array_get \- Query all existing multipath devices. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 b/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 index 261bea6a9..f368cc294 100644 --- a/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 +++ b/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_kdev_name_get" 3 "dmmp_mpath_kdev_name_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_kdev_name_get" 3 "dmmp_mpath_kdev_name_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_kdev_name_get \- Retrieve kernel DEVNAME of certain mpath. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_name_get.3 b/libdmmp/docs/man/dmmp_mpath_name_get.3 index 894d318f8..20cc09b6d 100644 --- a/libdmmp/docs/man/dmmp_mpath_name_get.3 +++ b/libdmmp/docs/man/dmmp_mpath_name_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_name_get" 3 "dmmp_mpath_name_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_name_get" 3 "dmmp_mpath_name_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_name_get \- Retrieve name(alias) of certain mpath. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_wwid_get.3 b/libdmmp/docs/man/dmmp_mpath_wwid_get.3 index e6afcb1a8..09c79aa6a 100644 --- a/libdmmp/docs/man/dmmp_mpath_wwid_get.3 +++ b/libdmmp/docs/man/dmmp_mpath_wwid_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_wwid_get" 3 "dmmp_mpath_wwid_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_wwid_get" 3 "dmmp_mpath_wwid_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_wwid_get \- Retrieve WWID of certain mpath. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_array_get.3 b/libdmmp/docs/man/dmmp_path_array_get.3 index de29c9d1e..7bd19e709 100644 --- a/libdmmp/docs/man/dmmp_path_array_get.3 +++ b/libdmmp/docs/man/dmmp_path_array_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_array_get" 3 "dmmp_path_array_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_array_get" 3 "dmmp_path_array_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_array_get \- Retrieve path pointer array. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_blk_name_get.3 b/libdmmp/docs/man/dmmp_path_blk_name_get.3 index 167e81c31..984ae9d4c 100644 --- a/libdmmp/docs/man/dmmp_path_blk_name_get.3 +++ b/libdmmp/docs/man/dmmp_path_blk_name_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_blk_name_get" 3 "dmmp_path_blk_name_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_blk_name_get" 3 "dmmp_path_blk_name_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_blk_name_get \- Retrieve block name. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_array_get.3 b/libdmmp/docs/man/dmmp_path_group_array_get.3 index b9b663f67..7faa89c90 100644 --- a/libdmmp/docs/man/dmmp_path_group_array_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_array_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_array_get" 3 "dmmp_path_group_array_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_array_get" 3 "dmmp_path_group_array_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_array_get \- Retrieve path groups pointer array. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_id_get.3 b/libdmmp/docs/man/dmmp_path_group_id_get.3 index 67ba5c00e..fb3cea644 100644 --- a/libdmmp/docs/man/dmmp_path_group_id_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_id_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_id_get" 3 "dmmp_path_group_id_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_id_get" 3 "dmmp_path_group_id_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_id_get \- Retrieve path group ID. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_priority_get.3 b/libdmmp/docs/man/dmmp_path_group_priority_get.3 index 09cbea9c5..ae8a08aa5 100644 --- a/libdmmp/docs/man/dmmp_path_group_priority_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_priority_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_priority_get" 3 "dmmp_path_group_priority_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_priority_get" 3 "dmmp_path_group_priority_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_priority_get \- Retrieve path group priority. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_selector_get.3 b/libdmmp/docs/man/dmmp_path_group_selector_get.3 index dc73fa82e..8fae07360 100644 --- a/libdmmp/docs/man/dmmp_path_group_selector_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_selector_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_selector_get" 3 "dmmp_path_group_selector_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_selector_get" 3 "dmmp_path_group_selector_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_selector_get \- Retrieve path group selector. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_status_get.3 b/libdmmp/docs/man/dmmp_path_group_status_get.3 index a7662b634..6adfeba65 100644 --- a/libdmmp/docs/man/dmmp_path_group_status_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_status_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_status_get" 3 "dmmp_path_group_status_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_status_get" 3 "dmmp_path_group_status_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_status_get \- Retrieve path group status. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_status_str.3 b/libdmmp/docs/man/dmmp_path_group_status_str.3 index 160c8a7ed..a2d654348 100644 --- a/libdmmp/docs/man/dmmp_path_group_status_str.3 +++ b/libdmmp/docs/man/dmmp_path_group_status_str.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_status_str" 3 "dmmp_path_group_status_str" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_status_str" 3 "dmmp_path_group_status_str" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_status_str \- Convert path group status to string. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_status_get.3 b/libdmmp/docs/man/dmmp_path_status_get.3 index 92e64331b..33d103f70 100644 --- a/libdmmp/docs/man/dmmp_path_status_get.3 +++ b/libdmmp/docs/man/dmmp_path_status_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_status_get" 3 "dmmp_path_status_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_status_get" 3 "dmmp_path_status_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_status_get \- Retrieve the path status. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_status_str.3 b/libdmmp/docs/man/dmmp_path_status_str.3 index 456cf24e2..e69c8be69 100644 --- a/libdmmp/docs/man/dmmp_path_status_str.3 +++ b/libdmmp/docs/man/dmmp_path_status_str.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_status_str" 3 "dmmp_path_status_str" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_status_str" 3 "dmmp_path_status_str" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_status_str \- Convert path status to string. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_reconfig.3 b/libdmmp/docs/man/dmmp_reconfig.3 index 413a3e6fc..b17c2d689 100644 --- a/libdmmp/docs/man/dmmp_reconfig.3 +++ b/libdmmp/docs/man/dmmp_reconfig.3 @@ -1,4 +1,4 @@ -.TH "dmmp_reconfig" 3 "dmmp_reconfig" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_reconfig" 3 "dmmp_reconfig" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_reconfig \- Instruct multipathd daemon to do reconfiguration. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_strerror.3 b/libdmmp/docs/man/dmmp_strerror.3 index 202941c9c..c1f80d315 100644 --- a/libdmmp/docs/man/dmmp_strerror.3 +++ b/libdmmp/docs/man/dmmp_strerror.3 @@ -1,4 +1,4 @@ -.TH "dmmp_strerror" 3 "dmmp_strerror" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_strerror" 3 "dmmp_strerror" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_strerror \- Convert error code to string. .SH SYNOPSIS diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h index 449e2825a..a4edb83be 100644 --- a/libmpathpersist/mpath_persist.h +++ b/libmpathpersist/mpath_persist.h @@ -113,7 +113,9 @@ struct prin_capdescr { uint16_t length; uint8_t flags[2]; - uint16_t pr_type_mask; + uint16_t pr_type_mask; /* The two bytes of the type mask are treated + as a single big-endian number. So the valid + type bits are 0xea01 */ uint16_t _reserved; }; diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c index ea8d65ea1..14f7ba830 100644 --- a/libmpathpersist/mpath_persist_int.c +++ b/libmpathpersist/mpath_persist_int.c @@ -536,19 +536,19 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, } } for (i = 0; i < count; i++) { - if (thread[i].param.status != MPATH_PR_SKIP && - thread[i].param.status != MPATH_PR_THREAD_ERROR) { + if (thread[i].param.status == MPATH_PR_SKIP) + continue; + if (thread[i].param.status != MPATH_PR_THREAD_ERROR) { rc = pthread_join(thread[i].id, NULL); if (rc) { condlog(3, "%s: failed to join thread while retrying %d", mpp->wwid, i); } - if (thread[i].param.status == - MPATH_PR_RETRYABLE_ERROR) - retryable_error = true; - else if (status == MPATH_PR_SUCCESS) - status = thread[i].param.status; } + if (thread[i].param.status == MPATH_PR_RETRYABLE_ERROR) + retryable_error = true; + else if (status == MPATH_PR_SUCCESS) + status = thread[i].param.status; } need_retry = false; } @@ -1001,12 +1001,12 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd, case MPATH_PROUT_REG_SA: case MPATH_PROUT_REG_IGN_SA: if (unregistering) - update_prflag(mpp->alias, 0); + update_prflag(mpp, 0); else - update_prflag(mpp->alias, 1); + update_prflag(mpp, 1); break; case MPATH_PROUT_CLEAR_SA: - update_prflag(mpp->alias, 0); + update_prflag(mpp, 0); if (mpp->prkey_source == PRKEY_SOURCE_FILE) update_prkey(mpp->alias, 0); break; diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c index bd8ed2be9..c925dcedf 100644 --- a/libmpathpersist/mpath_updatepr.c +++ b/libmpathpersist/mpath_updatepr.c @@ -20,8 +20,9 @@ #include "uxsock.h" #include "mpathpr.h" #include "structs.h" +#include "strbuf.h" -static char *do_pr(char *alias, char *str) +static char *do_pr(char *alias, const char *str) { int fd; char *reply; @@ -51,24 +52,26 @@ static char *do_pr(char *alias, char *str) return reply; } -static int do_update_pr(char *alias, char *cmd, char *key) +static int do_update_pr(char *alias, char *cmd, const char *data) { - char str[256]; + STRBUF_ON_STACK(buf); char *reply = NULL; int ret = -1; - if (key) - snprintf(str, sizeof(str), "%s map %s key %s", cmd, alias, key); + if (data) + print_strbuf(&buf, "%s map %s %s %s", cmd, alias, + strcmp(cmd, "setprkey") ? "pathlist" : "key", data); else - snprintf(str, sizeof(str), "%s map %s", cmd, alias); + print_strbuf(&buf, "%s map %s", cmd, alias); - reply = do_pr(alias, str); + reply = do_pr(alias, get_strbuf_str(&buf)); if (reply) { if (strncmp(reply, "ok", 2) == 0) ret = 0; else ret = -1; - condlog(ret ? 0 : 4, "%s: message=%s reply=%s", alias, str, reply); + condlog(ret ? 0 : 4, "%s: message=%s reply=%s", alias, + get_strbuf_str(&buf), reply); } free(reply); @@ -106,9 +109,31 @@ int get_prhold(char *mapname) return do_get_pr(mapname, "getprhold"); } -int update_prflag(char *mapname, int set) { - return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus", - NULL); +int update_prflag(struct multipath *mpp, int set) +{ + STRBUF_ON_STACK(buf); + int i, j; + bool first = true; + struct pathgroup *pgp = NULL; + struct path *pp = NULL; + + if (!set) + return do_update_pr(mpp->alias, "unsetprstatus", NULL); + + append_strbuf_str(&buf, "\""); + vector_foreach_slot (mpp->pg, pgp, j) { + vector_foreach_slot (pgp->paths, pp, i) { + if (pp->state == PATH_UP || pp->state == PATH_GHOST) { + if (first) { + append_strbuf_str(&buf, pp->dev); + first = false; + } else + print_strbuf(&buf, " %s", pp->dev_t); + } + } + } + append_strbuf_str(&buf, "\""); + return do_update_pr(mpp->alias, "setprstatus", get_strbuf_str(&buf)); } int update_prhold(char *mapname, bool set) diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h index 99d6b82ff..adbfc8653 100644 --- a/libmpathpersist/mpathpr.h +++ b/libmpathpersist/mpathpr.h @@ -1,12 +1,14 @@ #ifndef MPATHPR_H_INCLUDED #define MPATHPR_H_INCLUDED +#include "structs.h" + /* * This header file contains symbols that are only used by * libmpathpersist internally. */ -int update_prflag(char *mapname, int set); +int update_prflag(struct multipath *mpp, int set); int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags); int get_prflag(char *mapname); int get_prhold(char *mapname); diff --git a/libmpathutil/parser.c b/libmpathutil/parser.c index 131a5ed03..bf7c411b8 100644 --- a/libmpathutil/parser.c +++ b/libmpathutil/parser.c @@ -144,7 +144,7 @@ snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw, const void *data) { int r = 0; - char *f; + const char *f; struct config *conf; STRBUF_ON_STACK(sbuf); diff --git a/libmpathutil/util.c b/libmpathutil/util.c index 38e984fc8..37412c662 100644 --- a/libmpathutil/util.c +++ b/libmpathutil/util.c @@ -38,7 +38,7 @@ strchop(char *str) */ const char *libmp_basename(const char *filename) { - char *p = strrchr(filename, '/'); + const char *p = strrchr(filename, '/'); return p ? p + 1 : filename; } diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 31db8758b..4da8ea4a9 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -880,6 +880,8 @@ scsi_tmo_error_msg(struct path *pp) STRBUF_ON_STACK(proto_buf); unsigned int proto_id = bus_protocol_id(pp); + if (is_bit_set_in_bitfield(proto_id, bf)) + return; snprint_path_protocol(&proto_buf, pp); condlog(2, "%s: setting scsi timeouts is unsupported for protocol %s", pp->dev, get_strbuf_str(&proto_buf)); @@ -2557,6 +2559,8 @@ int pathinfo(struct path *pp, struct config *conf, int mask) * Recoverable error, for example faulty or offline path */ pp->chkrstate = pp->state = PATH_DOWN; + if (mask & DI_IOCTL && pp->ioctl_info == IOCTL_INFO_NOT_REQUESTED) + pp->ioctl_info = IOCTL_INFO_SKIPPED; if (pp->initialized == INIT_NEW || pp->initialized == INIT_FAILED) memset(pp->wwid, 0, WWID_SIZE); diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 925230422..23c549335 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -682,7 +682,7 @@ static struct hwentry default_hw[] = { .pgfailback = -FAILBACK_IMMEDIATE, }, { - // Storwize V5000/V7000 lines / SAN Volume Controller (SVC) + // Storwize V3x00/V5000/V7000 lines / SAN Volume Controller (SVC) // Flex System V7000 / FlashSystem V840/V9000 and 5x00/7x00/9x00/Cx00 .vendor = "IBM", .product = "^2145", @@ -1326,6 +1326,15 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_ALUA, .no_path_retry = 30, }, + { + // Exos / Nytro series + .vendor = "SEAGATE", + .product = "^[456]", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .prio_name = PRIO_ALUA, + .no_path_retry = 30, + }, /* * AccelStor */ diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version index 89ae2a3c5..5017a54db 100644 --- a/libmultipath/libmultipath.version +++ b/libmultipath/libmultipath.version @@ -85,7 +85,6 @@ global: dm_geteventnr; dm_get_major_minor; dm_get_maps; - dm_get_multipath; dm_is_mpath; dm_mapname; dm_prereq; diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c index 4fbde5ad2..ebed0d090 100644 --- a/libmultipath/prkey.c +++ b/libmultipath/prkey.c @@ -50,7 +50,7 @@ static int parse_prkey(const char *ptr, uint64_t *prkey) return 0; } -int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags) +int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags) { char *flagstr; diff --git a/libmultipath/prkey.h b/libmultipath/prkey.h index a89a617bd..806795ed3 100644 --- a/libmultipath/prkey.h +++ b/libmultipath/prkey.h @@ -15,7 +15,7 @@ int print_reservation_key(struct strbuf *buff, struct be64 key, uint8_t flags, int source); -int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags); +int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags); int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey, uint8_t sa_flags); int get_prkey(struct multipath *mpp, uint64_t *prkey, uint8_t *sa_flags); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index dfa547bb3..ee02b7aca 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -239,18 +239,8 @@ free_pgvec (vector pgvec, enum free_path_mode free_paths) if (!pgvec) return; - vector_foreach_slot(pgvec, pgp, i) { - - /* paths are going to be re-grouped, reset pgindex */ - if (free_paths != FREE_PATHS) { - struct path *pp; - int j; - - vector_foreach_slot(pgp->paths, pp, j) - pp->pgindex = 0; - } + vector_foreach_slot (pgvec, pgp, i) free_pathgroup(pgp, free_paths); - } vector_free(pgvec); } diff --git a/libmultipath/version.h b/libmultipath/version.h index e8f9be00b..d543c9bab 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -11,9 +11,9 @@ #ifndef VERSION_H_INCLUDED #define VERSION_H_INCLUDED -#define VERSION_CODE 0x000D00 +#define VERSION_CODE 0x000D01 /* MMDDYY, in hex */ -#define DATE_CODE 0x0A0A19 +#define DATE_CODE 0x01141A #define PROG "multipath-tools" diff --git a/mpathpersist/main.c b/mpathpersist/main.c index 97fd5a43f..fea5a9cbf 100644 --- a/mpathpersist/main.c +++ b/mpathpersist/main.c @@ -748,25 +748,42 @@ void mpath_print_buf_readcap( struct prin_resp *pr_buff) printf("Report capabilities response:\n"); - printf(" Compatible Reservation Handling(CRH): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x10)); - printf(" Specify Initiator Ports Capable(SIP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x8)); - printf(" All Target Ports Capable(ATP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x4 )); - printf(" Persist Through Power Loss Capable(PTPL_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0])); - printf(" Type Mask Valid(TMV): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80)); - printf(" Allow Commands: %d\n", !!(( pr_buff->prin_descriptor.prin_readcap.flags[1] >> 4) & 0x7)); + printf(" Compatible Reservation Handling(CRH): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x10)); + printf(" Specify Initiator Ports Capable(SIP_C): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x8)); + printf(" All Target Ports Capable(ATP_C): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x4)); + printf(" Persist Through Power Loss Capable(PTPL_C): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x1)); + printf(" Type Mask Valid(TMV): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80)); + printf(" Allow Commands: %d\n", + !!((pr_buff->prin_descriptor.prin_readcap.flags[1] >> 4) & 0x7)); printf(" Persist Through Power Loss Active(PTPL_A): %d\n", - !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x1)); + !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x1)); if(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80) { printf(" Support indicated in Type mask:\n"); - printf(" %s: %d\n", pr_type_strs[7], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x80); - printf(" %s: %d\n", pr_type_strs[6], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x40); - printf(" %s: %d\n", pr_type_strs[5], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x20); - printf(" %s: %d\n", pr_type_strs[3], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x8); - printf(" %s: %d\n", pr_type_strs[1], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x2); - printf(" %s: %d\n", pr_type_strs[8], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x100); + printf(" %s: %d\n", pr_type_strs[7], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x8000)); + printf(" %s: %d\n", pr_type_strs[6], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x4000)); + printf(" %s: %d\n", pr_type_strs[5], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x2000)); + printf(" %s: %d\n", pr_type_strs[3], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x800)); + printf(" %s: %d\n", pr_type_strs[1], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x200)); + printf(" %s: %d\n", pr_type_strs[8], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x1)); } } diff --git a/multipathd/callbacks.c b/multipathd/callbacks.c index b6b57f458..2027c3a66 100644 --- a/multipathd/callbacks.c +++ b/multipathd/callbacks.c @@ -59,6 +59,8 @@ void init_handler_callbacks(void) set_unlocked_handler_callback(VRB_SHUTDOWN, HANDLER(cli_shutdown)); set_handler_callback(VRB_GETPRSTATUS | Q1_MAP, HANDLER(cli_getprstatus)); set_handler_callback(VRB_SETPRSTATUS | Q1_MAP, HANDLER(cli_setprstatus)); + set_handler_callback(VRB_SETPRSTATUS | Q1_MAP | Q2_PATHLIST, + HANDLER(cli_setprstatus_list)); set_handler_callback(VRB_UNSETPRSTATUS | Q1_MAP, HANDLER(cli_unsetprstatus)); set_handler_callback(VRB_FORCEQ | Q1_DAEMON, HANDLER(cli_force_no_daemon_q)); set_handler_callback(VRB_RESTOREQ | Q1_DAEMON, HANDLER(cli_restore_no_daemon_q)); diff --git a/multipathd/cli.c b/multipathd/cli.c index d0e6cebc9..0d679c867 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -227,6 +227,7 @@ load_keys (void) r += add_key(keys, "getprhold", VRB_GETPRHOLD, 0); r += add_key(keys, "setprhold", VRB_SETPRHOLD, 0); r += add_key(keys, "unsetprhold", VRB_UNSETPRHOLD, 0); + r += add_key(keys, "pathlist", KEY_PATHLIST, 1); if (r) { free_keys(keys); diff --git a/multipathd/cli.h b/multipathd/cli.h index 5a943a455..3e607389a 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -62,6 +62,7 @@ enum { KEY_LOCAL = 81, KEY_GROUP = 82, KEY_KEY = 83, + KEY_PATHLIST = 84, }; /* @@ -94,6 +95,7 @@ enum { Q2_LOCAL = KEY_LOCAL << 16, Q2_GROUP = KEY_GROUP << 16, Q2_KEY = KEY_KEY << 16, + Q2_PATHLIST = KEY_PATHLIST << 16, /* byte 3: qualifier 3 */ Q3_FMT = KEY_FMT << 24, diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index 7f572fb44..53be68df4 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -31,6 +31,7 @@ #include "foreign.h" #include "strbuf.h" #include "cli_handlers.h" +#include static struct path * find_path_by_str(const struct vector_s *pathvec, const char *str, @@ -1276,8 +1277,8 @@ cli_getprstatus (void * v, struct strbuf *reply, void * data) return 0; } -static int -cli_setprstatus(void * v, struct strbuf *reply, void * data) +static int do_setprstatus(void *v, struct strbuf *reply, void *data, + const struct vector_s *registered_paths) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; @@ -1291,13 +1292,45 @@ cli_setprstatus(void * v, struct strbuf *reply, void * data) if (mpp->prflag != PR_SET) { set_pr(mpp); - condlog(2, "%s: prflag set", param); + pr_register_active_paths(mpp, registered_paths); + if (mpp->prflag == PR_SET) + condlog(2, "%s: prflag set", param); + else + condlog(0, "%s: Failed to set prflag", param); } memset(&mpp->old_pr_key, 0, 8); return 0; } +static int cli_setprstatus(void *v, struct strbuf *reply, void *data) +{ + return do_setprstatus(v, reply, data, NULL); +} + +static int cli_setprstatus_list(void *v, struct strbuf *reply, void *data) +{ + int r; + struct vector_s registered_paths_vec = {.allocated = 0}; + vector registered_paths + __attribute__((cleanup(cleanup_reset_vec))) = ®istered_paths_vec; + char *ptr = get_keyparam(v, KEY_PATHLIST); + + while (isspace(*ptr)) + ptr++; + while (*ptr) { + if (!vector_alloc_slot(registered_paths)) + return -ENOMEM; + vector_set_slot(registered_paths, ptr); + while (*ptr && !isspace(*ptr)) + ptr++; + while (isspace(*ptr)) + *ptr++ = '\0'; + } + r = do_setprstatus(v, reply, data, registered_paths); + return r; +} + static int cli_unsetprstatus(void * v, struct strbuf *reply, void * data) { diff --git a/multipathd/init_unwinder.c b/multipathd/init_unwinder.c index b1cd283d5..d39e4282f 100644 --- a/multipathd/init_unwinder.c +++ b/multipathd/init_unwinder.c @@ -34,5 +34,7 @@ int init_unwinder(void) pthread_mutex_unlock(&dummy_mtx); - return pthread_cancel(dummy); + rc = pthread_cancel(dummy); + pthread_join(dummy, NULL); + return rc; } diff --git a/multipathd/main.c b/multipathd/main.c index d11a8576c..64ba1fb8c 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -90,16 +90,16 @@ #define MSG_SIZE 32 static unsigned int -mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed); +mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed, + unsigned int nr_keys_wanted); #define LOG_MSG(lvl, pp) \ do { \ - if (pp->mpp && checker_selected(&pp->checker) && \ - lvl <= libmp_verbosity) { \ - if (pp->sysfs_state == PATH_DOWN) \ + if (pp->mpp && lvl <= libmp_verbosity) { \ + if (pp->sysfs_state != PATH_UP) \ condlog(lvl, "%s: %s - path offline", \ pp->mpp->alias, pp->dev); \ - else { \ + else if (checker_selected(&pp->checker)) { \ const char *__m = \ checker_message(&pp->checker); \ \ @@ -629,19 +629,47 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) { return true; } -static void -pr_register_active_paths(struct multipath *mpp) +/* + * If reg_paths in non-NULL, it is a vector of paths that libmpathpersist + * registered. If the number of registered keys is smaller than the number + * of registered paths, then likely a preempt that occurred while + * libmpathpersist was registering the key. As long as there are still some + * registered keys, treat the preempt as happening first, and make sure to + * register keys on all the paths. If the number of registered keys is at + * least as large as the number of registered paths, then no preempt happened, + * and multipathd does not need to re-register the paths that libmpathpersist + * handled + */ +void pr_register_active_paths(struct multipath *mpp, const struct vector_s *reg_paths) { - unsigned int i, j, nr_keys = 0; + unsigned int i, j, k, nr_keys = 0; + unsigned int wanted_nr = VECTOR_SIZE(reg_paths); struct path *pp; struct pathgroup *pgp; + char *pathname; vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp, j) { if (mpp->prflag == PR_UNSET) return; - if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST)) - nr_keys = mpath_pr_event_handle(pp, nr_keys); + if (pp->state != PATH_UP && pp->state != PATH_GHOST) + continue; + if (wanted_nr && nr_keys) { + vector_foreach_slot (reg_paths, pathname, k) { + if (strcmp(pp->dev_t, pathname) == 0) { + goto skip; + } + } + } + nr_keys = mpath_pr_event_handle(pp, nr_keys, wanted_nr); + if (nr_keys && nr_keys < wanted_nr) { + /* + * Incorrect number of registered keys. Need + * to register all devices + */ + wanted_nr = 0; + } + skip:; /* a statement must follow a label on pre C23 clang */ } } } @@ -672,8 +700,7 @@ handle_orphaned_offline_paths(vector offline_paths) pp->add_when_online = true; } -static void -cleanup_reset_vec(struct vector_s **v) +void cleanup_reset_vec(struct vector_s **v) { vector_reset(*v); } @@ -729,7 +756,7 @@ update_map (struct multipath *mpp, struct vectors *vecs, int new_map) sync_map_state(mpp, false); - pr_register_active_paths(mpp); + pr_register_active_paths(mpp, NULL); if (VECTOR_SIZE(offline_paths) != 0) handle_orphaned_offline_paths(offline_paths); @@ -1319,7 +1346,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) verify_paths(mpp); mpp->action = ACT_RELOAD; prflag = mpp->prflag; - mpath_pr_event_handle(pp, 0); + mpath_pr_event_handle(pp, 0, 0); } else { if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) { orphan_path(pp, "only one path"); @@ -1403,7 +1430,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) if (retries >= 0) { if ((mpp->prflag == PR_SET && prflag != PR_SET) || start_waiter) - pr_register_active_paths(mpp); + pr_register_active_paths(mpp, NULL); condlog(2, "%s [%s]: path added to devmap %s", pp->dev, pp->dev_t, mpp->alias); return 0; @@ -2517,6 +2544,26 @@ sync_mpp(struct vectors * vecs, struct multipath *mpp, unsigned int ticks) return do_sync_mpp(vecs, mpp); } +/* + * pp->wwid should never be empty when this function is called, but if it + * is, this function can set it. + */ +static bool new_path_wwid_changed(struct path *pp, int state) +{ + char wwid[WWID_SIZE]; + + strlcpy(wwid, pp->wwid, WWID_SIZE); + if (get_uid(pp, state, pp->udev, 1) != 0) { + strlcpy(pp->wwid, wwid, WWID_SIZE); + return false; + } + if (strlen(wwid) && strncmp(wwid, pp->wwid, WWID_SIZE) != 0) { + strlcpy(pp->wwid, wwid, WWID_SIZE); + return true; + } + return false; +} + static int update_path_state (struct vectors * vecs, struct path * pp) { @@ -2545,14 +2592,34 @@ update_path_state (struct vectors * vecs, struct path * pp) pp->tick = 1; return CHECK_PATH_SKIPPED; } - if (pp->recheck_wwid == RECHECK_WWID_ON && - (newstate == PATH_UP || newstate == PATH_GHOST) && + + if ((newstate == PATH_UP || newstate == PATH_GHOST) && ((pp->state != PATH_UP && pp->state != PATH_GHOST) || - pp->dmstate == PSTATE_FAILED) && - check_path_wwid_change(pp)) { - condlog(0, "%s: path wwid change detected. Removing", pp->dev); - return handle_path_wwid_change(pp, vecs)? CHECK_PATH_REMOVED : - CHECK_PATH_SKIPPED; + pp->dmstate == PSTATE_FAILED)) { + bool wwid_changed = false; + + if (pp->initialized == INIT_NEW) { + /* + * Path was added to map while offline, mark it as + * initialized. + * DI_SYSFS was checked when the path was added + * DI_IOCTL was checked when the checker was selected + * DI_CHECKER just got checked + * DI_WWID is about to be checked + * DI_PRIO will get checked at the end of this checker + * loop + */ + pp->initialized = INIT_OK; + wwid_changed = new_path_wwid_changed(pp, newstate); + } else if (pp->recheck_wwid == RECHECK_WWID_ON) + wwid_changed = check_path_wwid_change(pp); + if (wwid_changed) { + condlog(0, "%s: path wwid change detected. Removing", + pp->dev); + return handle_path_wwid_change(pp, vecs) + ? CHECK_PATH_REMOVED + : CHECK_PATH_SKIPPED; + } } if ((newstate != PATH_UP && newstate != PATH_GHOST && newstate != PATH_PENDING) && (pp->state == PATH_DELAYED)) { @@ -2646,9 +2713,9 @@ update_path_state (struct vectors * vecs, struct path * pp) */ condlog(2, "%s: checking persistent " "reservation registration", pp->dev); - mpath_pr_event_handle(pp, 0); + mpath_pr_event_handle(pp, 0, 0); if (pp->mpp->prflag == PR_SET && prflag != PR_SET) - pr_register_active_paths(pp->mpp); + pr_register_active_paths(pp->mpp, NULL); } /* @@ -3309,7 +3376,7 @@ configure (struct vectors * vecs, enum force_reload_types reload_type) vector_foreach_slot(mpvec, mpp, i){ if (remember_wwid(mpp->wwid) == 1) trigger_paths_udev_change(mpp, true); - pr_register_active_paths(mpp); + pr_register_active_paths(mpp, NULL); } /* @@ -3739,18 +3806,21 @@ static struct call_rcu_data *mp_rcu_data; static void cleanup_rcu(void) { - pthread_t rcu_thread; - /* Wait for any pending RCU calls */ rcu_barrier(); if (mp_rcu_data != NULL) { +#if (URCU_VERSION < 0x000E00) + pthread_t rcu_thread; rcu_thread = get_call_rcu_thread(mp_rcu_data); +#endif /* detach this thread from the RCU thread */ set_thread_call_rcu_data(NULL); synchronize_rcu(); /* tell RCU thread to exit */ call_rcu_data_free(mp_rcu_data); +#if (URCU_VERSION < 0x000E00) pthread_join(rcu_thread, NULL); +#endif } rcu_unregister_thread(); } @@ -3790,7 +3860,7 @@ child (__attribute__((unused)) void *param) int rc; struct config *conf; char *envp; - enum daemon_status state; + enum daemon_status state = DAEMON_INIT; int exit_code = 1; int fpin_marginal_paths = 0; @@ -4267,7 +4337,10 @@ void unset_pr(struct multipath *mpp) * * The number of found keys must be at least as large as *nr_keys, * and if MPATH_PR_SUCCESS is returned and mpp->prflag is PR_SET after - * the call, *nr_keys will be set to the number of found keys. + * the call, *nr_keys will be set to the number of found keys. Otherwise + * if mpp->prflag is PR_UNSET it will be set to 0. If MPATH_PR_SUCCESS + * is not returned and mpp->prflag is not PR_UNSET, nr_keys will not be + * changed. */ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *nr_keys) { @@ -4277,8 +4350,10 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n bool was_set = (mpp->prflag == PR_SET); /* If pr is explicitly unset, it must be manually set */ - if (mpp->prflag == PR_UNSET) + if (mpp->prflag == PR_UNSET) { + *nr_keys = 0; return MPATH_PR_SUCCESS; + } if (!get_be64(mpp->reservation_key)) { /* Nothing to do. Assuming pr mgmt feature is disabled*/ @@ -4286,6 +4361,7 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias); + *nr_keys = 0; return MPATH_PR_SUCCESS; } @@ -4293,8 +4369,10 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, &resp, 0); if (ret != MPATH_PR_SUCCESS) { - if (ret == MPATH_PR_ILLEGAL_REQ) + if (ret == MPATH_PR_ILLEGAL_REQ) { unset_pr(mpp); + *nr_keys = 0; + } condlog(0, "%s : pr in read keys service action failed Error=%d", mpp->alias, ret); return ret; @@ -4335,22 +4413,32 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n condlog(was_set ? 1 : 3, "%s: %u keys found. needed %u. prflag unset.", mpp->alias, nr_found, *nr_keys); + *nr_keys = 0; } return MPATH_PR_SUCCESS; } /* - * This function is called with the number of registered keys that should be + * This function is called with two numbers + * + * nr_keys_needed: the number of registered keys that should be * seen for this device to know that the key has not been preempted while the * path was getting registered. If 0 is passed in, update_mpath_pr is called * before registering the key to figure out the number, assuming that at * least one key must exist. * + * nr_keys_wanted: Only used if nr_keys_needed is 0, so we don't know how + * many keys we currently have. If nr_keys_wanted in non-zero and the + * number of keys found by the initial call to update_map_pr() is at least + * as large as it, exit early, since we have all the keys we are expecting. + * * The function returns the number of keys that are registered or 0 if * it's unknown. */ -static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed) +static unsigned int +mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed, + unsigned int nr_keys_wanted) { struct multipath *mpp = pp->mpp; int ret; @@ -4366,6 +4454,8 @@ static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_ nr_keys_needed = 1; if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS) return 0; + if (nr_keys_wanted && nr_keys_wanted <= nr_keys_needed) + return nr_keys_needed; } check_prhold(mpp, pp); @@ -4400,7 +4490,7 @@ static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_ clear_reg ? "Clearing" : "Setting", pp->dev, ret); } else if (!clear_reg) { if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS) - return 0; + return nr_keys_needed; if (mpp->prflag != PR_SET) { memset(¶m, 0, sizeof(param)); clear_reg = true; diff --git a/multipathd/main.h b/multipathd/main.h index 29b57e3de..b66208ec5 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -54,4 +54,7 @@ int resize_map(struct multipath *mpp, unsigned long long size, struct vectors *vecs); void set_pr(struct multipath *mpp); void unset_pr(struct multipath *mpp); +void pr_register_active_paths(struct multipath *mpp, + const struct vector_s *registered_paths); +void cleanup_reset_vec(struct vector_s **v); #endif /* MAIN_H_INCLUDED */ diff --git a/multipathd/multipathd.8.in b/multipathd/multipathd.8.in index 8815e099f..38a243e3e 100644 --- a/multipathd/multipathd.8.in +++ b/multipathd/multipathd.8.in @@ -344,48 +344,54 @@ will not be disabled when the daemon stops. Restores configured queue_without_daemon mode. . .TP -.B map|multipath $map setprstatus +.B setprstatus map|multipath $map Enable persistent reservation management on $map. . .TP -.B map|multipath $map unsetprstatus +.B setprstatus map|multipath $map pathlist $pathlist +Enable persistent reservation management on $map, and notify multipathd of +the paths that have been registered, so it doesn't attempt to re-register +them. +. +.TP +.B unsetprstatus map|multipath $map Disable persistent reservation management on $map. . .TP -.B map|multipath $map getprstatus +.B getprstatus map|multipath $map Get the current persistent reservation management status of $map. . .TP -.B map|multipath $map getprkey +.B getprkey map|multipath $map Get the current persistent reservation key associated with $map. . .TP -.B map|multipath $map setprkey key $key +.B setprkey map|multipath $map key $key Set the persistent reservation key associated with $map to $key in the \fIprkeys_file\fR. This key will only be used by multipathd if \fIreservation_key\fR is set to \fBfile\fR in \fI@CONFIGFILE@\fR. . .TP -.B map|multipath $map unsetprkey +.B unsetprkey map|multipath $map Remove the persistent reservation key associated with $map from the \fIprkeys_file\fR. This will only unset the key used by multipathd if \fIreservation_key\fR is set to \fBfile\fR in \fI@CONFIGFILE@\fR. . .TP -.B path $path setmarginal +.B setmarginal path $path move $path to a marginal pathgroup. The path will remain in the marginal path group until \fIunsetmarginal\fR is called. This command will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths detection method configured (see the multipath.conf man page for details). . .TP -.B path $path unsetmarginal +.B unsetmarginal path $path return marginal path $path to its normal pathgroup. This command will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths detection method configured (see the multipath.conf man page for details). . .TP -.B map $map unsetmarginal +.B unsetmarginal map $map return all marginal paths in $map to their normal pathgroups. This command will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths detection method configured (see the multipath.conf man page for details). diff --git a/tests/Makefile b/tests/Makefile index 28c00ad2d..0ec036ebd 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -5,7 +5,8 @@ TESTDIR := $(CURDIR) CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir) -I$(daemondir) \ -DTESTCONFDIR=\"$(TESTDIR)/conf.d\" -CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS) +CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS) \ + $(if $(shell [ $(CMOCKA_VERSION) -ge 131072 ] && echo yes),-Wno-error=deprecated-declarations) LIBDEPS += -L. -L $(mpathutildir) -L$(mpathcmddir) -lmultipath -lmpathutil -lmpathcmd -lcmocka TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \ diff --git a/tests/vpd.c b/tests/vpd.c index e3212e614..a72865337 100644 --- a/tests/vpd.c +++ b/tests/vpd.c @@ -262,8 +262,20 @@ static int create_scsi_string_desc(unsigned char *desc, assert_in_range(type, STR_EUI, STR_IQN); assert_true(maxlen % 4 == 0); +#if defined(__GNUC__) && __GNUC__ >= 15 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-truncation=" + /* + * This is called from test_vpd_str_XYZ() functions, where + * we deliberately use the overlong input string test_id. + * So format truncation is expected here. + */ +#endif len = snprintf((char *)(desc + 4), maxlen, "%s%s", str_prefix[type], id); +#if defined(__GNUC__) && __GNUC__ >= 15 +#pragma GCC diagnostic pop +#endif if (len > maxlen) len = maxlen; /* zero-pad */ @@ -451,7 +463,6 @@ static void test_vpd_str_ ## typ ## _ ## len ## _ ## wlen(void **state) \ int n, ret; \ int exp_len; \ int type = typ & STR_MASK; \ - \ n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, \ 8, typ, len); \ exp_len = len - strlen(str_prefix[type]); \