treewide: stop using LZ4 for initrd compression
There are two issues at play here: One is a bug in pierrec/lz4 when
using the legacy framing format [1]. This bit us when we hit a broken
size region with CL:2130, taking hours to debug.
The other is the fact that the Linux LZ4 frame format has significant
design issues [2], especially with concatenanted initrds.
The first issue could be fixed by switching to a different LZ4
implementation (we do even have the reference impl in the monorepo) but
there is no API to generate the legacy frame format and things like [3],
a patch carried by Ubuntu to fix more edge cases just do not inspire
confidence in such a solution.
Thus, this CL switches over to using zstd for compressing initrds.
Zstd is slower than LZ4 for decompressing, but it still decompresses at
multiple GB/s per core while having a much better compression ratio.
It also doesn't have any Linux-specific bits and Linux uses the
reference implementation for decoding, which should make it much more
robust. So overall I think this is a good tradeoff.
[1] https://github.com/pierrec/lz4/issues/156
[2] https://github.com/lz4/lz4/issues/956#issuecomment-736705712
[3] https://launchpadlibrarian.net/507407918/0001-unlz4-Handle-0-size-chunks-discard-trailing-padding-.patch
Change-Id: I69cf69f2f361de325f4b39f2d3644ee729643716
Reviewed-on: https://review.monogon.dev/c/monogon/+/2313
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/metropolis/node/build/def.bzl b/metropolis/node/build/def.bzl
index d71279c..4f0e30d 100644
--- a/metropolis/node/build/def.bzl
+++ b/metropolis/node/build/def.bzl
@@ -133,7 +133,7 @@
return
def _node_initramfs_impl(ctx):
- initramfs_name = ctx.label.name + ".cpio.lz4"
+ initramfs_name = ctx.label.name + ".cpio.zst"
initramfs = ctx.actions.declare_file(initramfs_name)
_fsspec_core_impl(ctx, ctx.executable._mkcpio, initramfs)
diff --git a/metropolis/node/build/mkcpio/BUILD.bazel b/metropolis/node/build/mkcpio/BUILD.bazel
index f0bb779..3ea98ae 100644
--- a/metropolis/node/build/mkcpio/BUILD.bazel
+++ b/metropolis/node/build/mkcpio/BUILD.bazel
@@ -8,7 +8,7 @@
deps = [
"//metropolis/node/build/fsspec",
"@com_github_cavaliergopher_cpio//:cpio",
- "@com_github_pierrec_lz4_v4//:lz4",
+ "@com_github_klauspost_compress//zstd",
"@org_golang_x_sys//unix",
],
)
diff --git a/metropolis/node/build/mkcpio/main.go b/metropolis/node/build/mkcpio/main.go
index 1c9b39f..b8f99b9 100644
--- a/metropolis/node/build/mkcpio/main.go
+++ b/metropolis/node/build/mkcpio/main.go
@@ -10,7 +10,7 @@
"strings"
"github.com/cavaliergopher/cpio"
- "github.com/pierrec/lz4/v4"
+ "github.com/klauspost/compress/zstd"
"golang.org/x/sys/unix"
"source.monogon.dev/metropolis/node/build/fsspec"
@@ -47,7 +47,7 @@
Inode interface{}
}
-// Usage: -out <out-path.cpio.lz4> fsspec-path...
+// Usage: -out <out-path.cpio.zst> fsspec-path...
func main() {
flag.Parse()
outFile, err := os.Create(*outPath)
@@ -55,8 +55,10 @@
log.Fatalf("Failed to open CPIO output file: %v", err)
}
defer outFile.Close()
- compressedOut := lz4.NewWriter(outFile)
- compressedOut.Apply(lz4.LegacyOption(true))
+ compressedOut, err := zstd.NewWriter(outFile)
+ if err != nil {
+ log.Fatalf("While initializing zstd writer: %v", err)
+ }
defer compressedOut.Close()
cpioWriter := cpio.NewWriter(compressedOut)
defer cpioWriter.Close()
@@ -168,7 +170,7 @@
}); err != nil {
log.Fatalf("Failed to write cpio header for file %q: %v", i.Path, err)
}
- if _, err := io.Copy(cpioWriter, inF); err != nil {
+ if n, err := io.Copy(cpioWriter, inF); err != nil || n != inFStat.Size() {
log.Fatalf("Failed to copy file %q into cpio: %v", i.SourcePath, err)
}
inF.Close()
diff --git a/metropolis/test/launch/cluster/cluster.go b/metropolis/test/launch/cluster/cluster.go
index a312e86..1066d4d 100644
--- a/metropolis/test/launch/cluster/cluster.go
+++ b/metropolis/test/launch/cluster/cluster.go
@@ -790,7 +790,7 @@
if err := launch.RunMicroVM(ctxT, &launch.MicroVMOptions{
Name: "nanoswitch",
KernelPath: "metropolis/test/ktest/vmlinux",
- InitramfsPath: "metropolis/test/nanoswitch/initramfs.cpio.lz4",
+ InitramfsPath: "metropolis/test/nanoswitch/initramfs.cpio.zst",
ExtraNetworkInterfaces: switchPorts,
PortMap: portMap,
GuestServiceMap: guestSvcMap,