From fd6bae62b2ea9a353230637ac2b717c473d7de7d Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Fri, 3 Apr 2026 21:50:52 +0200 Subject: [PATCH] linstor: add stale bitmap adjust retry patch Signed-off-by: Andrei Kvapil --- .../images/piraeus-server/patches/README.md | 3 + .../retry-adjust-after-stale-bitmap.diff | 141 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 packages/system/linstor/images/piraeus-server/patches/retry-adjust-after-stale-bitmap.diff diff --git a/packages/system/linstor/images/piraeus-server/patches/README.md b/packages/system/linstor/images/piraeus-server/patches/README.md index db497b7e6..47e5f6359 100644 --- a/packages/system/linstor/images/piraeus-server/patches/README.md +++ b/packages/system/linstor/images/piraeus-server/patches/README.md @@ -12,3 +12,6 @@ Custom patches for piraeus-server (linstor-server) v1.33.1. - Source PR/comment: [#472](https://github.com/LINBIT/linstor-server/pull/472), [maintainer note](https://github.com/LINBIT/linstor-server/pull/472#issuecomment-3949687603) - Backported from commits: [`ccc85fbd2`](https://github.com/LINBIT/linstor-server/commit/ccc85fbd2c65f0b97c52403fa80f1efdb886ec4e), [`71b601554`](https://github.com/LINBIT/linstor-server/commit/71b601554f41bcb50cd5bd06989c5b0d3a814acd) - Note: upstream commit [`3d0402a0c`](https://github.com/LINBIT/linstor-server/commit/3d0402a0c25f0a4b57b380321f10e89982f26e7a) is already included in `v1.33.1` +- **retry-adjust-after-stale-bitmap.diff** — Retry `drbdadm adjust` after detaching a stale local bitmap state + - Source PR: [#491](https://github.com/LINBIT/linstor-server/pull/491) + - Backported from commit: [`51ae50a84`](https://github.com/kvaps/linstor-server/commit/51ae50a84dcb98093f543b819652c750a94d96c9) diff --git a/packages/system/linstor/images/piraeus-server/patches/retry-adjust-after-stale-bitmap.diff b/packages/system/linstor/images/piraeus-server/patches/retry-adjust-after-stale-bitmap.diff new file mode 100644 index 000000000..009591674 --- /dev/null +++ b/packages/system/linstor/images/piraeus-server/patches/retry-adjust-after-stale-bitmap.diff @@ -0,0 +1,141 @@ +diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java +index 5627d1be8..ece191292 100644 +--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java ++++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java +@@ -42,6 +42,9 @@ import java.util.Arrays; + import java.util.List; + import java.util.concurrent.ArrayBlockingQueue; + import java.util.concurrent.TimeUnit; ++import java.nio.charset.StandardCharsets; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; + import java.util.stream.Collectors; + + @Singleton +@@ -56,6 +58,9 @@ public class DrbdAdm + + public static final int WAIT_CONNECT_RES_TIME = 10; + private static final long DOWN_WAIT_TIMEOUT_SEC = 5; ++ private static final long FORCE_DETACH_RETRY_WAIT_MS = 250; ++ private static final String BITMAP_LEAK_ERR_MSG = "already has a bitmap, this should not happen"; ++ private static final Pattern BITMAP_LEAK_MINOR_PATTERN = Pattern.compile("\\bminor\\s+(\\d+)\\b"); + + private final ExtCmdFactory extCmdFactory; + private final AccessContext sysCtx; +@@ -131,8 +136,38 @@ public class DrbdAdm + // command.add(resName); + command.add(drbdRscData.getSuffixedResourceName()); + // execute(Arrays.asList("drbdsetup", "show", drbdRscData.getSuffixedResourceName())); +- execute(command); +- // execute(Arrays.asList("drbdsetup", "show", drbdRscData.getSuffixedResourceName())); ++ String[] commandArr = command.toArray(new String[0]); ++ try ++ { ++ File nullDevice = new File(Platform.nullDevice()); ++ ExtCmd extCmd = extCmdFactory.create(); ++ if (Platform.isWindows()) ++ { ++ extCmd.setTimeout(TimeoutType.WAIT, 5 * 60 * 1000); ++ } ++ ++ OutputData outputData = extCmd.pipeExec(ProcessBuilder.Redirect.from(nullDevice), commandArr); ++ if ( ++ outputData.exitCode != 0 && ++ isBitmapLeakOnAttach(outputData) && ++ cleanupStaleBitmapAndRetry(extCmd, nullDevice, outputData) ++ ) ++ { ++ outputData = extCmd.pipeExec(ProcessBuilder.Redirect.from(nullDevice), commandArr); ++ } ++ if (outputData.exitCode != 0) ++ { ++ throw new ExtCmdFailedException(commandArr, outputData); ++ } ++ } ++ catch (ChildProcessTimeoutException timeoutExc) ++ { ++ throw new ExtCmdFailedException(commandArr, timeoutExc); ++ } ++ catch (IOException ioExc) ++ { ++ throw new ExtCmdFailedException(commandArr, ioExc); ++ } + + drbdRscData.setAdjustRequired(false); + } +@@ -805,6 +840,75 @@ public class DrbdAdm + } + } + ++ static boolean isBitmapLeakOnAttach(OutputData outputData) ++ { ++ return extractBitmapLeakMinor(outputData) != null; ++ } ++ ++ static @Nullable Integer extractBitmapLeakMinor(OutputData outputData) ++ { ++ String stderr = new String(outputData.stderrData, StandardCharsets.UTF_8); ++ if (!stderr.contains(BITMAP_LEAK_ERR_MSG)) ++ { ++ return null; ++ } ++ ++ Matcher matcher = BITMAP_LEAK_MINOR_PATTERN.matcher(stderr); ++ if (!matcher.find()) ++ { ++ return null; ++ } ++ ++ return Integer.parseInt(matcher.group(1)); ++ } ++ ++ private boolean cleanupStaleBitmapAndRetry( ++ ExtCmd extCmd, ++ File nullDevice, ++ OutputData outputData ++ ) ++ throws IOException, ChildProcessTimeoutException, ExtCmdFailedException ++ { ++ @Nullable Integer minor = extractBitmapLeakMinor(outputData); ++ if (minor == null) ++ { ++ return false; ++ } ++ ++ OutputData detachOut = extCmd.pipeExec( ++ ProcessBuilder.Redirect.from(nullDevice), ++ DRBDSETUP_UTIL, ++ "detach", ++ Integer.toString(minor) ++ ); ++ if (detachOut.exitCode == 0) ++ { ++ return true; ++ } ++ ++ OutputData forceDetachOut = extCmd.pipeExec( ++ ProcessBuilder.Redirect.from(nullDevice), ++ DRBDSETUP_UTIL, ++ "detach", ++ Integer.toString(minor), ++ "--force" ++ ); ++ if (forceDetachOut.exitCode != 0) ++ { ++ throw new ExtCmdFailedException(forceDetachOut.executedCommand, forceDetachOut); ++ } ++ ++ try ++ { ++ Thread.sleep(FORCE_DETACH_RETRY_WAIT_MS); ++ } ++ catch (InterruptedException ignored) ++ { ++ Thread.currentThread().interrupt(); ++ } ++ return true; ++ } ++ + public static class DrbdPrimary implements AutoCloseable + { + private final DrbdAdm drbdAdm;