From dd61ad86105c07c1ff8a101a0542af61699f0df3 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 4 Dec 2024 22:24:32 +0400 Subject: [PATCH] fix: lock provisioning order of user disk partitions Fixes #9877 As a side-effect, fix alignment of user disks for newer QEMU versions. Signed-off-by: Andrey Smirnov --- cmd/talosctl/cmd/mgmt/cluster/create.go | 6 ++++-- .../machined/pkg/controllers/block/user_disk_config.go | 6 +++++- .../pkg/controllers/block/user_disk_config_test.go | 1 + pkg/machinery/resources/block/volume_config.go | 5 +++-- pkg/provision/providers/vm/disk.go | 9 ++++++--- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/cmd/talosctl/cmd/mgmt/cluster/create.go b/cmd/talosctl/cmd/mgmt/cluster/create.go index 9f86ed08b5..be237b4679 100644 --- a/cmd/talosctl/cmd/mgmt/cluster/create.go +++ b/cmd/talosctl/cmd/mgmt/cluster/create.go @@ -1162,6 +1162,8 @@ func parseCPUShare(cpus string) (int64, error) { } func getDisks() ([]*provision.Disk, error) { + const GPTAlignment = 2 * 1024 * 1024 // 2 MB + // should have at least a single primary disk disks := []*provision.Disk{ { @@ -1211,8 +1213,8 @@ func getDisks() ([]*provision.Disk, error) { } disks = append(disks, &provision.Disk{ - // add 1 MB to make extra room for GPT and alignment - Size: diskSize + 2*1024*1024, + // add 2 MB per partition to make extra room for GPT and alignment + Size: diskSize + GPTAlignment*uint64(len(diskPartitions)+1), Partitions: diskPartitions, SkipPreallocate: !clusterDiskPreallocate, Driver: "ide", diff --git a/internal/app/machined/pkg/controllers/block/user_disk_config.go b/internal/app/machined/pkg/controllers/block/user_disk_config.go index 3c3ac9e039..17401a19c5 100644 --- a/internal/app/machined/pkg/controllers/block/user_disk_config.go +++ b/internal/app/machined/pkg/controllers/block/user_disk_config.go @@ -105,7 +105,11 @@ func (ctrl *UserDiskConfigController) Run(ctx context.Context, r controller.Runt vc.TypedSpec().Type = block.VolumeTypePartition vc.TypedSpec().Provisioning = block.ProvisioningSpec{ - Wave: block.WaveUserDisks, + // it's crucial to keep the order of provisioning locked within each disk, otherwise + // provisioning might order them different way, and create partitions in wrong order + // the matcher on partition idx would then discover partitions in wrong order, and mount them + // in wrong order + Wave: block.WaveLegacyUserDisks + idx, DiskSelector: block.DiskSelector{ Match: diskPathMatch(resolvedDevicePath), }, diff --git a/internal/app/machined/pkg/controllers/block/user_disk_config_test.go b/internal/app/machined/pkg/controllers/block/user_disk_config_test.go index e460bbe766..e4e556d633 100644 --- a/internal/app/machined/pkg/controllers/block/user_disk_config_test.go +++ b/internal/app/machined/pkg/controllers/block/user_disk_config_test.go @@ -132,6 +132,7 @@ func (suite *UserDiskConfigSuite) TestReconcileUserDisk() { ctest.AssertResource(suite, id, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.NotEmpty(r.TypedSpec().Provisioning) asrt.Contains(r.Metadata().Labels().Raw(), block.UserDiskLabel) + asrt.GreaterOrEqual(r.TypedSpec().Provisioning.Wave, block.WaveLegacyUserDisks) }) } diff --git a/pkg/machinery/resources/block/volume_config.go b/pkg/machinery/resources/block/volume_config.go index a068f3a358..6ca21c51d3 100644 --- a/pkg/machinery/resources/block/volume_config.go +++ b/pkg/machinery/resources/block/volume_config.go @@ -45,8 +45,9 @@ type VolumeConfigSpec struct { // Wave constants. const ( - WaveSystemDisk = -1 - WaveUserDisks = 0 + WaveSystemDisk = -1 + WaveUserDisks = 0 + WaveLegacyUserDisks = 1000000 // legacy user disks rely on specific order of provisioning ) // ProvisioningSpec is the spec for volume provisioning. diff --git a/pkg/provision/providers/vm/disk.go b/pkg/provision/providers/vm/disk.go index d19ebf3128..4c95bd7ef4 100644 --- a/pkg/provision/providers/vm/disk.go +++ b/pkg/provision/providers/vm/disk.go @@ -24,10 +24,13 @@ func (p *Provisioner) UserDiskName(index int) string { // CreateDisks creates empty disk files for each disk. func (p *Provisioner) CreateDisks(state *State, nodeReq provision.NodeRequest) (diskPaths []string, err error) { + const QEMUAlignment = 4 * 1024 * 1024 // 4 MiB, required by QEMU + diskPaths = make([]string, len(nodeReq.Disks)) for i, disk := range nodeReq.Disks { diskPath := state.GetRelativePath(fmt.Sprintf("%s-%d.disk", nodeReq.Name, i)) + diskSize := (disk.Size + QEMUAlignment - 1) / QEMUAlignment * QEMUAlignment var diskF *os.File @@ -38,13 +41,13 @@ func (p *Provisioner) CreateDisks(state *State, nodeReq provision.NodeRequest) ( defer diskF.Close() //nolint:errcheck - if err = diskF.Truncate(int64(disk.Size)); err != nil { + if err = diskF.Truncate(int64(diskSize)); err != nil { return nil, err } if !disk.SkipPreallocate { - if err = syscall.Fallocate(int(diskF.Fd()), 0, 0, int64(disk.Size)); err != nil { - fmt.Fprintf(os.Stderr, "WARNING: failed to preallocate disk space for %q (size %d): %s", diskPath, disk.Size, err) + if err = syscall.Fallocate(int(diskF.Fd()), 0, 0, int64(diskSize)); err != nil { + fmt.Fprintf(os.Stderr, "WARNING: failed to preallocate disk space for %q (size %d): %s", diskPath, diskSize, err) } }