[linstor] Update piraeus-server patches with critical fixes (#1850)

## What this PR does

This PR updates piraeus-server patches to address several critical
production issues with DRBD resources and LUKS encryption:

1. **Add fix-duplicate-tcp-ports.diff** - Prevents duplicate TCP ports
after toggle-disk operations (upstream PR #476)

2. **Update skip-adjust-when-device-inaccessible.diff** - Comprehensive
fix for multiple issues:
   - Resources stuck in StandAlone state after node reboot
   - Unknown state race condition during satellite restart
   - Encrypted LUKS resource deletion failures
   - Network reconnect blocked by unavailable child device checks

These patches resolve scenarios where DRBD resources fail to
automatically reconnect after node reboots and improve LUKS resource
lifecycle management.

Upstream PRs:
- https://github.com/LINBIT/linstor-server/pull/476
- https://github.com/LINBIT/linstor-server/pull/477

### Release note

```release-note
[linstor] Fix DRBD resources stuck in StandAlone state after reboot and encrypted resource deletion issues
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
  * Prevents duplicate TCP port conflicts after disk toggle operations
  * Fixes resources stuck in StandAlone or Unknown state after reboot
  * Resolves issues with encrypted resource deletion
  * Improves handling of temporarily inaccessible storage devices

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Andrei Kvapil 2026-01-12 23:23:27 +01:00 committed by GitHub
commit ea6ec3e5eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 258 additions and 27 deletions

View file

@ -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)

View file

@ -0,0 +1,87 @@
From 1250abe99d64a0501795e37d3b6af62410002239 Mon Sep 17 00:00:00 2001
From: Andrei Kvapil <kvapss@gmail.com>
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 <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
---
.../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)

View file

@ -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 <kvapss@gmail.com>
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 <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
---
.../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<Resource> 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 <kvapss@gmail.com>
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 <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
---
.../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<Resource> 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 <kvapss@gmail.com>
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 <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
---
.../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<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
+ {
+ Volume vlm = (Volume) drbdVlmData.getVolume();
+ StateFlags<Volume.Flags> 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<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
if (dataChild != null)
--
2.39.5 (Apple Git-154)