...

Source file src/github.com/Microsoft/hcsshim/internal/uvm/virtual_device.go

Documentation: github.com/Microsoft/hcsshim/internal/uvm

     1  //go:build windows
     2  
     3  package uvm
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  
     9  	"github.com/Microsoft/go-winio/pkg/guid"
    10  
    11  	"github.com/Microsoft/hcsshim/internal/hcs/resourcepaths"
    12  	hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
    13  	"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
    14  	"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
    15  )
    16  
    17  const (
    18  	GPUDeviceIDType         = "gpu"
    19  	VPCILocationPathIDType  = "vpci-location-path"
    20  	VPCIClassGUIDTypeLegacy = "class"
    21  	VPCIClassGUIDType       = "vpci-class-guid"
    22  	VPCIDeviceIDTypeLegacy  = "vpci"
    23  	VPCIDeviceIDType        = "vpci-instance-id"
    24  )
    25  
    26  // this is the well known channel type GUID defined by VMBUS for all assigned devices
    27  const vmbusChannelTypeGUIDFormatted = "{44c4f61d-4444-4400-9d52-802e27ede19f}"
    28  const assignedDeviceEnumerator = "VMBUS"
    29  
    30  type VPCIDeviceKey struct {
    31  	deviceInstanceID     string
    32  	virtualFunctionIndex uint16
    33  }
    34  
    35  // VPCIDevice represents a vpci device. Holds its guid and a handle to the uvm it
    36  // belongs to.
    37  type VPCIDevice struct {
    38  	// vm is the handle to the UVM that this device belongs to
    39  	vm *UtilityVM
    40  	// VMBusGUID is the instance ID for this device when it is exposed via VMBus
    41  	VMBusGUID string
    42  	// deviceInstanceID is the instance ID of the device on the host
    43  	deviceInstanceID string
    44  	// virtualFunctionIndex is the function index for the pci device to assign
    45  	virtualFunctionIndex uint16
    46  	// refCount stores the number of references to this device in the UVM
    47  	refCount uint32
    48  }
    49  
    50  // GetAssignedDeviceVMBUSInstanceID returns the instance ID of the VMBUS channel device node created.
    51  //
    52  // When a device is assigned to a UVM via VPCI support in HCS, a new VMBUS channel device node is
    53  // created in the UVM. The actual device that was assigned in is exposed as a child on this VMBUS
    54  // channel device node.
    55  //
    56  // A device node's instance ID is an identifier that distinguishes that device from other devices
    57  // on the system. The GUID of a VMBUS channel device node refers to that channel's unique
    58  // identifier used internally by VMBUS and can be used to determine the VMBUS channel
    59  // device node's instance ID.
    60  //
    61  // A VMBUS channel device node's instance ID is in the form:
    62  // "VMBUS\vmbusChannelTypeGUIDFormatted\{vmBusChannelGUID}"
    63  func (uvm *UtilityVM) GetAssignedDeviceVMBUSInstanceID(vmBusChannelGUID string) string {
    64  	return fmt.Sprintf("%s\\%s\\{%s}", assignedDeviceEnumerator, vmbusChannelTypeGUIDFormatted, vmBusChannelGUID)
    65  }
    66  
    67  // Release frees the resources of the corresponding vpci device
    68  func (vpci *VPCIDevice) Release(ctx context.Context) error {
    69  	if err := vpci.vm.RemoveDevice(ctx, vpci.deviceInstanceID, vpci.virtualFunctionIndex); err != nil {
    70  		return fmt.Errorf("failed to remove VPCI device: %s", err)
    71  	}
    72  	return nil
    73  }
    74  
    75  // AssignDevice assigns a vpci device to the uvm
    76  // if the device already exists, the stored VPCIDevice's ref count is increased
    77  // and the VPCIDevice is returned.
    78  // Otherwise, a new request is made to assign the target device indicated by the deviceID
    79  // onto the UVM. A new VPCIDevice entry is made on the UVM and the VPCIDevice is returned
    80  // to the caller.
    81  // Allow callers to specify the vmbus guid they want the device to show up with.
    82  func (uvm *UtilityVM) AssignDevice(ctx context.Context, deviceID string, index uint16, vmBusGUID string) (*VPCIDevice, error) {
    83  	if vmBusGUID == "" {
    84  		guid, err := guid.NewV4()
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  		vmBusGUID = guid.String()
    89  	}
    90  
    91  	key := VPCIDeviceKey{
    92  		deviceInstanceID:     deviceID,
    93  		virtualFunctionIndex: index,
    94  	}
    95  
    96  	uvm.m.Lock()
    97  	defer uvm.m.Unlock()
    98  
    99  	existingVPCIDevice := uvm.vpciDevices[key]
   100  	if existingVPCIDevice != nil {
   101  		existingVPCIDevice.refCount++
   102  		return existingVPCIDevice, nil
   103  	}
   104  
   105  	targetDevice := hcsschema.VirtualPciDevice{
   106  		Functions: []hcsschema.VirtualPciFunction{
   107  			{
   108  				DeviceInstancePath: deviceID,
   109  				VirtualFunction:    index,
   110  			},
   111  		},
   112  	}
   113  
   114  	request := &hcsschema.ModifySettingRequest{
   115  		ResourcePath: fmt.Sprintf(resourcepaths.VirtualPCIResourceFormat, vmBusGUID),
   116  		RequestType:  guestrequest.RequestTypeAdd,
   117  		Settings:     targetDevice,
   118  	}
   119  
   120  	// WCOW (when supported) does not require a guest request as part of the
   121  	// device assignment
   122  	if uvm.operatingSystem != "windows" {
   123  		// for LCOW, we need to make sure that specific paths relating to the
   124  		// device exist so they are ready to be used by later
   125  		// work in openGCS
   126  		request.GuestRequest = guestrequest.ModificationRequest{
   127  			ResourceType: guestresource.ResourceTypeVPCIDevice,
   128  			RequestType:  guestrequest.RequestTypeAdd,
   129  			Settings: guestresource.LCOWMappedVPCIDevice{
   130  				VMBusGUID: vmBusGUID,
   131  			},
   132  		}
   133  	}
   134  
   135  	if err := uvm.modify(ctx, request); err != nil {
   136  		return nil, err
   137  	}
   138  	result := &VPCIDevice{
   139  		vm:                   uvm,
   140  		VMBusGUID:            vmBusGUID,
   141  		deviceInstanceID:     deviceID,
   142  		virtualFunctionIndex: index,
   143  		refCount:             1,
   144  	}
   145  	uvm.vpciDevices[key] = result
   146  	return result, nil
   147  }
   148  
   149  // RemoveDevice removes a vpci device from a uvm when there are
   150  // no more references to a given VPCIDevice. Otherwise, decrements
   151  // the reference count of the stored VPCIDevice and returns nil.
   152  func (uvm *UtilityVM) RemoveDevice(ctx context.Context, deviceInstanceID string, index uint16) error {
   153  	key := VPCIDeviceKey{
   154  		deviceInstanceID:     deviceInstanceID,
   155  		virtualFunctionIndex: index,
   156  	}
   157  
   158  	uvm.m.Lock()
   159  	defer uvm.m.Unlock()
   160  
   161  	vpci := uvm.vpciDevices[key]
   162  	if vpci == nil {
   163  		return fmt.Errorf("no device with ID %s and index %d is present on the uvm %s", deviceInstanceID, index, uvm.ID())
   164  	}
   165  
   166  	vpci.refCount--
   167  	if vpci.refCount == 0 {
   168  		delete(uvm.vpciDevices, key)
   169  		return uvm.modify(ctx, &hcsschema.ModifySettingRequest{
   170  			ResourcePath: fmt.Sprintf(resourcepaths.VirtualPCIResourceFormat, vpci.VMBusGUID),
   171  			RequestType:  guestrequest.RequestTypeRemove,
   172  		})
   173  	}
   174  	return nil
   175  }
   176  

View as plain text