1
2
3 package uvm
4
5 import (
6 "context"
7 "errors"
8 "fmt"
9 "os"
10 "path/filepath"
11 "runtime"
12
13 "github.com/sirupsen/logrus"
14 "go.opencensus.io/trace"
15 "golang.org/x/sys/windows"
16
17 "github.com/Microsoft/hcsshim/internal/cow"
18 "github.com/Microsoft/hcsshim/internal/hcs"
19 hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
20 "github.com/Microsoft/hcsshim/internal/log"
21 "github.com/Microsoft/hcsshim/internal/logfields"
22 "github.com/Microsoft/hcsshim/internal/oc"
23 "github.com/Microsoft/hcsshim/internal/schemaversion"
24 "github.com/Microsoft/hcsshim/osversion"
25 )
26
27
28 type Options struct {
29 ID string
30 Owner string
31
32
33
34 MemorySizeInMB uint64
35
36 LowMMIOGapInMB uint64
37 HighMMIOBaseInMB uint64
38 HighMMIOGapInMB uint64
39
40
41
42 AllowOvercommit bool
43
44
45
46 FullyPhysicallyBacked bool
47
48
49
50 EnableDeferredCommit bool
51
52
53
54 ProcessorCount int32
55
56
57
58 ProcessorLimit int32
59
60
61
62 ProcessorWeight int32
63
64
65
66 StorageQoSIopsMaximum int32
67
68
69
70 StorageQoSBandwidthMaximum int32
71
72
73
74
75
76
77
78
79
80
81 DisableCompartmentNamespace bool
82
83
84
85 CPUGroupID string
86
87
88
89
90 NetworkConfigProxy string
91
92
93
94
95 ProcessDumpLocation string
96
97
98 NoWritableFileShares bool
99
100
101 SCSIControllerCount uint32
102
103
104 DumpDirectoryPath string
105 }
106
107
108 func verifyOptions(ctx context.Context, options interface{}) error {
109 switch opts := options.(type) {
110 case *OptionsLCOW:
111 if opts.EnableDeferredCommit && !opts.AllowOvercommit {
112 return errors.New("EnableDeferredCommit is not supported on physically backed VMs")
113 }
114 if opts.SCSIControllerCount > MaxSCSIControllers {
115 return fmt.Errorf("SCSI controller count can't be more than %d", MaxSCSIControllers)
116 }
117 if opts.VPMemDeviceCount > MaxVPMEMCount {
118 return fmt.Errorf("VPMem device count cannot be greater than %d", MaxVPMEMCount)
119 }
120 if opts.VPMemDeviceCount > 0 {
121 if opts.VPMemSizeBytes%4096 != 0 {
122 return errors.New("VPMemSizeBytes must be a multiple of 4096")
123 }
124 }
125 if opts.KernelDirect && osversion.Build() < 18286 {
126 return errors.New("KernelDirectBoot is not supported on builds older than 18286")
127 }
128
129 if opts.EnableColdDiscardHint && osversion.Build() < 18967 {
130 return errors.New("EnableColdDiscardHint is not supported on builds older than 18967")
131 }
132 case *OptionsWCOW:
133 if opts.EnableDeferredCommit && !opts.AllowOvercommit {
134 return errors.New("EnableDeferredCommit is not supported on physically backed VMs")
135 }
136 if len(opts.LayerFolders) < 2 {
137 return errors.New("at least 2 LayerFolders must be supplied")
138 }
139 if opts.SCSIControllerCount != 1 {
140 return errors.New("exactly 1 SCSI controller is required for WCOW")
141 }
142 }
143 return nil
144 }
145
146
147
148
149
150
151 func newDefaultOptions(id, owner string) *Options {
152 opts := &Options{
153 ID: id,
154 Owner: owner,
155 MemorySizeInMB: 1024,
156 AllowOvercommit: true,
157 EnableDeferredCommit: false,
158 ProcessorCount: defaultProcessorCount(),
159 FullyPhysicallyBacked: false,
160 NoWritableFileShares: false,
161 SCSIControllerCount: 1,
162 }
163
164 if opts.Owner == "" {
165 opts.Owner = filepath.Base(os.Args[0])
166 }
167
168 return opts
169 }
170
171
172 func (uvm *UtilityVM) ID() string {
173 return uvm.hcsSystem.ID()
174 }
175
176
177 func (uvm *UtilityVM) OS() string {
178 return uvm.operatingSystem
179 }
180
181 func (uvm *UtilityVM) create(ctx context.Context, doc interface{}) error {
182 uvm.exitCh = make(chan struct{})
183 system, err := hcs.CreateComputeSystem(ctx, uvm.id, doc)
184 if err != nil {
185 return err
186 }
187 defer func() {
188 if system != nil {
189 _ = system.Terminate(ctx)
190 _ = system.Wait()
191 }
192 }()
193
194
195 properties, err := system.Properties(ctx)
196 if err != nil {
197 return err
198 }
199 uvm.runtimeID = properties.RuntimeID
200 uvm.hcsSystem = system
201 system = nil
202
203 log.G(ctx).WithFields(logrus.Fields{
204 logfields.UVMID: uvm.id,
205 "runtime-id": uvm.runtimeID.String(),
206 }).Debug("created utility VM")
207 return nil
208 }
209
210
211 func (uvm *UtilityVM) Close() (err error) {
212 ctx, span := oc.StartSpan(context.Background(), "uvm::Close")
213 defer span.End()
214 defer func() { oc.SetSpanStatus(span, err) }()
215 span.AddAttributes(trace.StringAttribute(logfields.UVMID, uvm.id))
216
217 windows.Close(uvm.vmmemProcess)
218
219 if uvm.hcsSystem != nil {
220 _ = uvm.hcsSystem.Terminate(ctx)
221 _ = uvm.Wait()
222 }
223
224 if err := uvm.CloseGCSConnection(); err != nil {
225 log.G(ctx).Errorf("close GCS connection failed: %s", err)
226 }
227
228
229
230
231 if uvm.outputListener != nil {
232 close(uvm.outputProcessingDone)
233 uvm.outputListener.Close()
234 uvm.outputListener = nil
235 }
236
237 if uvm.confidentialUVMOptions != nil && uvm.confidentialUVMOptions.GuestStateFile != "" {
238 vmgsFullPath := filepath.Join(uvm.confidentialUVMOptions.BundleDirectory, uvm.confidentialUVMOptions.GuestStateFile)
239 log.G(context.Background()).WithField("VMGS file", vmgsFullPath).Debug("removing VMGS file")
240 if err := os.Remove(vmgsFullPath); err != nil {
241 log.G(ctx).WithError(err).Error("failed to remove VMGS file")
242 }
243 }
244
245 if uvm.hcsSystem != nil {
246 return uvm.hcsSystem.Close()
247 }
248
249 return nil
250 }
251
252
253 func (uvm *UtilityVM) CreateContainer(ctx context.Context, id string, settings interface{}) (cow.Container, error) {
254 if uvm.gc != nil {
255 c, err := uvm.gc.CreateContainer(ctx, id, settings)
256 if err != nil {
257 return nil, fmt.Errorf("failed to create container %s: %s", id, err)
258 }
259 return c, nil
260 }
261 doc := hcsschema.ComputeSystem{
262 HostingSystemId: uvm.id,
263 Owner: uvm.owner,
264 SchemaVersion: schemaversion.SchemaV21(),
265 ShouldTerminateOnLastHandleClosed: true,
266 HostedSystem: settings,
267 }
268 c, err := hcs.CreateComputeSystem(ctx, id, &doc)
269 if err != nil {
270 return nil, err
271 }
272 return c, err
273 }
274
275
276 func (uvm *UtilityVM) CreateProcess(ctx context.Context, settings interface{}) (cow.Process, error) {
277 if uvm.gc != nil {
278 return uvm.gc.CreateProcess(ctx, settings)
279 }
280 return uvm.hcsSystem.CreateProcess(ctx, settings)
281 }
282
283
284
285 func (uvm *UtilityVM) IsOCI() bool {
286 return false
287 }
288
289
290 func (uvm *UtilityVM) Terminate(ctx context.Context) error {
291 return uvm.hcsSystem.Terminate(ctx)
292 }
293
294
295 func (uvm *UtilityVM) ExitError() error {
296 return uvm.hcsSystem.ExitError()
297 }
298
299 func defaultProcessorCount() int32 {
300 if runtime.NumCPU() == 1 {
301 return 1
302 }
303 return 2
304 }
305
306
307
308 func (uvm *UtilityVM) normalizeProcessorCount(ctx context.Context, requested int32, processorTopology *hcsschema.ProcessorTopology) int32 {
309
310
311
312
313
314
315
316
317 hostCount := int32(processorTopology.LogicalProcessorCount)
318 if requested > hostCount {
319 log.G(ctx).WithFields(logrus.Fields{
320 logfields.UVMID: uvm.id,
321 "requested": requested,
322 "assigned": hostCount,
323 }).Warn("Changing user requested CPUCount to current number of processors")
324 return hostCount
325 } else {
326 return requested
327 }
328 }
329
330
331 func (uvm *UtilityVM) ProcessorCount() int32 {
332 return uvm.processorCount
333 }
334
335
336
337 func (uvm *UtilityVM) PhysicallyBacked() bool {
338 return uvm.physicallyBacked
339 }
340
341
342
343 func (uvm *UtilityVM) ProcessDumpLocation() string {
344 return uvm.processDumpLocation
345 }
346
347 func (uvm *UtilityVM) normalizeMemorySize(ctx context.Context, requested uint64) uint64 {
348 actual := (requested + 1) &^ 1
349 if requested != actual {
350 log.G(ctx).WithFields(logrus.Fields{
351 logfields.UVMID: uvm.id,
352 "requested": requested,
353 "assigned": actual,
354 }).Warn("Changing user requested MemorySizeInMB to align to 2MB")
355 }
356 return actual
357 }
358
359
360
361 func (uvm *UtilityVM) DevicesPhysicallyBacked() bool {
362 return uvm.devicesPhysicallyBacked
363 }
364
365
366 func (uvm *UtilityVM) VSMBNoDirectMap() bool {
367 return uvm.vsmbNoDirectMap
368 }
369
370 func (uvm *UtilityVM) NoWritableFileShares() bool {
371 return uvm.noWritableFileShares
372 }
373
374
375
376 func (uvm *UtilityVM) CloseGCSConnection() (err error) {
377 if uvm.gc != nil {
378 err = uvm.gc.Close()
379 }
380 if uvm.gcListener != nil {
381 err = uvm.gcListener.Close()
382 }
383 return
384 }
385
View as plain text