github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/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 ReadOnly: device.DeviceInfo.ReadOnly, 70 } 71 72 if fs, ok := device.DeviceInfo.DriverOptions["fstype"]; ok { 73 drive.Format = fs 74 } 75 76 customOptions := device.DeviceInfo.DriverOptions 77 if customOptions == nil || 78 customOptions["block-driver"] == "virtio-scsi" { 79 // User has not chosen a specific block device type 80 // Default to SCSI 81 scsiAddr, err := utils.GetSCSIAddress(index) 82 if err != nil { 83 return err 84 } 85 86 drive.SCSIAddr = scsiAddr 87 } else if customOptions["block-driver"] != "nvdimm" { 88 // When determine a drive name the block index needs to be incremented by 89 // an offset. The sandbox may have pre-allocated virtio-block drives that 90 // are not part of the sandbox BlockIndexMap. 91 globalIdx := index + devReceiver.GetSandboxBlockOffset() 92 93 driveName, err := utils.GetVirtDriveName(globalIdx) 94 if err != nil { 95 return err 96 } 97 98 drive.VirtPath = filepath.Join("/dev", driveName) 99 } 100 101 deviceLogger().WithField("device", device.DeviceInfo.HostPath).WithField("VirtPath", drive.VirtPath).Infof("Attaching %s device", customOptions["block-driver"]) 102 device.BlockDrive = drive 103 if err = devReceiver.HotplugAddDevice(device, config.DeviceBlock); err != nil { 104 return err 105 } 106 107 return nil 108 } 109 110 // Detach is standard interface of api.Device, it's used to remove device from some 111 // DeviceReceiver 112 func (device *BlockDevice) Detach(devReceiver api.DeviceReceiver) error { 113 skip, err := device.bumpAttachCount(false) 114 if err != nil { 115 return err 116 } 117 if skip { 118 return nil 119 } 120 121 defer func() { 122 if err != nil { 123 device.bumpAttachCount(true) 124 } else { 125 devReceiver.UnsetSandboxBlockIndex(device.BlockDrive.Index) 126 } 127 }() 128 129 deviceLogger().WithField("device", device.DeviceInfo.HostPath).Info("Unplugging block device") 130 131 if err = devReceiver.HotplugRemoveDevice(device, config.DeviceBlock); err != nil { 132 deviceLogger().WithError(err).Error("Failed to unplug block device") 133 return err 134 } 135 return nil 136 } 137 138 // DeviceType is standard interface of api.Device, it returns device type 139 func (device *BlockDevice) DeviceType() config.DeviceType { 140 return config.DeviceBlock 141 } 142 143 // GetDeviceInfo returns device information used for creating 144 func (device *BlockDevice) GetDeviceInfo() interface{} { 145 return device.BlockDrive 146 } 147 148 // Save converts Device to DeviceState 149 func (device *BlockDevice) Save() persistapi.DeviceState { 150 ds := device.GenericDevice.Save() 151 ds.Type = string(device.DeviceType()) 152 153 drive := device.BlockDrive 154 if drive != nil { 155 ds.BlockDrive = &persistapi.BlockDrive{ 156 File: drive.File, 157 Format: drive.Format, 158 ID: drive.ID, 159 Index: drive.Index, 160 MmioAddr: drive.MmioAddr, 161 PCIPath: drive.PCIPath, 162 SCSIAddr: drive.SCSIAddr, 163 NvdimmID: drive.NvdimmID, 164 VirtPath: drive.VirtPath, 165 DevNo: drive.DevNo, 166 Pmem: drive.Pmem, 167 } 168 } 169 return ds 170 } 171 172 // Load loads DeviceState and converts it to specific device 173 func (device *BlockDevice) Load(ds persistapi.DeviceState) { 174 device.GenericDevice = &GenericDevice{} 175 device.GenericDevice.Load(ds) 176 177 bd := ds.BlockDrive 178 if bd == nil { 179 return 180 } 181 device.BlockDrive = &config.BlockDrive{ 182 File: bd.File, 183 Format: bd.Format, 184 ID: bd.ID, 185 Index: bd.Index, 186 MmioAddr: bd.MmioAddr, 187 PCIPath: bd.PCIPath, 188 SCSIAddr: bd.SCSIAddr, 189 NvdimmID: bd.NvdimmID, 190 VirtPath: bd.VirtPath, 191 DevNo: bd.DevNo, 192 Pmem: bd.Pmem, 193 } 194 } 195 196 // It should implement GetAttachCount() and DeviceID() as api.Device implementation 197 // here it shares function from *GenericDevice so we don't need duplicate codes