gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/device/drivers/block.go (about) 1 // Copyright (c) 2017-2018 Intel Corporation 2 // Copyright (c) 2018-2019 Huawei Corporation 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package drivers 8 9 import ( 10 "path/filepath" 11 12 "github.com/kata-containers/runtime/virtcontainers/device/api" 13 "github.com/kata-containers/runtime/virtcontainers/device/config" 14 persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api" 15 "github.com/kata-containers/runtime/virtcontainers/utils" 16 ) 17 18 const maxDevIDSize = 31 19 20 // BlockDevice refers to a block storage device implementation. 21 type BlockDevice struct { 22 *GenericDevice 23 BlockDrive *config.BlockDrive 24 } 25 26 // NewBlockDevice creates a new block device based on DeviceInfo 27 func NewBlockDevice(devInfo *config.DeviceInfo) *BlockDevice { 28 return &BlockDevice{ 29 GenericDevice: &GenericDevice{ 30 ID: devInfo.ID, 31 DeviceInfo: devInfo, 32 }, 33 } 34 } 35 36 // Attach is standard interface of api.Device, it's used to add device to some 37 // DeviceReceiver 38 func (device *BlockDevice) Attach(devReceiver api.DeviceReceiver) (err error) { 39 skip, err := device.bumpAttachCount(true) 40 if err != nil { 41 return err 42 } 43 if skip { 44 return nil 45 } 46 47 // Increment the block index for the sandbox. This is used to determine the name 48 // for the block device in the case where the block device is used as container 49 // rootfs and the predicted block device name needs to be provided to the agent. 50 index, err := devReceiver.GetAndSetSandboxBlockIndex() 51 52 defer func() { 53 if err != nil { 54 devReceiver.UnsetSandboxBlockIndex(index) 55 device.bumpAttachCount(false) 56 } 57 }() 58 59 if err != nil { 60 return err 61 } 62 63 drive := &config.BlockDrive{ 64 File: device.DeviceInfo.HostPath, 65 Format: "raw", 66 ID: utils.MakeNameID("drive", device.DeviceInfo.ID, maxDevIDSize), 67 Index: index, 68 Pmem: device.DeviceInfo.Pmem, 69 } 70 71 if fs, ok := device.DeviceInfo.DriverOptions["fstype"]; ok { 72 drive.Format = fs 73 } 74 75 customOptions := device.DeviceInfo.DriverOptions 76 if customOptions == nil || 77 customOptions["block-driver"] == "virtio-scsi" { 78 // User has not chosen a specific block device type 79 // Default to SCSI 80 scsiAddr, err := utils.GetSCSIAddress(index) 81 if err != nil { 82 return err 83 } 84 85 drive.SCSIAddr = scsiAddr 86 } else if customOptions["block-driver"] != "nvdimm" { 87 var globalIdx int 88 89 switch customOptions["block-driver"] { 90 case "virtio-blk": 91 globalIdx = index 92 case "virtio-blk-ccw": 93 globalIdx = index 94 case "virtio-mmio": 95 //With firecracker the rootfs for the VM itself 96 //sits at /dev/vda and consumes the first index. 97 //Longer term block based VM rootfs should be added 98 //as a regular block device which eliminates the 99 //offset. 100 //https://github.com/kata-containers/runtime/issues/1061 101 globalIdx = index + 1 102 } 103 104 driveName, err := utils.GetVirtDriveName(globalIdx) 105 if err != nil { 106 return err 107 } 108 109 drive.VirtPath = filepath.Join("/dev", driveName) 110 } 111 112 deviceLogger().WithField("device", device.DeviceInfo.HostPath).WithField("VirtPath", drive.VirtPath).Infof("Attaching %s device", customOptions["block-driver"]) 113 device.BlockDrive = drive 114 if err = devReceiver.HotplugAddDevice(device, config.DeviceBlock); err != nil { 115 return err 116 } 117 118 return nil 119 } 120 121 // Detach is standard interface of api.Device, it's used to remove device from some 122 // DeviceReceiver 123 func (device *BlockDevice) Detach(devReceiver api.DeviceReceiver) error { 124 skip, err := device.bumpAttachCount(false) 125 if err != nil { 126 return err 127 } 128 if skip { 129 return nil 130 } 131 132 defer func() { 133 if err != nil { 134 device.bumpAttachCount(true) 135 } else { 136 devReceiver.UnsetSandboxBlockIndex(device.BlockDrive.Index) 137 } 138 }() 139 140 deviceLogger().WithField("device", device.DeviceInfo.HostPath).Info("Unplugging block device") 141 142 if err = devReceiver.HotplugRemoveDevice(device, config.DeviceBlock); err != nil { 143 deviceLogger().WithError(err).Error("Failed to unplug block device") 144 return err 145 } 146 return nil 147 } 148 149 // DeviceType is standard interface of api.Device, it returns device type 150 func (device *BlockDevice) DeviceType() config.DeviceType { 151 return config.DeviceBlock 152 } 153 154 // GetDeviceInfo returns device information used for creating 155 func (device *BlockDevice) GetDeviceInfo() interface{} { 156 return device.BlockDrive 157 } 158 159 // Save converts Device to DeviceState 160 func (device *BlockDevice) Save() persistapi.DeviceState { 161 ds := device.GenericDevice.Save() 162 ds.Type = string(device.DeviceType()) 163 164 drive := device.BlockDrive 165 if drive != nil { 166 ds.BlockDrive = &persistapi.BlockDrive{ 167 File: drive.File, 168 Format: drive.Format, 169 ID: drive.ID, 170 Index: drive.Index, 171 MmioAddr: drive.MmioAddr, 172 PCIAddr: drive.PCIAddr, 173 SCSIAddr: drive.SCSIAddr, 174 NvdimmID: drive.NvdimmID, 175 VirtPath: drive.VirtPath, 176 DevNo: drive.DevNo, 177 Pmem: drive.Pmem, 178 } 179 } 180 return ds 181 } 182 183 // Load loads DeviceState and converts it to specific device 184 func (device *BlockDevice) Load(ds persistapi.DeviceState) { 185 device.GenericDevice = &GenericDevice{} 186 device.GenericDevice.Load(ds) 187 188 bd := ds.BlockDrive 189 if bd == nil { 190 return 191 } 192 device.BlockDrive = &config.BlockDrive{ 193 File: bd.File, 194 Format: bd.Format, 195 ID: bd.ID, 196 Index: bd.Index, 197 MmioAddr: bd.MmioAddr, 198 PCIAddr: bd.PCIAddr, 199 SCSIAddr: bd.SCSIAddr, 200 NvdimmID: bd.NvdimmID, 201 VirtPath: bd.VirtPath, 202 DevNo: bd.DevNo, 203 Pmem: bd.Pmem, 204 } 205 } 206 207 // It should implement GetAttachCount() and DeviceID() as api.Device implementation 208 // here it shares function from *GenericDevice so we don't need duplicate codes