From 02374b88cf369ddb05e57493b4b181da9d01ab7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=87=A1=E4=B8=9C?= Date: Tue, 22 Sep 2026 19:22:04 +0200 Subject: [PATCH 1/4] docs: format README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 秦凡东 --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index dfcdf3376..78826a64e 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](LICENSE.md) [![Maintained by Unvertical](https://img.shields.io/badge/maintained%20by-Unvertical-0b5fff)](https://unvertical.com/) -Open CAS accelerates Linux applications by caching active (hot) data to +Open CAS accelerates Linux applications by caching active (hot) data to a local flash device inside servers. Open CAS implements caching at the server level, utilizing local high-performance flash media as the cache drive media inside the application server as close as possible to the CPU, thus reducing storage latency as much as possible. The Open Cache Acceleration Software installs into the GNU/Linux operating system itself, as a kernel module. The nature of the integration provides a -cache solution that is transparent to users and applications, and your +cache solution that is transparent to users and applications, and your existing storage infrastructure. No storage migration effort or application changes are required. @@ -72,14 +72,16 @@ you need to run `./configure` once again - this time it should succeed. Alternatively, you can generate RPM/DEB packages from downloaded sources and install those packages instead. To do so, simply run: -__on RPM based systems:__ +**on RPM based systems:** + ``` make rpm rm -f packages/*debug* dnf install ./packages/open-cas-linux*.rpm ``` -__on DEB based systems:__ +**on DEB based systems:** + ``` make deb apt install ./packages/open-cas-linux*.deb @@ -105,9 +107,9 @@ Before running tests make sure you have a platform with at least 2 disks (one fo 1. Go to test directory `cd test/functional`. 1. Install dependencies with command `pip3 install -r requirements.txt`. 1. Create DUT config. See example [here](test/functional/config/example_dut_config.yml). - a) Set disks params. You need at least two disks, of which at least one is an SSD drive. - b) For remote execution uncomment and set the `ip`, `user` and `password` fields. - c) For local execution just leave these fields commented. + a) Set disks params. You need at least two disks, of which at least one is an SSD drive. + b) For remote execution uncomment and set the `ip`, `user` and `password` fields. + c) For local execution just leave these fields commented. 1. Run tests using command `pytest-3 --dut-config=` where `` is path to your config file, for example `pytest-3 --dut-config="config/dut_config.yml"`. ## Contributing From 343d9422bdb3a1bfeebf28491b8b1717d727991e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=87=A1=E4=B8=9C?= Date: Tue, 22 Sep 2026 19:21:47 +0200 Subject: [PATCH 2/4] rpm: Add DKMS as an optional way to ship the kernel modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the DEB packaging: ship DKMS source to /usr/src/ and build the kernel modules on the target at install time instead of compiling them for a specific kernel at package build time. For building DKMS packages the build host no longer needs kernel-devel/kernel-headers. spec: - DKMS source tree + dkms.conf (heredoc); %post/%preun modules do dkms add/install/remove with --rpm_safe_upgrade on add+remove (per dkms(8)) - drop the kernel-version-specific subpackage and weak-modules/depmod logic (DKMS builds per-kernel natively) - build only userspace in %build; scrub OCF-synced headers and utils manpages so the DKMS tree ships source-only pckgen.sh: - --with-dkms/--without-dkms, plumbed to rpmbuild and mock as the matching bcond; --kernel-version is skipped in DKMS mode, where no kernel is selected at build time - in DKMS mode build only casadm ( -> make -C casadm, matching DEB) Makefile: - 'make rpm-dkms' alongside 'make rpm' The opencas_exporter remains experimental for the current release, so do not include it into the package. Co-authored-by: GLM-5.2 Signed-off-by: 秦凡东 Signed-off-by: Robert Baldyga --- Makefile | 5 ++ README.md | 10 ++- tools/pckgen.d/rpm/CAS_NAME.spec | 138 ++++++++++++++++++++++++++++++- tools/pckgen.sh | 44 ++++++++-- 4 files changed, 186 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 4279021ec..0b53b7d9f 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ all $(MAKECMDGOALS): $(DIRS) $(DIRS): ifneq ($(MAKECMDGOALS),archives) ifneq ($(MAKECMDGOALS),rpm) +ifneq ($(MAKECMDGOALS),rpm-dkms) ifneq ($(MAKECMDGOALS),srpm) ifneq ($(MAKECMDGOALS),deb) ifneq ($(MAKECMDGOALS),dsc) @@ -29,6 +30,7 @@ endif endif endif endif +endif archives: @tools/pckgen.sh $(PWD) tar zip @@ -36,6 +38,9 @@ archives: rpm: @tools/pckgen.sh $(PWD) rpm --debug +rpm-dkms: + @tools/pckgen.sh $(PWD) rpm --debug --with-dkms + srpm: @tools/pckgen.sh $(PWD) srpm diff --git a/README.md b/README.md index 78826a64e..228cac177 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,15 @@ rm -f packages/*debug* dnf install ./packages/open-cas-linux*.rpm ``` -**on DEB based systems:** +**on RPM based systems (with DKMS):** + +``` +make rpm-dkms +rm -f packages/*debug* +dnf install ./packages/open-cas-linux*.rpm +``` + +**on DEB based systems (with DKMS):** ``` make deb diff --git a/tools/pckgen.d/rpm/CAS_NAME.spec b/tools/pckgen.d/rpm/CAS_NAME.spec index f092aad83..32baf6920 100644 --- a/tools/pckgen.d/rpm/CAS_NAME.spec +++ b/tools/pckgen.d/rpm/CAS_NAME.spec @@ -11,16 +11,32 @@ # It contains tags in form of which are substituted with particular # values in the build time. # +# The kernel modules can be packaged in two ways, selected with the 'dkms' +# build conditional: +# +# (default) modules prebuilt for one specific kernel, shipped +# in a -modules_k subpackage. +# The build host needs the matching kernel-devel. +# +# rpmbuild --with dkms the module sources, shipped as a DKMS source tree +# and built on the target at install time. It requires +# DKMS support on the target node. %global __python %{__python3} +# Ship the kernel modules as DKMS sources built on the target instead of +# modules prebuilt for a single kernel. Off by default. Enable with: +# rpmbuild --with dkms (or: ./tools/pckgen.sh rpm --with-dkms) +%bcond_with dkms +%if %{without dkms} %define kver # Following define takes kernel version, cuts everything after (and including) # second hyphen (-), and then cuts the architecture (including 'noarch') part. # It's the only package version variant that is accepted by RPM spec. %define kver_pkg %{expand:%(kpkg="%{kver}"; for i in $(seq 2 $(grep -o '-' <<<$kpkg | grep -c .)); do kpkg="${kpkg%%-*}"; done; kpkg="${kpkg%%.$(uname -m)}"; echo "${kpkg%%.noarch}")} %define kver_filename k%{expand:%(echo "%{kver}" | sed -r "y/-/_/;")} +%endif Name: @@ -38,6 +54,7 @@ BuildRequires: gcc BuildRequires: make BuildRequires: procps BuildRequires: python3 +%if %{without dkms} # Allow using different version of kernel-headers package (some distros requires it). BuildRequires: kernel-headers BuildRequires: = %{kver_pkg} @@ -45,6 +62,9 @@ BuildRequires: = %{kver_pkg} BuildRequires: BuildRequires: Requires: -modules-%{version} +%else +Requires: %{name}-modules = %{version}-%{release} +%endif Requires: python3 Requires: python3-PyYAML Requires: sed @@ -57,6 +77,7 @@ This package contains tools and utilities for managing CAS and monitor running cache instances. +%if %{without dkms} %package modules_%{kver_filename} Summary: Open Cache Acceleration Software kernel modules Group: System @@ -68,6 +89,21 @@ encompassing block caching software libraries, adapters, tools and more. The main goal of this cache acceleration software is to accelerate a backend block device(s) by utilizing a higher performance device(s). This package contains only CAS kernel modules. +%else +%package modules +Summary: Open Cache Acceleration Software kernel modules (DKMS source) +Group: System +BuildArch: noarch +Requires: dkms +%description modules +Open Cache Acceleration Software (Open CAS) is an open source project +encompassing block caching software libraries, adapters, tools and more. +The main goal of this cache acceleration software is to accelerate a +backend block device(s) by utilizing a higher performance device(s). +This package contains the DKMS source tree for the CAS kernel modules. +The modules are compiled and installed on the target system by DKMS for +the kernels installed there, so no prebuilt kernel modules are shipped here. +%endif %prep @@ -75,14 +111,76 @@ This package contains only CAS kernel modules. %build +%if %{without dkms} export KERNEL_DIR=/lib/modules/%{kver}/build/ ./configure --kernel-dir $KERNEL_DIR +%else +# Only userspace is built here. Kernel modules are built on the target via DKMS. +(cd tools/; ./cas_version_gen.sh build) +make -C utils + +%endif %install rm -rf $RPM_BUILD_ROOT + +%if %{without dkms} /usr/bin/make install_files DESTDIR=$RPM_BUILD_ROOT KERNEL_VERSION=%{kver} +%else +# Install userspace before scrubbing the DKMS source tree below. +(cd casadm; make install_files DESTDIR="$RPM_BUILD_ROOT") +(cd utils; make install_files DESTDIR="$RPM_BUILD_ROOT") + +# Scrub build artifacts so the DKMS tree ships source-only: distsync reverses +# the OCF header sync into modules/, utils clean drops manpages, find strips +# any stray kernel build objects. +make -C modules distsync +make -C utils clean +find modules -type f \( -name '*.ko' -o -name '*.o' -o -name '*.cmd' \ + -o -name '*.mod.c' -o -name '*.mod' -o -name 'modules.order' \ + -o -name 'modules.builtin' \) -delete 2>/dev/null || : +rm -rf modules/.tmp_versions 2>/dev/null || : + +# Regenerate version metadata (no build timestamp) for the DKMS source tree. +(cd tools/; ./cas_version_gen.sh) + +# Install DKMS source tree. +DKMS_TREE=%{name}-modules-%{version} +DKMS_ROOT="$RPM_BUILD_ROOT/usr/src/$DKMS_TREE" +install -d -m 755 "$DKMS_ROOT" "$DKMS_ROOT/.metadata" "$DKMS_ROOT/tools" +cp -a .metadata/* "$DKMS_ROOT/.metadata/" 2>/dev/null || : +cp -a modules "$DKMS_ROOT/" +cp -a ocf "$DKMS_ROOT/" +cp -a utils "$DKMS_ROOT/" +cp -a tools/cas_version_gen.sh tools/helpers.mk "$DKMS_ROOT/tools/" +cp -a configure.d "$DKMS_ROOT/" 2>/dev/null || : +cp -a configure Makefile LICENSE.md version "$DKMS_ROOT/" + +# dkms.conf: // are substituted by +# pckgen.sh; $kernelver is a DKMS var (literal via the quoted heredoc). +# No -j here: dkms prepends "make -j ..." (get_num_cpus), and a later +# bare "-j" would win (last -j = unlimited) and clobber the nproc count. +# DKMS builds for a kernel that is not necessarily the running one, so the +# kernel has to be spelled out for './configure' as well - left to itself it +# probes the running kernel and generates a header for the wrong kernel API, +# which 'make' then refuses to build against. +cat > "$DKMS_ROOT/dkms.conf" <<'EOF' +PACKAGE_NAME="-modules" +PACKAGE_VERSION="" +BUILT_MODULE_NAME[0]="cas_cache" +BUILT_MODULE_LOCATION[0]="modules/cas_cache/" +DEST_MODULE_LOCATION[0]="/" +BUILT_MODULE_NAME[1]="cas_bd" +BUILT_MODULE_LOCATION[1]="modules/cas_bd/" +DEST_MODULE_LOCATION[1]="/" +DKMS_KERNEL_DIR="/lib/modules/$kernelver/build" +PRE_BUILD="./configure --kernel-dir $DKMS_KERNEL_DIR" +MAKE[0]="make -C modules/ KERNEL_VERSION=$kernelver KERNEL_DIR=$DKMS_KERNEL_DIR" +AUTOINSTALL=yes +EOF +%endif %post @@ -104,6 +202,7 @@ if [ $1 -eq 0 ]; then fi +%if %{without dkms} %post modules_%{kver_filename} depmod . /etc/os-release @@ -144,6 +243,35 @@ if [ $1 -eq 0 ]; then fi depmod fi +%else +%post modules +# Register the DKMS tree. --rpm_safe_upgrade keeps the add+remove pair safe +# across RPM upgrades (dkms(8); also on %preun remove). || : — dkms add +# returns 3 on re-add (reinstall), not a real failure. +dkms add -m %{name}-modules -v %{version} --rpm_safe_upgrade || : +# Build for the running kernel. No || : — fail visibly if kernel-devel is +# missing (dkms returns 0 for "already installed", so reinstalls still work). +dkms install -m %{name}-modules -v %{version} -k "$(uname -r)" || exit 1 +# Best-effort build for other installed kernels (skip those without +# kernel-devel) so a fallback kernel isn't left without modules. +for kver in $(ls /lib/modules 2>/dev/null | grep -vxF "$(uname -r)"); do + [ -d "/lib/modules/$kver/build" ] || continue + dkms install -m %{name}-modules -v %{version} -k "$kver" || : +done + +%preun modules +dkms remove -m %{name}-modules -v %{version} --all --rpm_safe_upgrade || : +# dkms weak-links the built modules into kABI-compatible kernels it did not +# build for (/lib/modules//weak-updates/), and `dkms remove` drops the +# built modules while leaving those symlinks behind. Dangling ones break +# dracut ("installkernel failed in module kernel-modules-extra"), which would +# outlive the package, so clear them and refresh the dependency lists. +find /lib/modules -path "*/weak-updates/*" -name "cas_*.ko*" -xtype l -delete 2>/dev/null || : +for kver in $(ls /lib/modules 2>/dev/null); do + [ -e "/lib/modules/$kver/modules.dep" ] || continue + depmod -a "$kver" 2>/dev/null || : +done +%endif %files @@ -175,14 +303,22 @@ fi %ghost /usr/lib/opencas/opencas.pyo %ghost /usr/lib/opencas/__pycache__ +%if %{without dkms} %files modules_%{kver_filename} %defattr(644, root, root, 755) %license LICENSE.md /lib/modules/%{kver} +%else +%files modules +%defattr(-, root, root, 755) +/usr/src/%{name}-modules-%{version}/ +%endif %changelog -* Tue Apr 28 2026 Qin Fandong - 26.06-1 +* Wed Aug 05 2026 秦凡东 - 26.09-1 +- Add an option to ship the kernel modules as DKMS sources (--with dkms) +* Tue Apr 28 2026 秦凡东 - 26.06-1 - Add opencas_exporter * Mon Aug 25 2025 Rafal Stefanowski - 25.03-1 * Thu Aug 7 2025 Brian J. Murrell - 25.03-1 diff --git a/tools/pckgen.sh b/tools/pckgen.sh index fcb1527e0..17159c17b 100755 --- a/tools/pckgen.sh +++ b/tools/pckgen.sh @@ -52,6 +52,7 @@ SUBMODULES=( # Unset all variables that may be checked for existence: unset ${!GENERATE_*} ARCHIVE_PREPARED DEBUG FAILED_DEPS KVER KVER_FULL LIST_KERNELS MOCK_CFG\ + WITH_DKMS\ OUTPUT_DIR RPM_BUILT SOURCES_DIR SUBMODULES_MISSING TAR_CREATED @@ -99,6 +100,12 @@ print_help() { echo " -o, --output-dir put all created files in the given directory;" echo " default: 'SOURCES_PATH/packages/'" echo " -d, --debug include debug information and create debug packages" + echo " --with-dkms ship the kernel modules as DKMS sources built on the" + echo " target at install time, instead of modules prebuilt" + echo " for one kernel (RPM only; DEB always uses DKMS);" + echo " the build host then needs no kernel-devel and" + echo " --kernel-version does not apply" + echo " --without-dkms explicitly build modules prebuilt for one kernel" echo " -c, --clean clean all temporary files and folders that" echo " may have been left around if $THIS ended" echo " unexpectedly in the previous run" @@ -442,12 +449,14 @@ rpm_spec_prepare() { echo "--- Debug info will be included and debug packages created as well" sed -i "s//%make_build DEBUG_PACKAGE=1/g" "$RPM_SPECS_DIR/$CAS_NAME.spec" + sed -i "s//make -C casadm DEBUG_PACKAGE=1/g" "$RPM_SPECS_DIR/$CAS_NAME.spec" sed -i "//d" "$RPM_SPECS_DIR/$CAS_NAME.spec" if is_suse; then sed -i "/%prep/i %debug_package\n\n" "$RPM_SPECS_DIR/$CAS_NAME.spec" fi else sed -i "s//%make_build/g" "$RPM_SPECS_DIR/$CAS_NAME.spec" + sed -i "s//make -C casadm/g" "$RPM_SPECS_DIR/$CAS_NAME.spec" sed -i "s//%define debug_package %{nil}/g" "$RPM_SPECS_DIR/$CAS_NAME.spec" fi @@ -530,11 +539,11 @@ generate_rpm() { if [ "$MOCK_CFG" ]; then echo "--- Building binary RPM packages in mock environment '$MOCK_CFG'" - if ! (HOME="$TEMP_DIR"; rpmbuild -bs "$RPM_SPECS_DIR/$CAS_NAME.spec"); then + if ! (HOME="$TEMP_DIR"; rpmbuild -bs ${RPM_BCOND_OPTS[@]} "$RPM_SPECS_DIR/$CAS_NAME.spec"); then error "couldn't create SRPM package for mock" fi - if ! mock -r $MOCK_CFG --no-clean --resultdir "$RPM_RPMS_DIR" "$RPM_SRPMS_DIR/$CAS_FILENAME"-*.src.rpm; then + if ! mock -r $MOCK_CFG --no-clean ${RPM_BCOND_OPTS[@]} --resultdir "$RPM_RPMS_DIR" "$RPM_SRPMS_DIR/$CAS_FILENAME"-*.src.rpm; then error "couldn't create RPM packages in mock environment" fi rm -rf "$RPM_SRPMS_DIR"/*.src.rpm @@ -547,15 +556,15 @@ generate_rpm() { else if [ ! "$GENERATE_SRPM" ] && [ "$GENERATE_RPM" ]; then echo "--- Building binary RPM packages" - (HOME="$TEMP_DIR"; rpmbuild -bb --target "$ARCH" "$RPM_SPECS_DIR/$CAS_NAME.spec") + (HOME="$TEMP_DIR"; rpmbuild -bb --target "$ARCH" ${RPM_BCOND_OPTS[@]} "$RPM_SPECS_DIR/$CAS_NAME.spec") if [ $? -ne 0 ]; then error "couldn't create RPM packages" fi - mv -ft "$OUTPUT_DIR" "$RPM_RPMS_DIR/$ARCH"/* + mv -ft "$OUTPUT_DIR" "$RPM_RPMS_DIR"/*/*.rpm fi if [ "$GENERATE_SRPM" ] && [ ! "$GENERATE_RPM" ]; then echo "--- Building source SRPM package" - (HOME="$TEMP_DIR"; rpmbuild -bs "$RPM_SPECS_DIR/$CAS_NAME.spec") + (HOME="$TEMP_DIR"; rpmbuild -bs ${RPM_BCOND_OPTS[@]} "$RPM_SPECS_DIR/$CAS_NAME.spec") if [ $? -ne 0 ]; then error "couldn't create SRPM package" fi @@ -563,12 +572,12 @@ generate_rpm() { fi if [ "$GENERATE_SRPM" ] && [ "$GENERATE_RPM" ]; then echo "--- Building source and binary RPM packages" - (HOME="$TEMP_DIR"; rpmbuild -ba --target "$ARCH" "$RPM_SPECS_DIR/$CAS_NAME.spec") + (HOME="$TEMP_DIR"; rpmbuild -ba --target "$ARCH" ${RPM_BCOND_OPTS[@]} "$RPM_SPECS_DIR/$CAS_NAME.spec") if [ $? -ne 0 ]; then error "couldn't create RPM packages" fi mv -ft "$OUTPUT_DIR" "$RPM_SRPMS_DIR"/* - mv -ft "$OUTPUT_DIR" "$RPM_RPMS_DIR/$ARCH"/* + mv -ft "$OUTPUT_DIR" "$RPM_RPMS_DIR"/*/*.rpm fi fi @@ -675,6 +684,12 @@ while (( $# )); do --debug|-d) DEBUG="debug" ;; + --with-dkms) + WITH_DKMS="with" + ;; + --without-dkms) + WITH_DKMS="without" + ;; --clean|-c) clean_all exit 0 @@ -721,6 +736,16 @@ VERSION_FILE="$SOURCES_DIR/.metadata/cas_version" # CAS version generator location: CAS_VERSION_GEN="$SOURCES_DIR/tools/cas_version_gen.sh" +# RPM build conditional selecting the DKMS packaging mode (see %bcond_with +# dkms in the spec). --with-dkms/--without-dkms map onto rpmbuild's +# --with/--without; passing neither leaves the spec default (off). +RPM_BCOND_OPTS=() +if [ "$WITH_DKMS" == "with" ]; then + RPM_BCOND_OPTS+=(--with dkms) +elif [ "$WITH_DKMS" == "without" ]; then + RPM_BCOND_OPTS+=(--without dkms) +fi + check_cas_version # CAS naming convention: @@ -750,8 +775,9 @@ echo -e "\n" check_dependencies [ "$MOCK_CFG" ] && mock_prepare [ "$LIST_KERNELS" ] && list_kernels -# Only RPM packages are built for specific kernel version. -[ "$GENERATE_RPM" ] && check_kernel_version +# Only RPM packages with prebuilt modules are built for a specific kernel +# version; with --with-dkms they are built on the target instead. +[ "$GENERATE_RPM" ] && [ "$WITH_DKMS" != "with" ] && check_kernel_version create_dir "$OUTPUT_DIR" for package in ${!GENERATE_*}; do ${package,,} From a1a483e2b94bfb7679d39742cd98580a2dea13fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=87=A1=E4=B8=9C?= Date: Wed, 23 Sep 2026 21:55:46 +0200 Subject: [PATCH 3/4] rpm: Make the two module packages mutually exclusive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both module packages ship modules for the same kernel, so having them installed together means two sets of modules on disk and whichever of them the module index happens to resolve first being the one in use. Declare them in conflict, to refuse installing both simultaneously, and leave switching to be asked for ('dnf --allowerasing', 'dnf swap'). rpm applies it whichever of the two is being installed, so it only has to be declared on the side whose counterpart can be named: the prebuilt package's own name carries the kernel version and cannot be written down. Switching is still meant to work, so two things have to survive the erasure it implies: - the tools require the modules by a name-and-version that both module packages provide, rather than by package, so the requirement stays satisfied within the transaction and erasing the outgoing package does not cascade into the tools - DKMS registers and builds from %posttrans rather than %post. The outgoing package is erased between the two, and dkms refuses to overwrite a module already installed at the same version, so from %post the modules being replaced are still in the way - and the erase that follows would then take away the ones dkms had declined to replace, leaving the kernel with none Co-authored-by: GLM-5.2 Signed-off-by: 秦凡东 Signed-off-by: Robert Baldyga --- tools/pckgen.d/rpm/CAS_NAME.spec | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tools/pckgen.d/rpm/CAS_NAME.spec b/tools/pckgen.d/rpm/CAS_NAME.spec index 32baf6920..c6e868f92 100644 --- a/tools/pckgen.d/rpm/CAS_NAME.spec +++ b/tools/pckgen.d/rpm/CAS_NAME.spec @@ -61,10 +61,12 @@ BuildRequires: = %{kver_pkg} BuildRequires: = %{kver_pkg} BuildRequires: BuildRequires: -Requires: -modules-%{version} -%else -Requires: %{name}-modules = %{version}-%{release} %endif +# Required by name-and-version rather than as a package, because either module +# package can satisfy it. Switching between them then keeps the requirement +# satisfied inside the one transaction, instead of the erasure of the outgoing +# one taking these tools with it. +Requires: -modules-%{version} Requires: python3 Requires: python3-PyYAML Requires: sed @@ -83,6 +85,13 @@ Summary: Open Cache Acceleration Software kernel modules Group: System Requires: kmod Provides: -modules-%{version} +# The two ways of shipping the modules are alternatives, not one superseding +# the other, so they are mutually exclusive rather than obsoleting: installing +# either over the other has to be an explicit choice (dnf --allowerasing / dnf +# swap) and not something the solver does on its own during an upgrade. rpm +# applies this in both directions, so naming the DKMS package here is enough - +# the prebuilt package cannot be named, its name carries the kernel version. +Conflicts: -modules %description modules_%{kver_filename} Open Cache Acceleration Software (Open CAS) is an open source project encompassing block caching software libraries, adapters, tools and more. @@ -95,6 +104,7 @@ Summary: Open Cache Acceleration Software kernel modules (DKMS source) Group: System BuildArch: noarch Requires: dkms +Provides: -modules-%{version} %description modules Open Cache Acceleration Software (Open CAS) is an open source project encompassing block caching software libraries, adapters, tools and more. @@ -244,10 +254,15 @@ if [ $1 -eq 0 ]; then depmod fi %else -%post modules -# Register the DKMS tree. --rpm_safe_upgrade keeps the add+remove pair safe -# across RPM upgrades (dkms(8); also on %preun remove). || : — dkms add -# returns 3 on re-add (reinstall), not a real failure. +%posttrans modules +# In %posttrans rather than %post: when this replaces the prebuilt package in +# one transaction, that package is erased between the two, and dkms refuses to +# overwrite a module already installed at the same version - so in %post its +# modules would still be in the way, and the erase would then take away the +# ones dkms declined to replace. +# --rpm_safe_upgrade keeps the add+remove pair safe across RPM upgrades +# (dkms(8); also on %preun remove). || : — dkms add returns 3 on re-add +# (reinstall), not a real failure. dkms add -m %{name}-modules -v %{version} --rpm_safe_upgrade || : # Build for the running kernel. No || : — fail visibly if kernel-devel is # missing (dkms returns 0 for "already installed", so reinstalls still work). From 2a9a0590f0f364cd91e3feb1d5e2817fc82b2550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=87=A1=E4=B8=9C?= Date: Tue, 22 Sep 2026 19:21:59 +0200 Subject: [PATCH 4/4] rpm: Migrate an existing prebuilt install to DKMS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Installing the DKMS package over an existing prebuilt one leaves the old modules package behind. It is named after the kernel it was built for (open-cas-linux-modules_k), so the DKMS package cannot Obsoletes it by name, and it is not replaced by the transaction either: it simply stays installed, still owning modules in /lib/modules. It cannot be removed from inside that transaction either, since rpm holds the database lock. Remove it from a detached worker started in %posttrans, which waits for the package manager to exit and then takes the orphan out with --nodeps (nothing depends on it) and --noscripts (its %preun operates on .ko files dkms has since archived, and fails). Its weak-updates symlinks are left dangling by --noscripts, so clear those too. The whole thing is guarded on such a package actually being installed, so a fresh install and a dkms-to-dkms upgrade do nothing. DKMS archives the modules it finds in place as original_module and restores them when the package is removed, which would put the old kernel's modules back as orphans after the package that owned them is gone. Drop the archive in %preun before 'dkms remove'. Two things the worker has to be careful about. It must not give up the prebuilt modules unless DKMS has modules of its own installed - a build that produced nothing would otherwise take away the ones the system is running on - so it checks that for itself, on top of the build's own failure exiting the scriptlet before the worker is started. And --noscripts means the old %postun never runs, so nothing rebuilds the module index once its files are gone: it still lists them, which breaks modprobe and makes dracut fail in kernel-modules-extra. Run depmod for the installed kernels afterwards. Only needed for packages that predate this change; from here on the two module packages conflict, so they cannot be installed over each other by accident. Co-authored-by: GLM-5.2 Signed-off-by: 秦凡东 Signed-off-by: Robert Baldyga --- tools/pckgen.d/rpm/CAS_NAME.spec | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tools/pckgen.d/rpm/CAS_NAME.spec b/tools/pckgen.d/rpm/CAS_NAME.spec index c6e868f92..3c570545b 100644 --- a/tools/pckgen.d/rpm/CAS_NAME.spec +++ b/tools/pckgen.d/rpm/CAS_NAME.spec @@ -274,7 +274,38 @@ for kver in $(ls /lib/modules 2>/dev/null | grep -vxF "$(uname -r)"); do dkms install -m %{name}-modules -v %{version} -k "$kver" || : done +# kmod->dkms upgrade orphans the old open-cas-linux-modules_k +# (name varies per kernel, can't Obsoletes). Can't rpm -e inside this +# transaction (rpmdb lock), so a detached worker (setsid) waits for the PM +# (dnf/zypper/yum) to exit, then rpm -e --nodeps --noscripts the orphans +# (--noscripts: old %preun fails on archived .ko; --nodeps: orphan has no +# deps) + deletes stale weak-updates symlinks. Guarded (no-op on fresh). +# Giving up the prebuilt modules is only safe once DKMS has modules of its own +# in place. A failed build above exits this scriptlet before the worker is even +# started, and the worker checks the state again for itself - between the two, +# a build that did not produce anything cannot end up taking the modules the +# system is currently using with it. +if [ -n "$(rpm -qa "open-cas-linux-modules_k*" 2>/dev/null)" ]; then + ( setsid sh -c ' + while pgrep -x dnf >/dev/null 2>&1 || pgrep -x zypper >/dev/null 2>&1 || pgrep -x yum >/dev/null 2>&1; do sleep 3; done + dkms status -m %{name}-modules -v %{version} -k "$(uname -r)" 2>/dev/null | grep -q ": installed" || exit 0 + rpm -e --nodeps --noscripts $(rpm -qa "open-cas-linux-modules_k*") >/dev/null 2>&1 || : + # --noscripts skipped the old %postun (weak-modules --remove-modules), + # so clean up after it: drop its dangling weak-updates/block/opencas + # symlinks and rebuild the module index, which still lists the files + # just removed and would otherwise break modprobe and dracut. + find /lib/modules -type l -path "*/weak-updates/block/opencas/*" -delete 2>/dev/null || : + for kver in $(ls /lib/modules 2>/dev/null); do + [ -e "/lib/modules/$kver/modules.dep" ] || continue + depmod -a "$kver" 2>/dev/null || : + done + ' /dev/null 2>&1 ) & +fi + %preun modules +# Drop dkms's archived old-kmod .ko (original_module) before `dkms remove` +# so it doesn't restore them as orphans (the old kmod package is gone). +rm -rf /var/lib/dkms/%{name}-modules/original_module 2>/dev/null || : dkms remove -m %{name}-modules -v %{version} --all --rpm_safe_upgrade || : # dkms weak-links the built modules into kABI-compatible kernels it did not # build for (/lib/modules//weak-updates/), and `dkms remove` drops the @@ -286,6 +317,7 @@ for kver in $(ls /lib/modules 2>/dev/null); do [ -e "/lib/modules/$kver/modules.dep" ] || continue depmod -a "$kver" 2>/dev/null || : done + %endif