github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/device/manager/manager.go (about) 1 // Copyright (c) 2017-2018 Intel Corporation 2 // Copyright (c) 2018 Huawei Corporation 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package manager 8 9 import ( 10 "encoding/hex" 11 "errors" 12 "sync" 13 14 "github.com/sirupsen/logrus" 15 16 "github.com/kata-containers/runtime/virtcontainers/device/api" 17 "github.com/kata-containers/runtime/virtcontainers/device/config" 18 "github.com/kata-containers/runtime/virtcontainers/device/drivers" 19 persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api" 20 "github.com/kata-containers/runtime/virtcontainers/utils" 21 ) 22 23 const ( 24 // VirtioMmio indicates block driver is virtio-mmio based 25 VirtioMmio string = "virtio-mmio" 26 // VirtioBlock indicates block driver is virtio-blk based 27 VirtioBlock string = "virtio-blk" 28 // VirtioBlockCCW indicates block driver is virtio-blk-ccw based 29 VirtioBlockCCW string = "virtio-blk-ccw" 30 // VirtioSCSI indicates block driver is virtio-scsi based 31 VirtioSCSI string = "virtio-scsi" 32 // Nvdimm indicates block driver is nvdimm based 33 Nvdimm string = "nvdimm" 34 ) 35 36 var ( 37 // ErrIDExhausted represents that devices are too many 38 // and no more IDs can be generated 39 ErrIDExhausted = errors.New("IDs are exhausted") 40 // ErrDeviceNotExist represents device hasn't been created before 41 ErrDeviceNotExist = errors.New("device with specified ID hasn't been created") 42 // ErrDeviceNotAttached represents the device isn't attached 43 ErrDeviceNotAttached = errors.New("device isn't attached") 44 // ErrRemoveAttachedDevice represents the device isn't detached 45 // so not allow to remove from list 46 ErrRemoveAttachedDevice = errors.New("can't remove attached device") 47 ) 48 49 type deviceManager struct { 50 blockDriver string 51 vhostUserStoreEnabled bool 52 vhostUserStorePath string 53 54 devices map[string]api.Device 55 sync.RWMutex 56 } 57 58 func deviceLogger() *logrus.Entry { 59 return api.DeviceLogger().WithField("subsystem", "device") 60 } 61 62 // NewDeviceManager creates a deviceManager object behaved as api.DeviceManager 63 func NewDeviceManager(blockDriver string, vhostUserStoreEnabled bool, vhostUserStorePath string, devices []api.Device) api.DeviceManager { 64 dm := &deviceManager{ 65 vhostUserStoreEnabled: vhostUserStoreEnabled, 66 vhostUserStorePath: vhostUserStorePath, 67 devices: make(map[string]api.Device), 68 } 69 if blockDriver == VirtioMmio { 70 dm.blockDriver = VirtioMmio 71 } else if blockDriver == VirtioBlock { 72 dm.blockDriver = VirtioBlock 73 } else if blockDriver == Nvdimm { 74 dm.blockDriver = Nvdimm 75 } else if blockDriver == VirtioBlockCCW { 76 dm.blockDriver = VirtioBlockCCW 77 } else { 78 dm.blockDriver = VirtioSCSI 79 } 80 81 drivers.AllPCIeDevs = make(map[string]bool) 82 83 for _, dev := range devices { 84 dm.devices[dev.DeviceID()] = dev 85 } 86 return dm 87 } 88 89 func (dm *deviceManager) findDeviceByMajorMinor(major, minor int64) api.Device { 90 for _, dev := range dm.devices { 91 dma, dmi := dev.GetMajorMinor() 92 if dma == major && dmi == minor { 93 return dev 94 } 95 } 96 return nil 97 } 98 99 // createDevice creates one device based on DeviceInfo 100 func (dm *deviceManager) createDevice(devInfo config.DeviceInfo) (dev api.Device, err error) { 101 // pmem device may points to block devices or raw files, 102 // do not change its HostPath. 103 if !devInfo.Pmem { 104 path, err := config.GetHostPathFunc(devInfo, dm.vhostUserStoreEnabled, dm.vhostUserStorePath) 105 if err != nil { 106 return nil, err 107 } 108 devInfo.HostPath = path 109 } 110 111 defer func() { 112 if err == nil { 113 dev.Reference() 114 } 115 }() 116 117 if existingDev := dm.findDeviceByMajorMinor(devInfo.Major, devInfo.Minor); existingDev != nil { 118 return existingDev, nil 119 } 120 121 // device ID must be generated by manager instead of device itself 122 // in case of ID collision 123 if devInfo.ID, err = dm.newDeviceID(); err != nil { 124 return nil, err 125 } 126 if isVFIO(devInfo.HostPath) { 127 return drivers.NewVFIODevice(&devInfo), nil 128 } else if isVhostUserBlk(devInfo) { 129 if devInfo.DriverOptions == nil { 130 devInfo.DriverOptions = make(map[string]string) 131 } 132 devInfo.DriverOptions["block-driver"] = dm.blockDriver 133 return drivers.NewVhostUserBlkDevice(&devInfo), nil 134 } else if isBlock(devInfo) { 135 if devInfo.DriverOptions == nil { 136 devInfo.DriverOptions = make(map[string]string) 137 } 138 devInfo.DriverOptions["block-driver"] = dm.blockDriver 139 return drivers.NewBlockDevice(&devInfo), nil 140 } else { 141 deviceLogger().WithField("device", devInfo.HostPath).Info("Device has not been passed to the container") 142 return drivers.NewGenericDevice(&devInfo), nil 143 } 144 } 145 146 // NewDevice creates a device based on specified DeviceInfo 147 func (dm *deviceManager) NewDevice(devInfo config.DeviceInfo) (api.Device, error) { 148 dm.Lock() 149 defer dm.Unlock() 150 dev, err := dm.createDevice(devInfo) 151 if err == nil { 152 dm.devices[dev.DeviceID()] = dev 153 } 154 return dev, err 155 } 156 157 // RemoveDevice deletes the device from list based on specified device id 158 func (dm *deviceManager) RemoveDevice(id string) error { 159 dm.Lock() 160 defer dm.Unlock() 161 dev, ok := dm.devices[id] 162 if !ok { 163 return ErrDeviceNotExist 164 } 165 166 if dev.Dereference() == 0 { 167 if dev.GetAttachCount() > 0 { 168 return ErrRemoveAttachedDevice 169 } 170 delete(dm.devices, id) 171 } 172 return nil 173 } 174 175 func (dm *deviceManager) newDeviceID() (string, error) { 176 for i := 0; i < 5; i++ { 177 // generate an random ID 178 randBytes, err := utils.GenerateRandomBytes(8) 179 if err != nil { 180 return "", err 181 } 182 id := hex.EncodeToString(randBytes) 183 184 // check ID collision, choose another one if ID is in use 185 if _, ok := dm.devices[id]; !ok { 186 return id, nil 187 } 188 } 189 return "", ErrIDExhausted 190 } 191 192 func (dm *deviceManager) AttachDevice(id string, dr api.DeviceReceiver) error { 193 dm.Lock() 194 defer dm.Unlock() 195 196 d, ok := dm.devices[id] 197 if !ok { 198 return ErrDeviceNotExist 199 } 200 201 if err := d.Attach(dr); err != nil { 202 return err 203 } 204 return nil 205 } 206 207 func (dm *deviceManager) DetachDevice(id string, dr api.DeviceReceiver) error { 208 dm.Lock() 209 defer dm.Unlock() 210 211 d, ok := dm.devices[id] 212 if !ok { 213 return ErrDeviceNotExist 214 } 215 if d.GetAttachCount() == 0 { 216 return ErrDeviceNotAttached 217 } 218 219 if err := d.Detach(dr); err != nil { 220 return err 221 } 222 return nil 223 } 224 225 func (dm *deviceManager) GetDeviceByID(id string) api.Device { 226 dm.RLock() 227 defer dm.RUnlock() 228 if d, ok := dm.devices[id]; ok { 229 return d 230 } 231 return nil 232 } 233 234 func (dm *deviceManager) GetAllDevices() []api.Device { 235 dm.RLock() 236 defer dm.RUnlock() 237 devices := []api.Device{} 238 for _, v := range dm.devices { 239 devices = append(devices, v) 240 } 241 return devices 242 } 243 244 func (dm *deviceManager) IsDeviceAttached(id string) bool { 245 dm.RLock() 246 defer dm.RUnlock() 247 d, ok := dm.devices[id] 248 if !ok { 249 return false 250 } 251 return d.GetAttachCount() > 0 252 } 253 254 // NewDevice creates a device based on specified DeviceInfo 255 func (dm *deviceManager) LoadDevices(devStates []persistapi.DeviceState) { 256 dm.Lock() 257 defer dm.Unlock() 258 259 for _, ds := range devStates { 260 var dev api.Device 261 262 switch config.DeviceType(ds.Type) { 263 case config.DeviceGeneric: 264 dev = &drivers.GenericDevice{} 265 case config.DeviceBlock: 266 dev = &drivers.BlockDevice{} 267 case config.DeviceVFIO: 268 dev = &drivers.VFIODevice{} 269 case config.VhostUserSCSI: 270 dev = &drivers.VhostUserSCSIDevice{} 271 case config.VhostUserBlk: 272 dev = &drivers.VhostUserBlkDevice{} 273 case config.VhostUserNet: 274 dev = &drivers.VhostUserNetDevice{} 275 default: 276 deviceLogger().WithField("device-type", ds.Type).Warning("unrecognized device type is detected") 277 // continue the for loop 278 continue 279 } 280 281 dev.Load(ds) 282 dm.devices[dev.DeviceID()] = dev 283 } 284 }