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