From dc2773ba267ee82ec5128f3c49d57912483ae0d0 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Mon, 12 Jan 2026 15:31:46 +0100 Subject: [PATCH] [linstor] Update piraeus-server patches with critical fixes Update piraeus-server patches to address critical production issues: - Add fix-duplicate-tcp-ports.diff to prevent duplicate TCP ports after toggle-disk operations (upstream PR #476) - Update skip-adjust-when-device-inaccessible.diff with comprehensive fixes for resources stuck in StandAlone after reboot, Unknown state race condition, and encrypted LUKS resource deletion (upstream PR #477) ```release-note [linstor] Fix DRBD resources stuck in StandAlone state after reboot and encrypted resource deletion issues ``` Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- .../images/piraeus-server/patches/README.md | 6 +- .../patches/fix-duplicate-tcp-ports.diff | 87 ++++++++ .../skip-adjust-when-device-inaccessible.diff | 192 +++++++++++++++--- 3 files changed, 258 insertions(+), 27 deletions(-) create mode 100644 packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff diff --git a/packages/system/linstor/images/piraeus-server/patches/README.md b/packages/system/linstor/images/piraeus-server/patches/README.md index c219eaf3..5ee29318 100644 --- a/packages/system/linstor/images/piraeus-server/patches/README.md +++ b/packages/system/linstor/images/piraeus-server/patches/README.md @@ -8,5 +8,7 @@ Custom patches for piraeus-server (linstor-server) v1.32.3. - Upstream: [#475](https://github.com/LINBIT/linstor-server/pull/475) - **force-metadata-check-on-disk-add.diff** — Create metadata during toggle-disk from diskless to diskful - Upstream: [#474](https://github.com/LINBIT/linstor-server/pull/474) -- **skip-adjust-when-device-inaccessible.diff** — Skip DRBD adjust/res file regeneration when child layer device is inaccessible - - Upstream: [#471](https://github.com/LINBIT/linstor-server/pull/471) +- **fix-duplicate-tcp-ports.diff** — Prevent duplicate TCP ports after toggle-disk operations + - Upstream: [#476](https://github.com/LINBIT/linstor-server/pull/476) +- **skip-adjust-when-device-inaccessible.diff** — Fix resources stuck in StandAlone after reboot, Unknown state race condition, and encrypted resource deletion + - Upstream: [#477](https://github.com/LINBIT/linstor-server/pull/477) diff --git a/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff b/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff new file mode 100644 index 00000000..07cd9eac --- /dev/null +++ b/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff @@ -0,0 +1,87 @@ +From 1250abe99d64a0501795e37d3b6af62410002239 Mon Sep 17 00:00:00 2001 +From: Andrei Kvapil +Date: Mon, 12 Jan 2026 13:44:46 +0100 +Subject: [PATCH] fix(drbd): prevent duplicate TCP ports after toggle-disk + operations + +Remove redundant ensureStackDataExists() call with empty payload from +resetStoragePools() method that was causing TCP port conflicts after +toggle-disk operations. + +Root Cause: +----------- +The resetStoragePools() method, introduced in 2019 (commit 95cc17d0b8), +calls ensureStackDataExists() with an empty LayerPayload. This worked +correctly when TCP ports were stored at RscDfn level. + +After the TCP port migration to per-node level (commit f754943463, May +2025), this empty payload results in DrbdRscData being created without +TCP ports assigned. The controller then sends a Pojo with an empty port +Set to satellites. + +On satellites, when DrbdRscData is initialized with an empty port list, +initPorts() uses preferredNewPortsRef from peer resources. Since +SatelliteDynamicNumberPool.tryAllocate() always returns true (no-op), +any port from preferredNewPortsRef is accepted without conflict checking, +leading to duplicate TCP port assignments. + +Impact: +------- +This regression affects toggle-disk operations, particularly: +- Snapshot creation/restore operations +- Manual toggle-disk operations +- Any operation calling resetStoragePools() + +Symptoms include: +- DRBD resources failing to adjust with "port is also used" errors +- Resources stuck in StandAlone or Connecting states +- Multiple resources on the same node using identical TCP ports + +Solution: +--------- +Remove the ensureStackDataExists() call from resetStoragePools() as it +is redundant. The calling code (e.g., CtrlRscToggleDiskApiCallHandler +line 1071) already invokes ensureStackDataExists() with the correct +payload immediately after resetStoragePools(). + +This fix ensures: +1. resetStoragePools() only resets storage pool assignments +2. Layer data creation with proper TCP ports happens via the caller's + ensureStackDataExists() with correct payload +3. No DrbdRscData objects are created without TCP port assignments + +Related Issues: +--------------- +Fixes #454 - Duplicate TCP ports after backup/restore operations +Related to user reports of resources stuck in StandAlone after node +reboots when toggle-disk or backup operations were in progress. + +Testing: +-------- +Verified that: +- Toggle-disk operations no longer create resources without TCP ports +- Backup/restore operations complete without TCP port conflicts +- Resources maintain unique TCP ports across toggle-disk cycles + +Co-Authored-By: Claude +Signed-off-by: Andrei Kvapil +--- + .../linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java +index 3538b380c..4f589145e 100644 +--- a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java ++++ b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java +@@ -276,8 +276,6 @@ public class CtrlRscLayerDataFactory + + rscDataToProcess.addAll(rscData.getChildren()); + } +- +- ensureStackDataExists(rscRef, null, new LayerPayload()); + } + catch (AccessDeniedException exc) + { +-- +2.39.5 (Apple Git-154) + diff --git a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff b/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff index 18399c41..65c8be7c 100644 --- a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff +++ b/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff @@ -1,30 +1,27 @@ -diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -index abc123def..def456abc 100644 ---- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -+++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -@@ -83,6 +83,8 @@ import java.util.TreeMap; - import java.util.TreeSet; - import java.util.concurrent.atomic.AtomicBoolean; - import java.util.function.Function; -+import java.nio.file.Files; -+import java.nio.file.Paths; +From 6a556821b9a0996d34389a27b941694ce810a44c Mon Sep 17 00:00:00 2001 +From: Andrei Kvapil +Date: Mon, 12 Jan 2026 14:26:57 +0100 +Subject: [PATCH 1/3] Fix: Skip DRBD adjust/res file regeneration when child + layer device is inaccessible + +When deleting encrypted (LUKS) resources, the LUKS layer may close its device +before the DRBD layer attempts to adjust the resource or regenerate the res +file. This causes 'Failed to adjust DRBD resource' errors. + +This fix adds checks before regenerateResFile() and drbdUtils.adjust() +to verify that child layer devices are accessible. If a child device doesn't +exist or is not accessible (e.g., LUKS device is closed during resource +deletion), these operations are skipped and the adjustRequired flag is cleared, +allowing resource deletion to proceed successfully. + +Co-Authored-By: Claude +Signed-off-by: Andrei Kvapil +--- + .../linbit/linstor/layer/drbd/DrbdLayer.java | 72 ++++++++++++++++--- + 1 file changed, 61 insertions(+), 11 deletions(-) - @Singleton - public class DeviceHandlerImpl implements DeviceHandler -@@ -1646,7 +1648,10 @@ public class DeviceHandlerImpl implements DeviceHandler - private void updateDiscGran(VlmProviderObject vlmData) throws DatabaseException, StorageException - { - String devicePath = vlmData.getDevicePath(); -- if (devicePath != null && vlmData.exists()) -+ // Check if device path physically exists before calling lsblk -+ // This is important for DRBD devices which might be temporarily unavailable during adjust -+ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet) -+ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath))) - { - if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE) - { diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -index 01967a3..871d830 100644 +index 01967a31f..871d830d1 100644 --- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java +++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java @@ -592,7 +592,29 @@ public class DrbdLayer implements DeviceLayer @@ -116,3 +113,148 @@ index 01967a3..871d830 100644 } } +-- +2.39.5 (Apple Git-154) + + +From afe51ea674c4a350c27d1f2cacfecf6fe42b8a7a Mon Sep 17 00:00:00 2001 +From: Andrei Kvapil +Date: Mon, 12 Jan 2026 14:27:52 +0100 +Subject: [PATCH 2/3] fix(satellite): skip lsblk when device path doesn't + physically exist + +Add physical device path existence check before calling lsblk in updateDiscGran(). +This prevents race condition when drbdadm adjust temporarily brings devices down/up +and the kernel hasn't created the device node yet. + +Issue: After satellite restart with patched code, some DRBD resources ended up in +Unknown state because: +1. drbdadm adjust successfully completes (brings devices up) +2. updateDiscGran() immediately tries to check discard granularity +3. /dev/drbd* device node doesn't exist yet (kernel hasn't created it) +4. lsblk fails with exit code 32 "not a block device" +5. StorageException interrupts DeviceManager cycle +6. DRBD device remains in incomplete state + +Solution: Check Files.exists(devicePath) before calling lsblk. If device doesn't +exist yet, skip the check - it will be retried in the next DeviceManager cycle +when the device node is available. + +Co-Authored-By: Claude +Signed-off-by: Andrei Kvapil +--- + .../linbit/linstor/core/devmgr/DeviceHandlerImpl.java | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java +index 49138a8fd..1c13cfc9d 100644 +--- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java ++++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java +@@ -68,6 +68,8 @@ import javax.inject.Inject; + import javax.inject.Provider; + import javax.inject.Singleton; + ++import java.nio.file.Files; ++import java.nio.file.Paths; + import java.util.ArrayList; + import java.util.Collection; + import java.util.Collections; +@@ -1645,8 +1647,11 @@ public class DeviceHandlerImpl implements DeviceHandler + + private void updateDiscGran(VlmProviderObject vlmData) throws DatabaseException, StorageException + { +- String devicePath = vlmData.getDevicePath(); +- if (devicePath != null && vlmData.exists()) ++ @Nullable String devicePath = vlmData.getDevicePath(); ++ // Check if device path physically exists before calling lsblk ++ // This is important for DRBD devices which might be temporarily unavailable during adjust ++ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet) ++ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath))) + { + if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE) + { +-- +2.39.5 (Apple Git-154) + + +From de1f22e7c008c5479f85a3b1ebdf8461944210f4 Mon Sep 17 00:00:00 2001 +From: Andrei Kvapil +Date: Mon, 12 Jan 2026 14:28:23 +0100 +Subject: [PATCH 3/3] fix(drbd): only check child devices when disk access is + actually needed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The previous implementation blocked `drbdadm adjust` whenever child +device paths were unavailable, even for operations that don't require +disk access (like network reconnect from StandAlone to Connected). + +After node reboot, DRBD resources often remain in StandAlone state +because: +1. updateResourceToCurrentDrbdState() correctly detects StandAlone + and sets adjustRequired=true +2. However, canAdjust check fails because child volumes may have + devicePath=null (due to INACTIVE flag, cloning state, or + initialization race) +3. adjust is skipped → adjustRequired=false → resources stay StandAlone + +Root cause: The canAdjust check was added to protect LUKS deletion +scenarios but was applied to ALL cases, including network reconnect +which doesn't need disk access. + +Fix: Check child device accessibility only when disk access is actually +required (volume creation, resize, metadata operations). Network +reconnect operations (StandAlone → Connected) now proceed without +checking child devices. + +This ensures automatic DRBD reconnection after reboot while preserving +protection for LUKS deletion scenarios. + +Co-Authored-By: Claude +Signed-off-by: Andrei Kvapil +--- + .../linbit/linstor/layer/drbd/DrbdLayer.java | 27 ++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java +index 871d830d1..78b8195a4 100644 +--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java ++++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java +@@ -792,7 +792,32 @@ public class DrbdLayer implements DeviceLayer + // This is important for encrypted resources (LUKS) where the device + // might be closed during deletion + boolean canAdjust = true; +- if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) ++ ++ // IMPORTANT: Check child volumes only when disk access is actually needed. ++ // For network reconnect (StandAlone -> Connected), disk access is not required. ++ boolean needsDiskAccess = false; ++ ++ // Check if there are pending operations that require disk access ++ for (DrbdVlmData drbdVlmData : drbdRscData.getVlmLayerObjects().values()) ++ { ++ Volume vlm = (Volume) drbdVlmData.getVolume(); ++ StateFlags vlmFlags = vlm.getFlags(); ++ ++ // Disk access is needed if: ++ // - creating a new volume ++ // - resizing ++ // - checking/creating metadata ++ if (!drbdVlmData.exists() || ++ drbdVlmData.checkMetaData() || ++ vlmFlags.isSomeSet(workerCtx, Volume.Flags.RESIZE, Volume.Flags.DRBD_RESIZE)) ++ { ++ needsDiskAccess = true; ++ break; ++ } ++ } ++ ++ // Check child volumes only if disk access is actually needed ++ if (needsDiskAccess && !skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) + { + AbsRscLayerObject dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA); + if (dataChild != null) +-- +2.39.5 (Apple Git-154) +