From 572b9666419f25807f92765fb4dfb60a73335086 Mon Sep 17 00:00:00 2001 From: Kevin Parsons Date: Tue, 18 Apr 2023 10:56:00 -0700 Subject: [PATCH] Add lcow-partitioned-layer mount type Adds support for a new type of LCOW mount that can use individual disk partitions for each read-only layer. This change adds the work to parse the new layer type and pass it through the shim, as well as the support to the shim-side SCSI package to send the partition index in the guest request. This change does not add the GCS-side work to actually mount the specified partition. That will come in a future change. This change also does not handle formatting the scratch disk. It it desired to be able to format it on the fly when creating the container, but that will also come in a future change. Signed-off-by: Kevin Parsons --- cmd/containerd-shim-runhcs-v1/rootfs.go | 25 ++++++++++++++++++++ internal/layers/layers.go | 24 +++++++++++-------- internal/protocol/guestresource/resources.go | 1 + internal/uvm/scsi/backend.go | 2 ++ internal/uvm/scsi/manager.go | 1 + internal/uvm/scsi/mount.go | 1 + 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/cmd/containerd-shim-runhcs-v1/rootfs.go b/cmd/containerd-shim-runhcs-v1/rootfs.go index 4828355312..7c199881cc 100644 --- a/cmd/containerd-shim-runhcs-v1/rootfs.go +++ b/cmd/containerd-shim-runhcs-v1/rootfs.go @@ -100,6 +100,31 @@ func getLCOWLayers(rootfs []*types.Mount, layerFolders []string) (*layers.LCOWLa return nil, err } return legacyLayer(scratchLayer, parentLayers), nil + case "lcow-partitioned-layer": + var ( + scratchPath string + layerData []struct { + Path string + Partition uint64 + } + ) + for _, opt := range m.Options { + if optPrefix := "scratch="; strings.HasPrefix(opt, optPrefix) { + scratchPath = strings.TrimPrefix(opt, optPrefix) + } else if optPrefix := "parent-partitioned-layers="; strings.HasPrefix(opt, optPrefix) { + layerJSON := strings.TrimPrefix(opt, optPrefix) + if err := json.Unmarshal([]byte(layerJSON), &layerData); err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf("unrecognized %s mount option: %s", m.Type, opt) + } + } + roLayers := make([]*layers.LCOWLayer, 0, len(layerData)) + for _, layer := range layerData { + roLayers = append(roLayers, &layers.LCOWLayer{VHDPath: layer.Path, Partition: layer.Partition}) + } + return &layers.LCOWLayers{Layers: roLayers, ScratchVHDPath: scratchPath}, nil default: return nil, fmt.Errorf("unrecognized rootfs mount type: %s", m.Type) } diff --git a/internal/layers/layers.go b/internal/layers/layers.go index 4eb4831b5b..8eeee5f27b 100644 --- a/internal/layers/layers.go +++ b/internal/layers/layers.go @@ -27,7 +27,8 @@ import ( ) type LCOWLayer struct { - VHDPath string + VHDPath string + Partition uint64 } // Defines a set of LCOW layers. @@ -108,7 +109,7 @@ func MountLCOWLayers(ctx context.Context, containerID string, layers *LCOWLayers for _, layer := range layers.Layers { log.G(ctx).WithField("layerPath", layer.VHDPath).Debug("mounting layer") - uvmPath, closer, err := addLCOWLayer(ctx, vm, layer.VHDPath) + uvmPath, closer, err := addLCOWLayer(ctx, vm, layer) if err != nil { return "", "", nil, fmt.Errorf("failed to add LCOW layer: %s", err) } @@ -389,15 +390,17 @@ func mountWCOWIsolatedLayers(ctx context.Context, containerID string, layerFolde return containerScratchPathInUVM, closer, nil } -func addLCOWLayer(ctx context.Context, vm *uvm.UtilityVM, layerPath string) (uvmPath string, _ resources.ResourceCloser, err error) { - // don't try to add as vpmem when we want additional devices on the uvm to be fully physically backed - if !vm.DevicesPhysicallyBacked() { +func addLCOWLayer(ctx context.Context, vm *uvm.UtilityVM, layer *LCOWLayer) (uvmPath string, _ resources.ResourceCloser, err error) { + // Don't add as VPMEM when we want additional devices on the UVM to be fully physically backed. + // Also don't use VPMEM when we need to mount a specific partition of the disk, as this is only + // supported for SCSI. + if !vm.DevicesPhysicallyBacked() && layer.Partition == 0 { // We first try vPMEM and if it is full or the file is too large we // fall back to SCSI. - mount, err := vm.AddVPMem(ctx, layerPath) + mount, err := vm.AddVPMem(ctx, layer.VHDPath) if err == nil { log.G(ctx).WithFields(logrus.Fields{ - "layerPath": layerPath, + "layerPath": layer.VHDPath, "layerType": "vpmem", }).Debug("Added LCOW layer") return mount.GuestPath, mount, nil @@ -406,13 +409,14 @@ func addLCOWLayer(ctx context.Context, vm *uvm.UtilityVM, layerPath string) (uvm } } - sm, err := vm.SCSIManager.AddVirtualDisk(ctx, layerPath, true, "", &scsi.MountConfig{Options: []string{"ro"}}) + sm, err := vm.SCSIManager.AddVirtualDisk(ctx, layer.VHDPath, true, "", &scsi.MountConfig{Partition: layer.Partition, Options: []string{"ro"}}) if err != nil { return "", nil, fmt.Errorf("failed to add SCSI layer: %s", err) } log.G(ctx).WithFields(logrus.Fields{ - "layerPath": layerPath, - "layerType": "scsi", + "layerPath": layer.VHDPath, + "layerPartition": layer.Partition, + "layerType": "scsi", }).Debug("Added LCOW layer") return sm.GuestPath(), sm, nil } diff --git a/internal/protocol/guestresource/resources.go b/internal/protocol/guestresource/resources.go index 579c9d4687..07c4319696 100644 --- a/internal/protocol/guestresource/resources.go +++ b/internal/protocol/guestresource/resources.go @@ -81,6 +81,7 @@ type LCOWMappedVirtualDisk struct { MountPath string `json:"MountPath,omitempty"` Lun uint8 `json:"Lun,omitempty"` Controller uint8 `json:"Controller,omitempty"` + Partition uint64 `json:"Partition,omitempty"` ReadOnly bool `json:"ReadOnly,omitempty"` Encrypted bool `json:"Encrypted,omitempty"` Options []string `json:"Options,omitempty"` diff --git a/internal/uvm/scsi/backend.go b/internal/uvm/scsi/backend.go index 3f387b1ee1..5f756862c6 100644 --- a/internal/uvm/scsi/backend.go +++ b/internal/uvm/scsi/backend.go @@ -200,6 +200,7 @@ func mountRequest(controller, lun uint, path string, config *mountConfig, osType MountPath: path, Controller: uint8(controller), Lun: uint8(lun), + Partition: config.partition, ReadOnly: config.readOnly, Encrypted: config.encrypted, Options: config.options, @@ -226,6 +227,7 @@ func unmountRequest(controller, lun uint, path string, config *mountConfig, osTy req.Settings = guestresource.LCOWMappedVirtualDisk{ MountPath: path, Lun: uint8(lun), + Partition: config.partition, Controller: uint8(controller), VerityInfo: config.verity, } diff --git a/internal/uvm/scsi/manager.go b/internal/uvm/scsi/manager.go index ef371a7e31..9dbf2aa6fe 100644 --- a/internal/uvm/scsi/manager.go +++ b/internal/uvm/scsi/manager.go @@ -64,6 +64,7 @@ func NewManager( // MountConfig specifies the options to apply for mounting a SCSI device in // the guest OS. type MountConfig struct { + Partition uint64 Encrypted bool Options []string } diff --git a/internal/uvm/scsi/mount.go b/internal/uvm/scsi/mount.go index d539b37062..d35b78fee4 100644 --- a/internal/uvm/scsi/mount.go +++ b/internal/uvm/scsi/mount.go @@ -38,6 +38,7 @@ type mount struct { } type mountConfig struct { + partition uint64 readOnly bool encrypted bool verity *guestresource.DeviceVerityInfo