...

Source file src/github.com/Microsoft/hcsshim/internal/guest/runtime/hcsv2/spec_devices.go

Documentation: github.com/Microsoft/hcsshim/internal/guest/runtime/hcsv2

     1  //go:build linux
     2  // +build linux
     3  
     4  package hcsv2
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/Microsoft/hcsshim/internal/guest/storage/pci"
    13  	"github.com/Microsoft/hcsshim/internal/log"
    14  	"github.com/opencontainers/runc/libcontainer/devices"
    15  	oci "github.com/opencontainers/runtime-spec/specs-go"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  const (
    20  	sysfsDevPathFormat = "/sys/dev/%s/%d:%d"
    21  
    22  	charType  = "char"
    23  	blockType = "block"
    24  
    25  	vpciDeviceIDTypeLegacy = "vpci"
    26  	vpciDeviceIDType       = "vpci-instance-id"
    27  )
    28  
    29  // addAssignedDevice goes through the assigned devices that have been enumerated
    30  // on the spec and updates the spec so that the correct device files can be mounted
    31  // into the resulting container by the runtime.
    32  func addAssignedDevice(ctx context.Context, spec *oci.Spec) error {
    33  	for _, d := range spec.Windows.Devices {
    34  		switch d.IDType {
    35  		case vpciDeviceIDTypeLegacy, vpciDeviceIDType:
    36  			// validate that the device is ready
    37  			fullPCIPath, err := pci.FindDeviceFullPath(ctx, d.ID)
    38  			if err != nil {
    39  				return errors.Wrapf(err, "failed to find device pci path for device %v", d)
    40  			}
    41  			// find the device node that links to the pci path we just got
    42  			dev, err := devicePathFromPCIPath(fullPCIPath)
    43  			if err != nil {
    44  				return errors.Wrapf(err, "failed to find dev node for device %v", d)
    45  			}
    46  			addLinuxDeviceToSpec(ctx, dev, spec, true)
    47  		}
    48  	}
    49  
    50  	return nil
    51  }
    52  
    53  // devicePathFromPCIPath takes a sysfs bus path to the pci device assigned into the guest
    54  // and attempts to find the dev node in the guest that maps to it.
    55  func devicePathFromPCIPath(pciPath string) (*devices.Device, error) {
    56  	// get the full pci path to make sure that it's the final path
    57  	pciFullPath, err := filepath.EvalSymlinks(pciPath)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	// get all host dev devices
    63  	hostDevices, err := devices.HostDevices()
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	// find corresponding entry in sysfs
    69  	for _, d := range hostDevices {
    70  		major := d.Rule.Major
    71  		minor := d.Rule.Minor
    72  
    73  		deviceTypeString := ""
    74  		switch d.Rule.Type {
    75  		case devices.BlockDevice:
    76  			deviceTypeString = blockType
    77  		case devices.CharDevice:
    78  			deviceTypeString = charType
    79  		default:
    80  			return nil, errors.New("unsupported device type")
    81  		}
    82  
    83  		syfsDevPath := fmt.Sprintf(sysfsDevPathFormat, deviceTypeString, major, minor)
    84  		sysfsFullPath, err := filepath.EvalSymlinks(syfsDevPath)
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  		if strings.HasPrefix(sysfsFullPath, pciFullPath) {
    89  			// return early once we find the device
    90  			return d, nil
    91  		}
    92  	}
    93  
    94  	return nil, errors.New("failed to find the device node from sysfs pci path")
    95  }
    96  
    97  func addLinuxDeviceToSpec(ctx context.Context, hostDevice *devices.Device, spec *oci.Spec, addCgroupDevice bool) {
    98  	rd := oci.LinuxDevice{
    99  		Path:  hostDevice.Path,
   100  		Type:  string(hostDevice.Type),
   101  		Major: hostDevice.Major,
   102  		Minor: hostDevice.Minor,
   103  		UID:   &hostDevice.Uid,
   104  		GID:   &hostDevice.Gid,
   105  	}
   106  	if hostDevice.Major == 0 && hostDevice.Minor == 0 {
   107  		// Invalid device, most likely a symbolic link, skip it.
   108  		return
   109  	}
   110  	found := false
   111  	for i, dev := range spec.Linux.Devices {
   112  		if dev.Path == rd.Path {
   113  			found = true
   114  			spec.Linux.Devices[i] = rd
   115  			break
   116  		}
   117  		if dev.Type == rd.Type && dev.Major == rd.Major && dev.Minor == rd.Minor {
   118  			log.G(ctx).Warnf("The same type '%s', major '%d' and minor '%d', should not be used for multiple devices.", dev.Type, dev.Major, dev.Minor)
   119  		}
   120  	}
   121  	if !found {
   122  		spec.Linux.Devices = append(spec.Linux.Devices, rd)
   123  		if addCgroupDevice {
   124  			deviceCgroup := oci.LinuxDeviceCgroup{
   125  				Allow:  true,
   126  				Type:   string(hostDevice.Type),
   127  				Major:  &hostDevice.Major,
   128  				Minor:  &hostDevice.Minor,
   129  				Access: string(hostDevice.Permissions),
   130  			}
   131  			spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, deviceCgroup)
   132  		}
   133  	}
   134  }
   135  

View as plain text