1
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
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
36
37 type VPCIDevice struct {
38
39 vm *UtilityVM
40
41 VMBusGUID string
42
43 deviceInstanceID string
44
45 virtualFunctionIndex uint16
46
47 refCount uint32
48 }
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 func (uvm *UtilityVM) GetAssignedDeviceVMBUSInstanceID(vmBusChannelGUID string) string {
64 return fmt.Sprintf("%s\\%s\\{%s}", assignedDeviceEnumerator, vmbusChannelTypeGUIDFormatted, vmBusChannelGUID)
65 }
66
67
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
76
77
78
79
80
81
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
121
122 if uvm.operatingSystem != "windows" {
123
124
125
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
150
151
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