1
2
3 package uvm
4
5 import (
6 "context"
7 "fmt"
8 "os"
9
10 "github.com/pkg/errors"
11 "github.com/sirupsen/logrus"
12
13 "github.com/Microsoft/hcsshim/internal/hcs/resourcepaths"
14 hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
15 "github.com/Microsoft/hcsshim/internal/log"
16 "github.com/Microsoft/hcsshim/internal/memory"
17 "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
18 "github.com/Microsoft/hcsshim/internal/protocol/guestresource"
19 )
20
21 const (
22 PageSize = 0x1000
23 MaxMappedDeviceCount = 1024
24 )
25
26 const lcowPackedVPMemLayerFmt = "/run/layers/p%d-%d-%d"
27
28 type mappedDeviceInfo struct {
29 vPMemInfoDefault
30 mappedRegion memory.MappedRegion
31 sizeInBytes uint64
32 }
33
34 type vPMemInfoMulti struct {
35 memory.PoolAllocator
36 maxSize uint64
37 maxMappedDeviceCount uint32
38 mappings map[string]*mappedDeviceInfo
39 }
40
41 func newVPMemMappedDevice(hostPath, uvmPath string, sizeBytes uint64, memReg memory.MappedRegion) *mappedDeviceInfo {
42 return &mappedDeviceInfo{
43 vPMemInfoDefault: vPMemInfoDefault{
44 hostPath: hostPath,
45 uvmPath: uvmPath,
46 refCount: 1,
47 },
48 mappedRegion: memReg,
49 sizeInBytes: sizeBytes,
50 }
51 }
52
53 func newPackedVPMemDevice() *vPMemInfoMulti {
54 return &vPMemInfoMulti{
55 PoolAllocator: memory.NewPoolMemoryAllocator(),
56 maxSize: DefaultVPMemSizeBytes,
57 mappings: make(map[string]*mappedDeviceInfo),
58 maxMappedDeviceCount: MaxMappedDeviceCount,
59 }
60 }
61
62 func pageAlign(t uint64) uint64 {
63 if t%PageSize == 0 {
64 return t
65 }
66 return (t/PageSize + 1) * PageSize
67 }
68
69
70
71 func newMappedVPMemModifyRequest(
72 ctx context.Context,
73 rType guestrequest.RequestType,
74 deviceNumber uint32,
75 md *mappedDeviceInfo,
76 uvm *UtilityVM,
77 ) (*hcsschema.ModifySettingRequest, error) {
78 guestSettings := guestresource.LCOWMappedVPMemDevice{
79 DeviceNumber: deviceNumber,
80 MountPath: md.uvmPath,
81 MappingInfo: &guestresource.LCOWVPMemMappingInfo{
82 DeviceOffsetInBytes: md.mappedRegion.Offset(),
83 DeviceSizeInBytes: md.sizeInBytes,
84 },
85 }
86
87 if verity, err := readVeritySuperBlock(ctx, md.hostPath); err != nil {
88 log.G(ctx).WithError(err).WithField("hostPath", md.hostPath).Debug("unable to read dm-verity information from VHD")
89 } else {
90 log.G(ctx).WithFields(logrus.Fields{
91 "hostPath": md.hostPath,
92 "rootDigest": verity.RootDigest,
93 }).Debug("adding multi-mapped VPMem with dm-verity")
94 guestSettings.VerityInfo = verity
95 }
96
97 request := &hcsschema.ModifySettingRequest{
98 RequestType: rType,
99 GuestRequest: guestrequest.ModificationRequest{
100 ResourceType: guestresource.ResourceTypeVPMemDevice,
101 RequestType: rType,
102 Settings: guestSettings,
103 },
104 }
105
106 pmem := uvm.vpmemDevicesMultiMapped[deviceNumber]
107 switch rType {
108 case guestrequest.RequestTypeAdd:
109 if pmem == nil {
110 request.Settings = hcsschema.VirtualPMemDevice{
111 ReadOnly: true,
112 HostPath: md.hostPath,
113 ImageFormat: "Vhd1",
114 }
115 request.ResourcePath = fmt.Sprintf(resourcepaths.VPMemControllerResourceFormat, deviceNumber)
116 } else {
117 request.Settings = hcsschema.VirtualPMemMapping{
118 HostPath: md.hostPath,
119 ImageFormat: "Vhd1",
120 }
121 request.ResourcePath = fmt.Sprintf(resourcepaths.VPMemDeviceResourceFormat, deviceNumber, md.mappedRegion.Offset())
122 }
123 case guestrequest.RequestTypeRemove:
124 if pmem == nil {
125 return nil, errors.Errorf("no device found at location %d", deviceNumber)
126 }
127 request.ResourcePath = fmt.Sprintf(resourcepaths.VPMemDeviceResourceFormat, deviceNumber, md.mappedRegion.Offset())
128 default:
129 return nil, errors.New("unsupported request type")
130 }
131
132 log.G(ctx).WithFields(logrus.Fields{
133 "deviceNumber": deviceNumber,
134 "hostPath": md.hostPath,
135 "uvmPath": md.uvmPath,
136 }).Debugf("new mapped VPMem modify request: %v", request)
137 return request, nil
138 }
139
140
141 func (pmem *vPMemInfoMulti) mapVHDLayer(ctx context.Context, device *mappedDeviceInfo) (err error) {
142 if md, ok := pmem.mappings[device.hostPath]; ok {
143 md.refCount++
144 return nil
145 }
146
147 log.G(ctx).WithFields(logrus.Fields{
148 "hostPath": device.hostPath,
149 "mountPath": device.uvmPath,
150 "deviceOffset": device.mappedRegion.Offset(),
151 "deviceSize": device.sizeInBytes,
152 }).Debug("mapped new device")
153
154 pmem.mappings[device.hostPath] = device
155 return nil
156 }
157
158
159 func (pmem *vPMemInfoMulti) unmapVHDLayer(ctx context.Context, hostPath string) (err error) {
160 dev, ok := pmem.mappings[hostPath]
161 if !ok {
162 return ErrNotAttached
163 }
164
165 if dev.refCount > 1 {
166 dev.refCount--
167 return nil
168 }
169
170 if err := pmem.Release(dev.mappedRegion); err != nil {
171 return err
172 }
173 log.G(ctx).WithFields(logrus.Fields{
174 "hostPath": dev.hostPath,
175 }).Debugf("Done releasing resources: %s", dev.hostPath)
176 delete(pmem.mappings, hostPath)
177 return nil
178 }
179
180
181 func (uvm *UtilityVM) findVPMemMappedDevice(ctx context.Context, hostPath string) (uint32, *mappedDeviceInfo, error) {
182 for i := uint32(0); i < uvm.vpmemMaxCount; i++ {
183 vi := uvm.vpmemDevicesMultiMapped[i]
184 if vi != nil {
185 if vhd, ok := vi.mappings[hostPath]; ok {
186 log.G(ctx).WithFields(logrus.Fields{
187 "deviceNumber": i,
188 "hostPath": hostPath,
189 "uvmPath": vhd.uvmPath,
190 "refCount": vhd.refCount,
191 "deviceSize": vhd.sizeInBytes,
192 "deviceOffset": vhd.mappedRegion.Offset(),
193 }).Debug("found mapped VHD")
194 return i, vhd, nil
195 }
196 }
197 }
198 return 0, nil, ErrNotAttached
199 }
200
201
202
203 func (uvm *UtilityVM) allocateNextVPMemMappedDeviceLocation(ctx context.Context, devSize uint64) (uint32, memory.MappedRegion, error) {
204
205 devSize = pageAlign(devSize)
206
207 for i := uint32(0); i < uvm.vpmemMaxCount; i++ {
208 pmem := uvm.vpmemDevicesMultiMapped[i]
209 if pmem == nil {
210 pmem = newPackedVPMemDevice()
211 uvm.vpmemDevicesMultiMapped[i] = pmem
212 }
213
214 if len(pmem.mappings) >= int(pmem.maxMappedDeviceCount) {
215 continue
216 }
217
218 reg, err := pmem.Allocate(devSize)
219 if err != nil {
220 continue
221 }
222 log.G(ctx).WithFields(logrus.Fields{
223 "deviceNumber": i,
224 "deviceOffset": reg.Offset(),
225 "deviceSize": devSize,
226 }).Debug("found offset for mapped VHD on an existing VPMem device")
227 return i, reg, nil
228 }
229 return 0, nil, ErrNoAvailableLocation
230 }
231
232
233
234
235
236 func (uvm *UtilityVM) addVPMemMappedDevice(ctx context.Context, hostPath string) (_ string, err error) {
237 if _, dev, err := uvm.findVPMemMappedDevice(ctx, hostPath); err == nil {
238 dev.refCount++
239 return dev.uvmPath, nil
240 }
241
242 st, err := os.Stat(hostPath)
243 if err != nil {
244 return "", err
245 }
246
247
248
249
250 devSize := pageAlign(uint64(st.Size()))
251 deviceNumber, memReg, err := uvm.allocateNextVPMemMappedDeviceLocation(ctx, devSize)
252 if err != nil {
253 return "", err
254 }
255 defer func() {
256 if err != nil {
257 pmem := uvm.vpmemDevicesMultiMapped[deviceNumber]
258 if err := pmem.Release(memReg); err != nil {
259 log.G(ctx).WithError(err).Debugf("failed to reclaim pmem region: %s", err)
260 }
261 }
262 }()
263
264 uvmPath := fmt.Sprintf(lcowPackedVPMemLayerFmt, deviceNumber, memReg.Offset(), devSize)
265 md := newVPMemMappedDevice(hostPath, uvmPath, devSize, memReg)
266 modification, err := newMappedVPMemModifyRequest(ctx, guestrequest.RequestTypeAdd, deviceNumber, md, uvm)
267 if err := uvm.modify(ctx, modification); err != nil {
268 return "", errors.Errorf("uvm::addVPMemMappedDevice: failed to modify utility VM configuration: %s", err)
269 }
270 defer func() {
271 if err != nil {
272 rmRequest, _ := newMappedVPMemModifyRequest(ctx, guestrequest.RequestTypeRemove, deviceNumber, md, uvm)
273 if err := uvm.modify(ctx, rmRequest); err != nil {
274 log.G(ctx).WithError(err).Debugf("failed to rollback modification")
275 }
276 }
277 }()
278
279 pmem := uvm.vpmemDevicesMultiMapped[deviceNumber]
280 if err := pmem.mapVHDLayer(ctx, md); err != nil {
281 return "", errors.Wrapf(err, "failed to update internal state")
282 }
283 return uvmPath, nil
284 }
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300 func (uvm *UtilityVM) removeVPMemMappedDevice(ctx context.Context, hostPath string) error {
301 devNum, md, err := uvm.findVPMemMappedDevice(ctx, hostPath)
302 if err != nil {
303 return err
304 }
305 if md.refCount > 1 {
306 md.refCount--
307 return nil
308 }
309
310 modification, err := newMappedVPMemModifyRequest(ctx, guestrequest.RequestTypeRemove, devNum, md, uvm)
311 if err != nil {
312 return err
313 }
314
315 if err := uvm.modify(ctx, modification); err != nil {
316 return errors.Errorf("failed to remove packed VPMem %s from UVM %s: %s", md.hostPath, uvm.id, err)
317 }
318
319 pmem := uvm.vpmemDevicesMultiMapped[devNum]
320 if err := pmem.unmapVHDLayer(ctx, hostPath); err != nil {
321 log.G(ctx).WithError(err).Debugf("failed unmapping VHD layer %s", hostPath)
322 }
323 if len(pmem.mappings) == 0 {
324 uvm.vpmemDevicesMultiMapped[devNum] = nil
325 }
326 return nil
327 }
328
View as plain text