github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/device/drivers/vhost_user_blk.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 "github.com/kata-containers/runtime/virtcontainers/device/api" 11 "github.com/kata-containers/runtime/virtcontainers/device/config" 12 persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api" 13 "github.com/kata-containers/runtime/virtcontainers/utils" 14 "github.com/sirupsen/logrus" 15 ) 16 17 // VhostUserBlkDevice is a block vhost-user based device 18 type VhostUserBlkDevice struct { 19 *GenericDevice 20 VhostUserDeviceAttrs *config.VhostUserDeviceAttrs 21 } 22 23 // NewVhostUserBlkDevice creates a new vhost-user block device based on DeviceInfo 24 func NewVhostUserBlkDevice(devInfo *config.DeviceInfo) *VhostUserBlkDevice { 25 return &VhostUserBlkDevice{ 26 GenericDevice: &GenericDevice{ 27 ID: devInfo.ID, 28 DeviceInfo: devInfo, 29 }, 30 } 31 } 32 33 // 34 // VhostUserBlkDevice's implementation of the device interface: 35 // 36 37 // Attach is standard interface of api.Device, it's used to add device to some 38 // DeviceReceiver 39 func (device *VhostUserBlkDevice) Attach(devReceiver api.DeviceReceiver) (err error) { 40 skip, err := device.bumpAttachCount(true) 41 if err != nil { 42 return err 43 } 44 if skip { 45 return nil 46 } 47 48 // From the explanation of function attach in block.go, block index of 49 // a general block device is utilized for some situation. 50 // Since vhost-user-blk uses "vd" prefix in Linux kernel, not "sd", 51 // sandbox block index should be updated only if sandbox default block 52 // driver is "virtio-blk"/"virtio-blk-ccw"/"virtio-mmio" which uses 53 // "vd" prefix in Linux kernel. 54 index := -1 55 updateBlockIndex := isVirtioBlkBlockDriver(device.DeviceInfo.DriverOptions) 56 if updateBlockIndex { 57 index, err = devReceiver.GetAndSetSandboxBlockIndex() 58 } 59 60 defer func() { 61 if err != nil { 62 if updateBlockIndex { 63 devReceiver.UnsetSandboxBlockIndex(index) 64 } 65 device.bumpAttachCount(false) 66 } 67 }() 68 69 if err != nil { 70 return err 71 } 72 73 vAttrs := &config.VhostUserDeviceAttrs{ 74 DevID: utils.MakeNameID("blk", device.DeviceInfo.ID, maxDevIDSize), 75 SocketPath: device.DeviceInfo.HostPath, 76 Type: config.VhostUserBlk, 77 Index: index, 78 } 79 80 deviceLogger().WithFields(logrus.Fields{ 81 "device": device.DeviceInfo.HostPath, 82 "SocketPath": vAttrs.SocketPath, 83 "Type": config.VhostUserBlk, 84 "Index": index, 85 }).Info("Attaching device") 86 87 device.VhostUserDeviceAttrs = vAttrs 88 if err = devReceiver.HotplugAddDevice(device, config.VhostUserBlk); err != nil { 89 return err 90 } 91 92 return nil 93 } 94 95 func isVirtioBlkBlockDriver(customOptions map[string]string) bool { 96 var blockDriverOption string 97 98 if customOptions == nil { 99 // User has not chosen a specific block device type 100 // Default to SCSI 101 blockDriverOption = "virtio-scsi" 102 } else { 103 blockDriverOption = customOptions["block-driver"] 104 } 105 106 if blockDriverOption == "virtio-blk" || 107 blockDriverOption == "virtio-blk-ccw" || 108 blockDriverOption == "virtio-mmio" { 109 return true 110 } 111 112 return false 113 } 114 115 // Detach is standard interface of api.Device, it's used to remove device from some 116 // DeviceReceiver 117 func (device *VhostUserBlkDevice) Detach(devReceiver api.DeviceReceiver) error { 118 skip, err := device.bumpAttachCount(false) 119 if err != nil { 120 return err 121 } 122 if skip { 123 return nil 124 } 125 126 defer func() { 127 if err != nil { 128 device.bumpAttachCount(true) 129 } else { 130 updateBlockIndex := isVirtioBlkBlockDriver(device.DeviceInfo.DriverOptions) 131 if updateBlockIndex { 132 devReceiver.UnsetSandboxBlockIndex(device.VhostUserDeviceAttrs.Index) 133 } 134 } 135 }() 136 137 deviceLogger().WithField("device", device.DeviceInfo.HostPath).Info("Unplugging vhost-user-blk device") 138 139 if err = devReceiver.HotplugRemoveDevice(device, config.VhostUserBlk); err != nil { 140 deviceLogger().WithError(err).Error("Failed to unplug vhost-user-blk device") 141 return err 142 } 143 return nil 144 } 145 146 // DeviceType is standard interface of api.Device, it returns device type 147 func (device *VhostUserBlkDevice) DeviceType() config.DeviceType { 148 return config.VhostUserBlk 149 } 150 151 // GetDeviceInfo returns device information used for creating 152 func (device *VhostUserBlkDevice) GetDeviceInfo() interface{} { 153 return device.VhostUserDeviceAttrs 154 } 155 156 // Save converts Device to DeviceState 157 func (device *VhostUserBlkDevice) Save() persistapi.DeviceState { 158 ds := device.GenericDevice.Save() 159 ds.Type = string(device.DeviceType()) 160 161 vAttr := device.VhostUserDeviceAttrs 162 if vAttr != nil { 163 ds.VhostUserDev = &persistapi.VhostUserDeviceAttrs{ 164 DevID: vAttr.DevID, 165 SocketPath: vAttr.SocketPath, 166 Type: string(vAttr.Type), 167 PCIPath: vAttr.PCIPath, 168 Index: vAttr.Index, 169 } 170 } 171 return ds 172 } 173 174 // Load loads DeviceState and converts it to specific device 175 func (device *VhostUserBlkDevice) Load(ds persistapi.DeviceState) { 176 device.GenericDevice = &GenericDevice{} 177 device.GenericDevice.Load(ds) 178 179 dev := ds.VhostUserDev 180 if dev == nil { 181 return 182 } 183 184 device.VhostUserDeviceAttrs = &config.VhostUserDeviceAttrs{ 185 DevID: dev.DevID, 186 SocketPath: dev.SocketPath, 187 Type: config.DeviceType(dev.Type), 188 PCIPath: dev.PCIPath, 189 Index: dev.Index, 190 } 191 } 192 193 // It should implement GetAttachCount() and DeviceID() as api.Device implementation 194 // here it shares function from *GenericDevice so we don't need duplicate codes